diff --git a/android/app/build.gradle b/android/app/build.gradle
index f1799225..ab0271d6 100644
--- a/android/app/build.gradle
+++ b/android/app/build.gradle
@@ -55,6 +55,7 @@ configurations.all {
android {
compileSdkVersion 34
// ndkVersion "24.0.8215888"
+ buildToolsVersion "31.0.0"
sourceSets {
main.java.srcDirs += 'src/main/kotlin'
@@ -129,6 +130,11 @@ android {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
+ repositories {
+ flatDir {
+ dirs 'libs'
+ }
+ }
}
flutter {
@@ -151,7 +157,7 @@ dependencies {
implementation 'com.google.code.gson:gson:2.8.6'
// Dependency on a remote binary
-// implementation 'com.example.android:app-magic:12.3'
+ // implementation 'com.example.android:app-magic:12.3'
implementation "us.zoom.videosdk:zoomvideosdk-core:1.10.1"
@@ -169,6 +175,7 @@ dependencies {
implementation "org.jetbrains.anko:anko-commons:0.10.4"
implementation 'com.github.kittinunf.fuel:fuel:2.3.0' //for JVM
+ implementation project(':blesdk_2301')
implementation 'com.github.kittinunf.fuel:fuel-android:2.3.0'
implementation 'com.google.android.gms:play-services-location:17.1.0'//for Android
implementation 'com.google.android.gms:play-services-basement:17.5.0'
@@ -177,6 +184,23 @@ dependencies {
implementation 'com.facebook.stetho:stetho-urlconnection:1.5.1'
implementation 'androidx.core:core-ktx:1.6.0'
implementation 'androidx.appcompat:appcompat:1.3.1'
+ implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.4.0'
+ implementation 'androidx.work:work-runtime:2.7.0-alpha05'
androidTestImplementation "androidx.test:core:1.4.0"
+ implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.2"
+ implementation 'com.android.support:appcompat-v7:27.1.1'
+ testImplementation 'junit:junit:4.12'
+ implementation project(':blesdk_2301')
+ // compile "com.jstyle:1770sdk:1.0.2"
+ implementation 'io.reactivex.rxjava3:rxjava:3.1.3'
+ implementation 'io.reactivex.rxjava3:rxandroid:3.0.0'
+// implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.4.2'
+ // Lepu Libraries
+ implementation 'no.nordicsemi.android:ble:2.2.4'
+ implementation(name: 'lepu-blepro-1.0.1', ext: 'aar')
+ implementation 'io.github.jeremyliao:live-event-bus-x:1.8.0'
+ implementation 'com.guolindev.permissionx:permissionx:1.7.1'
+ implementation 'org.apache.commons:commons-io:1.3.2' // for FileUtills in ER1 device configuration
+
}
diff --git a/android/app/libs/lepu-blepro-1.0.1.aar b/android/app/libs/lepu-blepro-1.0.1.aar
new file mode 100644
index 00000000..1dcefd22
Binary files /dev/null and b/android/app/libs/lepu-blepro-1.0.1.aar differ
diff --git a/android/app/proguard-rules.pro b/android/app/proguard-rules.pro
index 69cd8026..b6c96d5b 100644
--- a/android/app/proguard-rules.pro
+++ b/android/app/proguard-rules.pro
@@ -53,4 +53,6 @@
}
-dontwarn com.opentok.android.**
--dontwarn com.opentok.otc.**
\ No newline at end of file
+-dontwarn com.opentok.otc.**
+
+-keep class com.boskokg.flutter_blue_plus.* { *; }
\ No newline at end of file
diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml
index 9a3ac334..afe87f9a 100644
--- a/android/app/src/main/AndroidManifest.xml
+++ b/android/app/src/main/AndroidManifest.xml
@@ -6,6 +6,7 @@
In most cases you can leave this as-is, but you if you want to provide
additional functionality it is fine to subclass or reimplement
FlutterApplication and put your custom class here. -->
+
@@ -21,7 +22,6 @@
-
@@ -34,6 +34,9 @@
+
+
+
@@ -52,6 +55,23 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -70,7 +90,7 @@
-->
+
diff --git a/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/MainActivity.kt b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/MainActivity.kt
index 8aa1ef85..3f312cd7 100644
--- a/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/MainActivity.kt
+++ b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/MainActivity.kt
@@ -1,29 +1,33 @@
-package com.ejada.hmg
-import android.app.NotificationChannel
-import android.app.NotificationManager
-import android.content.ContentResolver
-import android.media.AudioAttributes
-import android.net.Uri
-import android.os.Bundle
+package com.cloud.diplomaticquarterapp
+
import android.util.Log
-import android.os.Build
import android.view.WindowManager
-import androidx.annotation.NonNull;
+import com.cloud.diplomaticquarterapp.check_me_pro.ble.manager.BleScanManager
import com.ejada.hmg.utils.*
+import com.ejada.hmg.*
import io.flutter.embedding.android.FlutterFragmentActivity
import io.flutter.embedding.engine.FlutterEngine
-import io.flutter.plugin.common.MethodChannel
import io.flutter.plugins.GeneratedPluginRegistrant
+import com.cloud.diplomaticquarterapp.ble.BleBridge
+import com.cloud.diplomaticquarterapp.smart_ring_2301.BleManager
-class MainActivity: FlutterFragmentActivity() {
- override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) {
+@Suppress("DEPRECATION")
+class MainActivity : FlutterFragmentActivity() {
+ override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
GeneratedPluginRegistrant.registerWith(flutterEngine);
+
+
// Create Flutter Platform Bridge
this.window.addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED or WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON or WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD or WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON)
-
PlatformBridge(flutterEngine, this).create()
OpenTokPlatformBridge(flutterEngine, this).create()
+ BleManager.init(this)
+ BleBridge(flutterEngine, this).createBleBridge(BleScanManager()) { name, bluetoothDevice ->
+ // Handle the scan result here
+ Log.d("MainActivity", "Received scan result: $name, $bluetoothDevice")
+ }
+ }
// if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
// val mChannel = NotificationChannel("video_call_noti", "video call", NotificationManager.IMPORTANCE_HIGH)
// val soundUri = Uri.parse("android.resource://" + getApplicationContext()
@@ -43,9 +47,12 @@ class MainActivity: FlutterFragmentActivity() {
// val time = timeToMillis("04:00:00", "HH:mm:ss")
- }
+}
+
+
+// override fun onBleStateChanged(model: Int, state: Int) {
+// println("onBleStateChanged")
+// println("model $model, state: $state")
+//
+// }
- override fun onResume() {
- super.onResume()
- }
-}
\ No newline at end of file
diff --git a/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/ble/BPModelMeasuring.java b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/ble/BPModelMeasuring.java
new file mode 100644
index 00000000..81c9e864
--- /dev/null
+++ b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/ble/BPModelMeasuring.java
@@ -0,0 +1,17 @@
+package com.cloud.diplomaticquarterapp.ble;
+
+public class BPModelMeasuring {
+
+ public int pressure;
+ public int pr;
+ public boolean deflate;
+ public boolean pulse;
+
+ public BPModelMeasuring(int pressure, int pr, boolean deflate, boolean pulse) {
+ this.pressure = pressure;
+ this.pr = pr;
+ this.deflate = deflate;
+ this.pulse = pulse;
+
+ }
+}
diff --git a/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/ble/BPModelResult.java b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/ble/BPModelResult.java
new file mode 100644
index 00000000..e07b6e42
--- /dev/null
+++ b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/ble/BPModelResult.java
@@ -0,0 +1,23 @@
+package com.cloud.diplomaticquarterapp.ble;
+
+public class BPModelResult {
+
+ public boolean deflate;
+ public int dia;
+ public int mean;
+ public int pr;
+ public int pressure;
+ public int result;
+ public int sys;
+
+ public BPModelResult(boolean deflate, int dia, int mean, int pr, int pressure, int result, int sys) {
+ this.deflate = deflate;
+ this.dia = dia;
+ this.mean = mean;
+ this.pr = pr;
+ this.pressure = pressure;
+ this.result = result;
+ this.sys = sys;
+
+ }
+}
diff --git a/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/ble/BleBridge.kt b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/ble/BleBridge.kt
new file mode 100644
index 00000000..fe8dc8ac
--- /dev/null
+++ b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/ble/BleBridge.kt
@@ -0,0 +1,1319 @@
+package com.cloud.diplomaticquarterapp.ble
+
+
+//Ble
+import BaseActivity
+import TmpInfo
+import android.annotation.SuppressLint
+import android.bluetooth.BluetoothDevice
+import android.os.Build
+import android.os.Handler
+import android.util.Log
+import android.util.SparseArray
+import androidx.annotation.RequiresApi
+import com.cloud.diplomaticquarterapp.MainActivity
+import com.cloud.diplomaticquarterapp.ble.utils.EcgData
+import com.cloud.diplomaticquarterapp.ble.utils.HexString
+import com.cloud.diplomaticquarterapp.check_me_pro.bean.BleBean
+import com.cloud.diplomaticquarterapp.check_me_pro.bean.UserBean
+import com.cloud.diplomaticquarterapp.check_me_pro.bean.toJson
+import com.cloud.diplomaticquarterapp.check_me_pro.ble.format.BpInfo
+import com.cloud.diplomaticquarterapp.check_me_pro.ble.format.EcgInfo
+import com.cloud.diplomaticquarterapp.check_me_pro.ble.format.EcgWaveInfo
+import com.cloud.diplomaticquarterapp.check_me_pro.ble.format.GluInfo
+import com.cloud.diplomaticquarterapp.check_me_pro.ble.format.OxyInfo
+import com.cloud.diplomaticquarterapp.check_me_pro.ble.format.UserInfo
+import com.cloud.diplomaticquarterapp.check_me_pro.ble.manager.BleScanManager
+import com.cloud.diplomaticquarterapp.check_me_pro.ble.worker.BleDataWorker
+import com.cloud.diplomaticquarterapp.smart_ring_2301.BleManager
+import com.cloud.diplomaticquarterapp.utils.Constant
+import com.cloud.diplomaticquarterapp.utils.UiChannel
+import com.google.gson.Gson
+import com.jeremyliao.liveeventbus.LiveEventBus
+import com.jstyle.blesdk2301.Util.BleSDK
+import com.jstyle.blesdk2301.Util.BleSDK.EnterActivityMode
+import com.jstyle.blesdk2301.Util.BleSDK.GetDynamicHRWithMode
+import com.jstyle.blesdk2301.Util.BleSDK.GetHRVDataWithMode
+import com.jstyle.blesdk2301.Util.BleSDK.GetStaticHRWithMode
+import com.jstyle.blesdk2301.Util.BleSDK.GetTemperature_historyData
+import com.jstyle.blesdk2301.Util.BleSDK.GetTotalActivityDataWithMode
+import com.jstyle.blesdk2301.Util.BleSDK.Oxygen_data
+import com.jstyle.blesdk2301.callback.BleConnectionListener
+import com.jstyle.blesdk2301.constant.BleConst
+import com.jstyle.blesdk2301.constant.DeviceKey
+import com.jstyle.blesdk2301.model.ExerciseMode
+import com.lepu.blepro.event.EventMsgConst
+import com.lepu.blepro.event.EventMsgConst.Ble.*
+import com.lepu.blepro.event.InterfaceEvent
+import com.lepu.blepro.ext.BleServiceHelper
+import com.lepu.blepro.ext.bp2.*
+import com.lepu.blepro.ext.er1.Er1EcgFile
+import com.lepu.blepro.ext.er1.Er1File
+import com.lepu.blepro.ext.er2.DeviceInfo
+import com.lepu.blepro.ext.er2.Er2EcgFile
+import com.lepu.blepro.ext.er2.Er2File
+import com.lepu.blepro.ext.er2.RtData
+import com.lepu.blepro.ext.pc60fw.RtParam
+import com.lepu.blepro.ext.sp20.RtWave
+import com.lepu.blepro.objs.Bluetooth
+import com.lepu.blepro.objs.BluetoothController
+import com.lepu.blepro.utils.DateUtil
+import com.lepu.blepro.utils.Er1Decompress
+import io.flutter.embedding.engine.FlutterEngine
+import io.flutter.plugin.common.EventChannel
+import io.flutter.plugin.common.MethodCall
+import io.flutter.plugin.common.MethodChannel
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.MainScope
+import kotlinx.coroutines.channels.Channel
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.withTimeoutOrNull
+import org.apache.commons.io.FileUtils
+import org.json.JSONObject
+import java.io.File
+
+class BleBridge(private var flutterEngine: FlutterEngine, private var mainActivity: MainActivity) : BaseActivity() {
+
+ private lateinit var channel: MethodChannel
+ private lateinit var Echannel: EventChannel
+ private var eventSink: EventChannel.EventSink? = null
+
+ private var ecgFileNames = arrayListOf()
+ var ecgList: ArrayList = arrayListOf()
+ private val bleListCheckMe: MutableList = ArrayList()
+
+
+ val gson = Gson()
+
+ companion object {
+ private const val CHANNEL = "BLE-Platform-Bridge"
+ private const val EVENTCHANNEL = "BLE-Platform-Bridge-Event"
+ private const val SCAN_DEVICE = "scanDevices"
+ private const val STOP_SCAN = "stopScan"
+ private const val STOP_SCAN_CHECK_ME = "stopScanCheckMe"
+ private const val CONNECT_DEVICE = "connectDevice"
+ private const val SCAN_DEVICE_EKG = "scan_ekg"
+ private const val EKG_FILES_LIST = "ecgFilesList"
+ private const val FACTORY_RESET_ECG = "factoryResetECG"
+ private const val BP2_FILES_LIST = "bp2FilesList"
+ private const val DISCONNECT_DEVICE = "disconnectDevice"
+ private const val DISCONNECT_CHECK_ME = "disconnectCheckMe"
+ private const val READ_USER_CHECK_ME_PRO = "readUserCheckMePro"
+ private const val SCAN_FOR_CHECK_ME_PRO = "scanForCheckMePro"
+ private const val CONNECT_DEVICE_CHECK_ME = "connectDeviceCheckMe"
+ private const val GET_BP_DATA_CHECK_ME_PRO = "getBPDataFromCheckMePro"
+ private const val GET_OXI_DATA_CHECK_ME_PRO = "getOxiDataFromCheckMePro"
+ private const val GET_TEMP_DATA_CHECK_ME_PRO = "getTempDataFromCheckMePro"
+ private const val GET_ECG_DATA_CHECK_ME_PRO = "getEcgDataFromCheckMePro"
+ private const val GET_ECG_WAVE_CHECK_ME_PRO = "getEcgWaveFromCheckMePro"
+ private const val GET_GLUCOSE_DATA_CHECK_ME_PRO = "getGlucoseDataFromCheckMePro"
+
+ //FOR SMART RING
+ private const val SCAN_FOR_SMART_RING = "scanForSmartRing"
+ private const val CONNECT_DEVICE_SMART_RING = "connectDeviceSmartRing"
+ private const val DISCONNECT_DEVICE_SMART_RING = "disConnectDeviceSmartRing"
+ private const val GET_BATTERY_LEVEL_SMART_RING = "getBatteryLevelSmartRing"
+ private const val GET_TEMPERATURE_SMART_RING = "getTemperatureSmartRing"
+ private const val GET_DYNAMIC_HEART_RATE_SMART_RING = "getDynamicHeartRateSmartRing"
+ private const val GET_STATIC_HEART_RATE_SMART_RING = "getStaticHeartRateSmartRing"
+ private const val GET_HRV_SMART_RING = "getHRVSmartRing"
+ private const val GET_BLOOD_OXYGEN_SMART_RING = "getBloodOxygenSmartRing"
+ private const val GET_TOTAL_STEP_COUNT_SMART_RING = "getTotalStepCountSmartRing"
+ private const val GET_SPORTS_MODE_DATA_SMART_RING = "getSportsModeDataSmartRing"
+ private const val STOP_SPORTS_MODE_DATA_SMART_RING = "stopSportsModeDataSmartRing"
+
+
+ val scan = BleScanManager()
+ val dataScope = CoroutineScope(Dispatchers.IO)
+ val uiScope = CoroutineScope(Dispatchers.Main)
+
+
+ }
+
+ private val bleWorker = BleDataWorker { deviceInfoString ->
+ // Update eventSink in BleBridge
+ eventSink?.success(deviceInfoString)
+ }
+
+ var modeStart: Byte = 0x00 //开始获取数据 start getting data
+
+ var modeContinue: Byte = 0x02 //继续读取数据 continue reading data
+
+ var modeDelete = 0x99.toByte()
+
+ var mUserData: MutableList = java.util.ArrayList()
+
+ private val userChannel = Channel(Channel.CONFLATED)
+
+
+ private var userFileName = arrayOf(
+ "dlc.dat", "ped.dat", "nibp.dat", "glu.dat"
+ )
+ private var commonFileName = arrayOf(
+ "bpcal.dat",
+ "ecg.dat",
+ "oxi.dat",
+ "tmp.dat",
+ "slm.dat",
+ )
+ var currentUser = 0
+
+ lateinit var userInfo: UserInfo
+
+
+ private val models = intArrayOf(
+ Bluetooth.MODEL_PC60FW, Bluetooth.MODEL_PC_60NW, Bluetooth.MODEL_PC_60NW_1,
+ Bluetooth.MODEL_PC66B, Bluetooth.MODEL_PF_10, Bluetooth.MODEL_PF_20,
+ Bluetooth.MODEL_OXYSMART, Bluetooth.MODEL_POD2B,
+ Bluetooth.MODEL_POD_1W, Bluetooth.MODEL_S5W,
+ Bluetooth.MODEL_PF_10AW, Bluetooth.MODEL_PF_10AW1,
+ Bluetooth.MODEL_PF_10BW, Bluetooth.MODEL_PF_10BW1,
+ Bluetooth.MODEL_PF_20AW, Bluetooth.MODEL_PF_20B,
+ Bluetooth.MODEL_S7W, Bluetooth.MODEL_S7BW,
+ Bluetooth.MODEL_S6W, Bluetooth.MODEL_S6W1,
+ Bluetooth.MODEL_PC60NW_BLE, Bluetooth.MODEL_PC60NW_WPS,
+ Bluetooth.MODEL_PC_60NW_NO_SN, // Pc60fwActivity
+ Bluetooth.MODEL_O2RING, Bluetooth.MODEL_O2M,
+ Bluetooth.MODEL_BABYO2, Bluetooth.MODEL_BABYO2N,
+ Bluetooth.MODEL_CHECKO2, Bluetooth.MODEL_SLEEPO2,
+ Bluetooth.MODEL_SNOREO2, Bluetooth.MODEL_WEARO2,
+ Bluetooth.MODEL_SLEEPU, Bluetooth.MODEL_OXYLINK,
+ Bluetooth.MODEL_KIDSO2, Bluetooth.MODEL_OXYFIT,
+ Bluetooth.MODEL_OXYRING, Bluetooth.MODEL_BBSM_S1,
+ Bluetooth.MODEL_BBSM_S2, Bluetooth.MODEL_OXYU,
+ Bluetooth.MODEL_AI_S100, Bluetooth.MODEL_O2M_WPS,
+ Bluetooth.MODEL_CMRING, Bluetooth.MODEL_OXYFIT_WPS,
+ Bluetooth.MODEL_KIDSO2_WPS, Bluetooth.MODEL_SI_PO6, // OxyActivity
+ Bluetooth.MODEL_PC80B, Bluetooth.MODEL_PC80B_BLE,
+ Bluetooth.MODEL_PC80B_BLE2, // Pc80bActivity
+ Bluetooth.MODEL_PC100, // Pc102Activity
+ Bluetooth.MODEL_AP20, Bluetooth.MODEL_AP20_WPS, // Ap20Activity
+ Bluetooth.MODEL_PC_68B, // Pc68bActivity
+ Bluetooth.MODEL_PULSEBITEX, Bluetooth.MODEL_HHM4, // PulsebitExActivity
+ Bluetooth.MODEL_CHECKME_LE, // CheckmeLeActivity
+ Bluetooth.MODEL_PC300, Bluetooth.MODEL_PC300_BLE,
+ Bluetooth.MODEL_GM_300SNT, Bluetooth.MODEL_CMI_PC303, // Pc303Activity
+ Bluetooth.MODEL_CHECK_POD, Bluetooth.MODEL_CHECKME_POD_WPS, // CheckmePodActivity
+ Bluetooth.MODEL_AOJ20A, // Aoj20aActivity
+ Bluetooth.MODEL_SP20, Bluetooth.MODEL_SP20_BLE, Bluetooth.MODEL_SP20_WPS, // Sp20Activity
+ Bluetooth.MODEL_VETCORDER, Bluetooth.MODEL_CHECK_ADV, // CheckmeMonitorActivity
+ Bluetooth.MODEL_TV221U, // Vtm20fActivity
+ Bluetooth.MODEL_BPM, // BpmActivity
+ Bluetooth.MODEL_BIOLAND_BGM, // BiolandBgmActivity
+ Bluetooth.MODEL_POCTOR_M3102, // PoctorM3102Activity
+ Bluetooth.MODEL_LPM311, // Lpm311Activity
+ Bluetooth.MODEL_LEM, // LemActivity
+ Bluetooth.MODEL_ER1, Bluetooth.MODEL_ER1_N, Bluetooth.MODEL_HHM1, // Er1Activity
+ Bluetooth.MODEL_ER2, Bluetooth.MODEL_LP_ER2, Bluetooth.MODEL_DUOEK,
+ Bluetooth.MODEL_HHM2, Bluetooth.MODEL_HHM3, // Er2Activity
+ Bluetooth.MODEL_BP2, Bluetooth.MODEL_BP2A, Bluetooth.MODEL_BP2T, // Bp2Activity
+ Bluetooth.MODEL_BP2W, // Bp2wActivity
+ Bluetooth.MODEL_LP_BP2W, // LpBp2wActivity
+ Bluetooth.MODEL_ER3, // Er3Activity
+ Bluetooth.MODEL_LEPOD, // LepodActivity
+ Bluetooth.MODEL_ECN, // EcnActivity
+ Bluetooth.MODEL_R20, Bluetooth.MODEL_R21,
+ Bluetooth.MODEL_R10, Bluetooth.MODEL_R11,
+ Bluetooth.MODEL_LERES, // VentilatorActivity
+ Bluetooth.MODEL_FHR, // FhrActivity
+ Bluetooth.MODEL_VTM_AD5, Bluetooth.MODEL_FETAL, // Ad5Activity
+ Bluetooth.MODEL_VCOMIN, // VcominActivity
+ )
+
+ private var list = arrayListOf()
+
+ private var model = Bluetooth.MODEL_O2RING
+
+ /**
+ * PS: O2 devices do not support processing multiple commands.
+ * If you want to use other command and start rtTask, you must stop rtTask.
+ */
+ private var rtHandler = Handler()
+ private var rtTask = RtTask()
+
+ private var ecgRTHandler = Handler()
+
+ private var br2Handler = Handler()
+ private var bp2rtTask = Bp2RtTask()
+
+
+ inner class RtTask : Runnable {
+ override fun run() {
+ rtHandler.postDelayed(rtTask, 1000)
+ BleServiceHelper.BleServiceHelper.oxyGetRtParam(model)
+ }
+ }
+
+ inner class Bp2RtTask : Runnable {
+ override fun run() {
+ br2Handler.postDelayed(bp2rtTask, 1000)
+ BleServiceHelper.BleServiceHelper.startRtTask(model)
+ }
+ }
+
+ inner class ECGRtTask : Runnable {
+ override fun run() {
+ ecgRTHandler.postDelayed(rtTask, 1000)
+ BleServiceHelper.BleServiceHelper.startRtTask(model)
+ }
+ }
+
+
+ private var mHandler: Handler? = null
+
+ @SuppressLint("NewApi")
+ fun createBleBridge(bleScanManager: BleScanManager, scanResultHandler: (String, BluetoothDevice) -> Unit) {
+ subscribeForSmartRingEvents();
+ channel = MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL)
+ Echannel = EventChannel(flutterEngine.dartExecutor.binaryMessenger, EVENTCHANNEL)
+ channel.setMethodCallHandler { methodCall: MethodCall, result: MethodChannel.Result ->
+
+
+ if (methodCall.method == SCAN_DEVICE) {
+ scanDevice()
+ } else if (methodCall.method == STOP_SCAN) {
+ stopScan()
+ } else if (methodCall.method == CONNECT_DEVICE) {
+ connectDevice(methodCall.arguments as List)
+ } else if (methodCall.method == DISCONNECT_DEVICE) {
+ disconnectDevice(methodCall, result)
+ } else if (methodCall.method == CONNECT_DEVICE_CHECK_ME) {
+ connectDeviceCheckMe(methodCall.arguments as List)
+ } else if (methodCall.method == DISCONNECT_CHECK_ME) {
+ disConnectDeviceCheckMe()
+ } else if (methodCall.method == GET_BP_DATA_CHECK_ME_PRO) {
+ getBPDataFromCheckMePro(methodCall.arguments as String);
+ } else if (methodCall.method == GET_GLUCOSE_DATA_CHECK_ME_PRO) {
+ getGlucoseDataFromCheckMePro(methodCall.arguments as String)
+ } else if (methodCall.method == GET_OXI_DATA_CHECK_ME_PRO) {
+ getOxyDataFromCheckMePro()
+ } else if (methodCall.method == GET_ECG_DATA_CHECK_ME_PRO) {
+ getECGDataFromCheckMePro()
+ } else if (methodCall.method == GET_ECG_WAVE_CHECK_ME_PRO) {
+ getECGWaveFromCheckMePro(methodCall.arguments as String)
+ } else if (methodCall.method == GET_TEMP_DATA_CHECK_ME_PRO) {
+ getTemperatureDataFromCheckMePro()
+ } else if (methodCall.method == SCAN_DEVICE_EKG) {
+// scanDeviceEKG(methodCall, result)
+ } else if (methodCall.method == EKG_FILES_LIST) {
+ getECGFilesList(methodCall.arguments as List)
+ } else if (methodCall.method == FACTORY_RESET_ECG) {
+ factoryResetECG(methodCall.arguments as List)
+ } else if (methodCall.method == BP2_FILES_LIST) {
+ getBP2FilesList()
+ } else if (methodCall.method == SCAN_FOR_CHECK_ME_PRO) {
+ scanForCheckMePro(bleScanManager, scanResultHandler)
+ } else if (methodCall.method == STOP_SCAN_CHECK_ME) {
+ stopScanForCheckMePro()
+ } else if (methodCall.method == SCAN_FOR_SMART_RING) {
+ scanForSmartRing(bleScanManager)
+ } else if (methodCall.method == CONNECT_DEVICE_SMART_RING) {
+ connectDeviceSmartRing(methodCall.arguments as String);
+ } else if (methodCall.method == DISCONNECT_DEVICE_SMART_RING) {
+ disConnectDeviceSmartRing()
+ } else if (methodCall.method == GET_BATTERY_LEVEL_SMART_RING) {
+ getBatteryLevelSmartRing()
+ } else if (methodCall.method == GET_TEMPERATURE_SMART_RING) {
+ getTemperatureSmartRing(modeStart);
+ } else if (methodCall.method == GET_DYNAMIC_HEART_RATE_SMART_RING) {
+ getDynamicHeartRateSmartRing(modeStart);
+ } else if (methodCall.method == GET_STATIC_HEART_RATE_SMART_RING) {
+ getStaticHeartRateSmartRing(modeStart);
+ } else if (methodCall.method == GET_HRV_SMART_RING) {
+ getHRVSmartRing(modeStart);
+ } else if (methodCall.method == GET_BLOOD_OXYGEN_SMART_RING) {
+ getBloodOxygenSmartRing(modeStart);
+ } else if (methodCall.method == GET_TOTAL_STEP_COUNT_SMART_RING) {
+ getTotalStepCountSmartRing(modeStart);
+ } else if (methodCall.method == GET_SPORTS_MODE_DATA_SMART_RING) {
+ getSportsModeDataSmartRing(modeStart);
+ } else if (methodCall.method == STOP_SPORTS_MODE_DATA_SMART_RING) {
+ stopSportsModeDataSmartRing(modeStart);
+ } else {
+ result.notImplemented()
+ }
+ }
+ Echannel.setStreamHandler(
+ object : EventChannel.StreamHandler {
+ override fun onListen(arguments: Any?, eventSink: EventChannel.EventSink?) {
+ this@BleBridge.eventSink = eventSink
+ }
+
+ override fun onCancel(arguments: Any?) {
+ }
+ },
+ )
+ }
+
+ private fun stopScanForCheckMePro() {
+ scan.stopScan();
+ }
+
+
+ @RequiresApi(Build.VERSION_CODES.Q)
+ fun disconnectDevice(methodCall: MethodCall, result: MethodChannel.Result) {
+ if (rtHandler.hasCallbacks(rtTask)) rtHandler.removeCallbacks(rtTask)
+ BleServiceHelper.BleServiceHelper.disconnect(false)
+ }
+
+
+ private fun scanDevice() {
+
+ LiveEventBus.get(EventMsgConst.Ble.EventServiceConnectedAndInterfaceInit).observe(this.mainActivity) {
+ // BleService init success
+ println("EventServiceConnectedAndInterfaceInit---------")
+ BleServiceHelper.BleServiceHelper.startScan(models)
+ println("EventServiceConnectedAndInterfaceInit")
+ }
+ LiveEventBus.get(EventMsgConst.Discovery.EventDeviceFound).observe(this.mainActivity) {
+ val deviceName: String = ""
+ val returnData = mapOf("type" to "DevicesList", "data" to gson.toJson(BluetoothController.getDevices()))
+ println("DevicesList: ${BluetoothController.getDevices()}");
+ eventSink?.success(returnData)
+
+ println("EventDeviceFound")
+ println(deviceName)
+
+ }
+ val rawFolders = SparseArray()
+ try {
+ BleServiceHelper.BleServiceHelper.initRawFolder(rawFolders).initService(this.mainActivity.application)
+ } catch (e: Exception) {
+ println(e)
+ }
+ }
+
+ @RequiresApi(Build.VERSION_CODES.M)
+ private fun scanForCheckMePro(bleScanManager: BleScanManager, scanResultHandler: (String, BluetoothDevice) -> Unit) {
+ initVar()
+ bleScanManager.initScan(this.mainActivity)
+ bleScanManager.setCallBack(object : BleScanManager.Scan {
+ override fun scanReturn(name: String, bluetoothDevice: BluetoothDevice) {
+ onScanResult(name, bluetoothDevice)
+ }
+ })
+ }
+
+ private fun initVar() {
+ Constant.initVar(this.mainActivity)
+ }
+
+
+ @SuppressLint("MissingPermission")
+ fun onScanResult(name: String, bluetoothDevice: BluetoothDevice) {
+ if (!(name.contains("Checkme"))) return;
+ var z: Int = 0;
+ for (ble in bleListCheckMe) run {
+ if (ble.name == bluetoothDevice.name) {
+ z = 1
+ }
+ }
+ if (z == 0) {
+ bleListCheckMe.add(BleBean(name, bluetoothDevice))
+
+ Log.d("CheckMeProDeviceFound", "This is the Device Name: $name")
+ val bluetoothDeviceJson = JSONObject().apply {
+ put("name", bluetoothDevice.name ?: "")
+ put("macAddr", bluetoothDevice.address ?: "")
+ put("type", bluetoothDevice.type)
+ // Add more properties as needed
+ }
+
+ println("EventDeviceFound : ${bluetoothDeviceJson.toString()}")
+
+ val returnData = mapOf("type" to "DevicesListCheckMe", "data" to bluetoothDeviceJson.toString())
+ eventSink?.success(returnData)
+
+ println(name)
+ }
+ }
+
+ private fun stopScan() {
+ BleServiceHelper.BleServiceHelper.stopScan()
+ }
+
+
+ private suspend fun readUserFromCheckMePro() {
+ userChannel.receive()
+
+ val userTemp = File(Constant.getPathX("usr.dat")).readBytes() // CRASHES HERE
+ userTemp.apply {
+ userInfo = UserInfo(this)
+
+ val total = userInfo.user.size * 2 + 6 + 1
+ var tIndex = 1
+ UiChannel.progressChannel.send(tIndex * 100 / total)
+ for (user in userInfo.user) {
+ for (f in userFileName) {
+ bleWorker.getFile(user.id + f)
+ tIndex++
+ UiChannel.progressChannel.send(tIndex * 100 / total)
+ }
+ mUserData.add(user)
+ }
+ for (f in commonFileName) {
+ bleWorker.getFile(f)
+ tIndex++
+ UiChannel.progressChannel.send(tIndex * 100 / total)
+ }
+
+ delay(300)
+ UiChannel.progressChannel.close()
+ sendUsersDataToFlutter()
+
+ }
+ }
+
+
+ private fun sendUsersDataToFlutter() {
+ if (mUserData.isNotEmpty()) {
+
+ val jsonDataList = mutableListOf()
+ for (user in mUserData) {
+ val jsonData = user.toJson()
+ jsonDataList.add(jsonData)
+ }
+ Log.d("userBeanJSON", jsonDataList.toString())
+ val returnData = mapOf("type" to "CheckMeUsersList", "data" to jsonDataList)
+ eventSink?.success(returnData)
+ }
+ }
+
+ private fun getBPDataFromCheckMePro(s: String) {
+ val file = File(Constant.getPathX(s + "nibp.dat"))
+ if (file.exists()) {
+ val temp = file.readBytes()
+ if (temp.isNotEmpty()) {
+ temp.let {
+ val f = BpInfo(it)
+ val json = gson.toJson(f.Bp)
+ Log.d("BPMeter", json.toString())
+ val returnData = mapOf("type" to "BPCheckMePro", "data" to json.toString())
+ eventSink?.success(returnData)
+
+ }
+ }
+ } else {
+ val returnData = mapOf("type" to "FailureCheckMePro", "data" to "")
+ eventSink?.success(returnData)
+ }
+ }
+
+ private fun getGlucoseDataFromCheckMePro(s: String) {
+ val file = File(Constant.getPathX(s + "glu.dat"))
+ if (file.exists()) {
+ val temp = file.readBytes()
+ if (temp.isNotEmpty()) {
+ temp.let {
+ val f = GluInfo(it)
+ val json = gson.toJson(f.Glu)
+ Log.d("BloodGlucoseMeter", json.toString())
+ val returnData = mapOf("type" to "GlucoseCheckMePro", "data" to json.toString())
+ eventSink?.success(returnData)
+
+ }
+ }
+ } else {
+ val returnData = mapOf("type" to "FailureCheckMePro", "data" to "")
+ eventSink?.success(returnData)
+ }
+ }
+
+
+ private fun getOxyDataFromCheckMePro() {
+ val file = File(Constant.getPathX("oxi.dat"))
+ if (file.exists()) {
+ val temp = file.readBytes()
+ if (temp.isNotEmpty()) {
+ temp.let {
+ val f = OxyInfo(it)
+ val json = gson.toJson(f.Oxy)
+ Log.d("Oximeter", json.toString())
+ val returnData = mapOf("type" to "OxiCheckMePro", "data" to json.toString())
+ eventSink?.success(returnData)
+
+ }
+ }
+ } else {
+ val returnData = mapOf("type" to "FailureCheckMePro", "data" to "")
+ eventSink?.success(returnData)
+ }
+ }
+
+
+ private fun getECGDataFromCheckMePro() {
+ val file = File(Constant.getPathX("ecg.dat"))
+ if (file.exists()) {
+ val temp = file.readBytes()
+ if (temp.isNotEmpty()) {
+ temp.let {
+ val f = EcgInfo(it)
+ val json = gson.toJson(f.ecg)
+ Log.d("ECGMeter", json.toString())
+ val returnData = mapOf("type" to "ECGCheckMePro", "data" to json.toString())
+
+ eventSink?.success(returnData)
+
+ }
+ }
+ } else {
+ val returnData = mapOf("type" to "FailureCheckMePro", "data" to "")
+ eventSink?.success(returnData)
+ }
+ }
+
+ private fun getECGWaveFromCheckMePro(timeString: String) {
+
+ val file = File(Constant.getPathX(timeString))
+ val exist = file.exists()
+ MainScope().launch {
+ if (!exist) {
+// if (!MainActivity.isOffline) {
+ bleWorker.getFile(timeString)
+// }
+ }
+ val file2 = File(Constant.getPathX(timeString))
+ delay(100)
+ BleDataWorker.fileProgressChannel.send(BleDataWorker.FileProgress(progress = 0))
+ delay(100)
+ BleDataWorker.fileProgressChannel.send(BleDataWorker.FileProgress(progress = -100))
+ if (file2.exists()) {
+ val info = EcgWaveInfo(file2.readBytes())
+ val json = gson.toJson(info)
+ Log.d("ECGWaveData", json.toString())
+ val returnData = mapOf("type" to "ECGWaveCheckMePro", "data" to json.toString())
+ eventSink?.success(returnData)
+ }
+ }
+ }
+
+ private fun getTemperatureDataFromCheckMePro() {
+ val file = File(Constant.getPathX("tmp.dat"))
+ if (file.exists()) {
+ val temp = file.readBytes()
+ if (temp.isNotEmpty()) {
+ temp.let {
+ val f = TmpInfo(it)
+ val json = gson.toJson(f.Tmp)
+ Log.d("TempMeter", json.toString())
+ val returnData = mapOf("type" to "TemperatureCheckMePro", "data" to json.toString())
+ eventSink?.success(returnData)
+
+ }
+ }
+ } else {
+ val returnData = mapOf("type" to "FailureCheckMePro", "data" to "")
+ eventSink?.success(returnData)
+ }
+ }
+
+
+ @RequiresApi(Build.VERSION_CODES.Q)
+ private fun subscribeToStreams(deviceName: String) {
+
+
+ // ECG TRACKER
+ if (deviceName.contains("DuoEK", ignoreCase = true)) {
+ LiveEventBus.get(InterfaceEvent.ER2.EventEr2Info).observe(this.mainActivity) {
+ val data = it.data as DeviceInfo
+ println("DuoEK INFO DATA: $data")
+ val returnData = mapOf("type" to "infoData", "data" to data.toString())
+ eventSink?.success(returnData)
+ BleServiceHelper.BleServiceHelper.startRtTask(model)
+ }
+ LiveEventBus.get(InterfaceEvent.ER2.EventEr2FactoryReset).observe(this.mainActivity) {
+ val data = it.data as Boolean // We will get a ping from here to confirm if reset success
+ }
+ LiveEventBus.get(InterfaceEvent.ER2.EventEr2FileList).observe(this.mainActivity) {
+ BleServiceHelper.BleServiceHelper.stopRtTask(model)
+ ecgFileNames = it.data as ArrayList
+ readFileForEr2()
+ }
+ LiveEventBus.get(InterfaceEvent.ER2.EventEr2SetTime).observe(this.mainActivity) {
+ println("EventEr2SetTime")
+ BleServiceHelper.BleServiceHelper.er2GetInfo(model)
+// ecgRTTask.run()
+ }
+ LiveEventBus.get(InterfaceEvent.ER2.EventEr2ReadFileComplete).observe(this.mainActivity) {
+ println("EventEr2ReadFileComplete")
+ val data = it.data as Er2File
+ if (data.fileName.contains("R")) {
+ val file = Er2EcgFile(data.content)
+ val ecgShorts = Er1Decompress.unCompressAlgECG(file.waveData)
+ val ecgData = EcgData()
+ val startTime = DateUtil.getSecondTimestamp(data.fileName.replace("R", ""))
+ ecgData.fileName = data.fileName
+ ecgData.duration = file.recordingTime
+ ecgData.shortData = ecgShorts
+ ecgData.startTime = startTime
+ ecgList.add(ecgData)
+ print(ecgList)
+
+ }
+ ecgFileNames.removeAt(0)
+ readFileForEr2()
+ }
+ LiveEventBus.get(InterfaceEvent.ER2.EventEr2RtData).observe(this.mainActivity) {
+ val data = it.data as RtData
+ println("EventEr2RtData")
+ val returnData = mapOf("type" to "RealTimeDataECG", "data" to gson.toJson(data))
+ println(returnData)
+ eventSink?.success(returnData)
+ }
+ }
+
+
+ if (deviceName.contains("ER1")) {
+ LiveEventBus.get(InterfaceEvent.ER1.EventEr1Info).observe(this.mainActivity) {
+ val data = it.data as com.lepu.blepro.ext.er1.DeviceInfo
+ println("ER1 INFO DATA: $data")
+ val returnData = mapOf("type" to "infoData", "data" to data.toString())
+ eventSink?.success(returnData)
+ BleServiceHelper.BleServiceHelper.startRtTask(model)
+ }
+ LiveEventBus.get(InterfaceEvent.ER1.EventEr1ResetFactory).observe(this.mainActivity) {
+ val data = it.data as Boolean
+ }
+ LiveEventBus.get(InterfaceEvent.ER1.EventEr1FileList).observe(this.mainActivity) {
+ BleServiceHelper.BleServiceHelper.stopRtTask(model)
+ ecgFileNames = it.data as ArrayList
+ readFileForEr1()
+ }
+ LiveEventBus.get(InterfaceEvent.ER1.EventEr1SetTime).observe(this.mainActivity) {
+ println("EventEr1SetTime")
+ BleServiceHelper.BleServiceHelper.er1GetInfo(model)
+ }
+ LiveEventBus.get(InterfaceEvent.ER1.EventEr1ReadFileComplete).observe(this.mainActivity) {
+ println("EventEr1ReadFileComplete")
+ val rawFile = getOffset(it.model, ecgFileNames[0], "")
+ val data = it.data as Er1File
+
+ val file = if (rawFile.isEmpty()) {
+ Er1EcgFile(data.content)
+ } else {
+ Er1EcgFile(rawFile)
+ }
+ val ecgShorts = Er1Decompress.unCompressAlgECG(file.waveData)
+ val ecgData = EcgData()
+ val startTime = DateUtil.getSecondTimestamp(data.fileName.replace("R", ""))
+ ecgData.fileName = data.fileName
+ ecgData.duration = file.recordingTime
+ ecgData.shortData = ecgShorts
+ ecgData.startTime = startTime
+ ecgList.add(ecgData)
+
+ ecgFileNames.removeAt(0)
+ readFileForEr1()
+ }
+ LiveEventBus.get(InterfaceEvent.ER1.EventEr1RtData).observe(this.mainActivity) {
+ val data = it.data as com.lepu.blepro.ext.er1.RtData
+ println("EventEr2RtData")
+ val returnData = mapOf("type" to "RealTimeDataECG", "data" to gson.toJson(data))
+ println(returnData)
+ eventSink?.success(returnData)
+ // sampling rate:125HZ
+ // mV = n * 0.002467(data.wave.ecgFloats = data.wave.ecgShorts * 0.002467)
+ // data.param.batteryState:0(no charge),1(charging),2(charging complete),3(low battery)
+ // data.param.battery:0-100
+ // data.param.recordTime:unit(s)
+ // data.param.curStatus:0(idle),1(preparing),2(measuring),3(saving file),4(saving succeed),
+ // 5(less than 30s, file not saved),6(6 retests),7(lead off)
+ }
+
+ }
+
+ // OXYMETER
+ if (deviceName == "POD-1_SN8187" || deviceName == "PC-60FW") { //DONE
+ LiveEventBus.get(InterfaceEvent.PC60Fw.EventPC60FwRtParam).observe(this.mainActivity) {
+ val data = it.data as RtParam
+ println("POD-1_SN8187: $data")
+ println("PC-60FW: $data")
+ val oxyModel = OxymeterModel(data.spo2, data.pr, data.pi, data.isProbeOff, data.isPulseSearching, true, 100);
+ val returnData = mapOf("type" to "OxyRtParam", "data" to gson.toJson(oxyModel))
+ eventSink?.success(returnData)
+ }
+ }
+
+
+ if (deviceName == "O2M 1670" || deviceName == "O2Ring 4135") { // DONE
+ rtHandler.removeCallbacks(rtTask)
+ rtHandler.post(rtTask)
+ LiveEventBus.get(InterfaceEvent.Oxy.EventOxyRtParamData).observe(this.mainActivity) {
+ val data = it.data as com.lepu.blepro.ext.oxy.RtParam
+ println("O2M 1670: $data")
+ println("O2Ring 4135: $data")
+ val oxyModel = OxymeterModel(data.spo2, data.pr, data.pi, false, true, true, data.battery);
+ val returnData = mapOf("type" to "OxyRtParam", "data" to gson.toJson(oxyModel))
+ eventSink?.success(returnData)
+ }
+ }
+
+
+ if (deviceName.contains("SP-20", ignoreCase = true)) { //DONE
+ LiveEventBus.get(InterfaceEvent.SP20.EventSp20SetTime).observe(this.mainActivity) {
+ println("EventSp20SetTime")
+ BleServiceHelper.BleServiceHelper.bp2GetInfo(model)
+ }
+ LiveEventBus.get(InterfaceEvent.SP20.EventSp20DeviceInfo).observe(this.mainActivity) {
+ val data = it.data as com.lepu.blepro.ext.sp20.DeviceInfo
+ println("EventSp20DeviceInfo: $data")
+ val returnData = mapOf("type" to "infoData", "data" to data.toString(), "deviceName" to deviceName)
+ eventSink?.success(returnData)
+ BleServiceHelper.BleServiceHelper.startRtTask(model)
+ }
+ LiveEventBus.get(InterfaceEvent.SP20.EventSp20RtParam).observe(this.mainActivity) {
+ val data = it.data as com.lepu.blepro.ext.sp20.RtParam
+ println("OxyRtParam: $data")
+ val oxyModel = OxymeterModel(data.spo2, data.pr, data.pi, data.isProbeOff, data.isPulseSearching, data.isCheckProbe, data.battery);
+ val returnData = mapOf("type" to "OxyRtParam", "data" to gson.toJson(oxyModel))
+ eventSink?.success(returnData)
+ }
+ LiveEventBus.get(InterfaceEvent.SP20.EventSp20RtWave).observe(this.mainActivity) {
+ val data = it.data as RtWave
+ println("OxyRtParam: $data")
+
+ }
+ }
+
+
+ //BLOOD PRESSURE
+ if (deviceName.contains("BP2", ignoreCase = true)) {
+ LiveEventBus.get(InterfaceEvent.BP2.EventBp2SyncTime).observe(this.mainActivity) {
+ println("EventBp2SyncTime")
+ // Get Device Info
+ BleServiceHelper.BleServiceHelper.bp2GetInfo(model)
+ }
+ LiveEventBus.get(InterfaceEvent.BP2.EventBp2Info).observe(this.mainActivity) {
+ val data = it.data as com.lepu.blepro.ext.bp2.DeviceInfo
+ println("BP2 INFO DATA: $data")
+ val returnData = mapOf("type" to "infoData", "data" to data.toString(), "deviceName" to deviceName)
+ eventSink?.success(returnData)
+ BleServiceHelper.BleServiceHelper.startRtTask(model)
+ }
+ LiveEventBus.get(InterfaceEvent.BP2.EventBp2FileList).observe(this.mainActivity) {
+ BleServiceHelper.BleServiceHelper.stopRtTask(model)
+ ecgFileNames = it.data as ArrayList
+ println("EventBp2FileList: ${it.data}")
+ readFileForBp2()
+ }
+ LiveEventBus.get(InterfaceEvent.BP2.EventBp2ReadFileComplete).observe(this.mainActivity) {
+ val data = it.data as Bp2File
+ // data.type: 1(BP), 2(ECG)
+ println("LiveEventBus with BP2 : $data")
+
+ if (data.type == 1) {
+ val file = BpFile(data.content)
+ // file.measureTime:unit(s)
+ println("BpFile : $file")
+ } else if (data.type == 2) {
+
+ val file = EcgFile(data.content)
+ val ecgShorts = Er1Decompress.unCompressAlgECG(file.waveData)
+ val ecgData = EcgData()
+ val startTime = DateUtil.getSecondTimestamp(data.fileName)
+ ecgData.fileName = data.fileName
+ ecgData.duration = file.recordingTime
+ ecgData.shortData = ecgShorts
+ ecgData.startTime = startTime
+ ecgList.add(ecgData)
+ print(ecgList)
+
+ }
+ ecgFileNames.removeAt(0)
+ readFileForBp2()
+ }
+ LiveEventBus.get(InterfaceEvent.BP2.EventBp2RtData).observe(this.mainActivity) {
+ val data = it.data as com.lepu.blepro.ext.bp2.RtData
+ // data.status: RtStatus
+ // data.status.deviceStatus: 0(STATUS_SLEEP), 1(STATUS_MEMERY), 2(STATUS_CHARGE), 3(STATUS_READY),
+ // 4(STATUS_BP_MEASURING), 5(STATUS_BP_MEASURE_END),
+ // 6(STATUS_ECG_MEASURING), 7(STATUS_ECG_MEASURE_END), 20(STATUS_VEN)
+ // data.status.batteryStatus: 0(no charge), 1(charging), 2(charging complete), 3(low battery)
+ // data.status.percent: 0-100
+ // data.param: RtParam
+ // data.param.paramDataType: 0(Bp measuring), 1(Bp end), 2(Ecg measuring), 3(Ecg end)
+
+ when (data.param.paramDataType) {
+ 0 -> {
+ val bpIng = RtBpIng(data.param.paramData)
+
+ //class BpRtMeasuringModel {
+ // bool deflate;
+ // int pr;
+ // int pressure;
+ // bool pulse;
+ val bpMeasuring = BPModelMeasuring(bpIng.pressure, bpIng.pr, bpIng.isDeflate, bpIng.isPulse);
+
+ val returnData = mapOf("type" to "RealTimeDataBPMeasuring", "data" to gson.toJson(bpMeasuring))
+ eventSink?.success(returnData)
+
+ }
+
+ 1 -> {
+ val bpResult = RtBpResult(data.param.paramData)
+ val bpModel = BPModelResult(bpResult.isDeflate, bpResult.dia, bpResult.mean, bpResult.pr, bpResult.pressure, bpResult.result, bpResult.sys);
+
+ val returnData = mapOf("type" to "RealTimeDataBPResult", "data" to gson.toJson(bpModel))
+ eventSink?.success(returnData)
+
+
+ }
+
+ 2 -> {
+ val ecgIng = RtEcgIng(data.param.paramData)
+ val returnData = mapOf("type" to "RealTimeDataECGMeasuring", "data" to gson.toJson(ecgIng))
+ println("RealTimeDataECGMeasuring: $returnData")
+ eventSink?.success(returnData)
+// hr.text = "${ecgIng.hr}"
+// data_log.text = "lead status:${if (ecgIng.isLeadOff) "lead off" else "lead on"}\n" +
+// "pool signal:${if (ecgIng.isPoolSignal) "yes" else "no"}\n" +
+// "duration: ${ecgIng.curDuration} s"
+// DataController.receive(data.param.ecgFloatsFilter)
+ // sampling rate:250HZ
+ // mV = n * 0.003098 (data.param.ecgFloats = data.param.ecgShorts * 0.003098)
+ }
+
+ 3 -> {
+ val ecgResult = RtEcgResult(data.param.paramData)
+ val returnData = mapOf("type" to "RealTimeDataECGResult", "data" to gson.toJson(ecgResult))
+ println("RealTimeDataECGResult: $returnData")
+ eventSink?.success(returnData)
+// hr.text = "${ecgResult.hr}"
+// data_log.text = "result:${ecgResult.diagnosis.resultMess}\n" +
+// "hr:${ecgResult.hr}\n" +
+// "qrs:${ecgResult.qrs}\n" +
+// "pvcs:${ecgResult.pvcs}\n" +
+// "qtc:${ecgResult.qtc}"
+ // ecgResult.diagnosis:EcgDiagnosis
+ // diagnosis.isRegular:Whether Regular ECG Rhythm
+ // diagnosis.isPoorSignal:Whether Unable to analyze
+ // diagnosis.isLeadOff:Whether Always lead off
+ // diagnosis.isFastHr:Whether Fast Heart Rate
+ // diagnosis.isSlowHr:Whether Slow Heart Rate
+ // diagnosis.isIrregular:Whether Irregular ECG Rhythm
+ // diagnosis.isPvcs:Whether Possible ventricular premature beats
+ // diagnosis.isHeartPause:Whether Possible heart pause
+ // diagnosis.isFibrillation:Whether Possible Atrial fibrillation
+ // diagnosis.isWideQrs:Whether Wide QRS duration
+ // diagnosis.isProlongedQtc:Whether QTc is prolonged
+ // diagnosis.isShortQtc:Whether QTc is short
+ }
+
+ }
+
+ }
+ }
+
+ }
+
+ @RequiresApi(Build.VERSION_CODES.Q)
+ private fun connectDevice(device: List) {
+ println("connectDevice: $device");
+ model = device[1].toInt()
+ val deviceName = device[0]
+ for (b in BluetoothController.getDevices()) {
+ println("getDevices device: ${b.name}")
+ if (b.name == deviceName) {
+ BleServiceHelper.BleServiceHelper.setInterfaces(b.model)
+// BleServiceHelper.BleServiceHelper.stopScan()
+ BleServiceHelper.BleServiceHelper.connect(
+ this.mainActivity.applicationContext, model, b.device
+ )
+
+ subscribeToStreams(deviceName);
+ }
+
+ }
+
+ }
+
+ private fun connectDeviceCheckMe(device: List) {
+ val currentBluetoothDevice = bleListCheckMe.first { bleBean ->
+ bleBean.name == device[0]
+ }
+ Log.d("DeviceFound", "This is the Device i have : ${currentBluetoothDevice.name}")
+// stopScanForCheckMePro()
+ bleWorker.initWorker(this.mainActivity, currentBluetoothDevice.bluetoothDevice)
+ dataScope.launch {
+ val a = withTimeoutOrNull(10000) {
+ bleWorker.waitConnect()
+ }
+ a?.let {
+ val b = withTimeoutOrNull(10000) {
+ Log.d("getFile", "getFile 718")
+ bleWorker.getFile("usr.dat")
+ }
+ b?.let {
+ userChannel.send(1)
+ }
+
+
+ }
+ }
+ uiScope.launch {
+ readUserFromCheckMePro()
+ }
+
+
+// uiScope.launch {
+// val a = withTimeoutOrNull(10000) {
+// bleWorker.waitConnect()
+// }
+// a?.let {
+// val b = withTimeoutOrNull(10000) {
+// bleWorker.getFile("usr.dat")
+// }
+// }
+// }
+
+
+// uiScope.launch {
+////
+// getCheckMeProDeviceInfo()
+// }
+
+
+ }
+
+ private fun disConnectDeviceCheckMe() {}
+
+
+ private fun readFileForEr2() {
+ if (ecgFileNames.size == 0) {
+ val returnData = mapOf("type" to "FileDetail", "data" to gson.toJson(ecgList))
+ eventSink?.success(returnData)
+ } else {
+ BleServiceHelper.BleServiceHelper.er2ReadFile(model, ecgFileNames[0])
+ }
+ }
+
+ private fun readFileForEr1() {
+ if (ecgFileNames.size == 0) {
+ val returnData = mapOf("type" to "FileDetail", "data" to gson.toJson(ecgList))
+ eventSink?.success(returnData)
+ } else {
+ BleServiceHelper.BleServiceHelper.er1ReadFile(model, ecgFileNames[0])
+ }
+ }
+
+ private fun readFileForBp2() {
+ if (ecgFileNames.size == 0) {
+ val returnData = mapOf("type" to "FileDetail", "data" to gson.toJson(ecgList))
+ eventSink?.success(returnData)
+ } else {
+ BleServiceHelper.BleServiceHelper.bp2ReadFile(model, ecgFileNames[0])
+ }
+ }
+
+ // sdk save the original file name : userId + fileName + .dat
+ private fun getOffset(model: Int, fileName: String, userId: String): ByteArray {
+ val trimStr = HexString.trimStr(fileName)
+ BleServiceHelper.BleServiceHelper.rawFolder?.get(model)?.let { s ->
+ val mFile = File(s, "$userId$trimStr.dat")
+ if (mFile.exists()) {
+ FileUtils.readFileToByteArray(mFile)?.let {
+ return it
+ }
+ } else {
+ return ByteArray(0)
+ }
+ }
+ return ByteArray(0)
+ }
+
+ private fun getECGFilesList(device: List) {
+ //Get ECG File List
+ val deviceName = device[0]
+ println("deviceName: $deviceName and model: $model")
+
+ if (deviceName.contains("DuoEK")) {
+ BleServiceHelper.BleServiceHelper.er2GetFileList(model);
+ } else if (deviceName.contains("BP2")) {
+ BleServiceHelper.BleServiceHelper.bp2GetFileList(model);
+ }
+ }
+
+ private fun factoryResetECG(device: List) {
+ val deviceName = device[0]
+
+ println("deviceName: $deviceName and model: $model")
+
+ if (deviceName.contains("DuoEK")) {
+ BleServiceHelper.BleServiceHelper.er2FactoryResetAll(model);
+ } else if (deviceName.contains("BP2")) {
+ BleServiceHelper.BleServiceHelper.bp2FactoryResetAll(model);
+ }
+ }
+
+ private fun getBP2FilesList() {
+ //Get BP2 File List
+ BleServiceHelper.BleServiceHelper.bp2GetFileList(model)
+ }
+
+
+ // SMART RING FUNCTIONS
+ var filterRssi: Int = -100
+
+ @SuppressLint("MissingPermission")
+ fun onScanResultSmartRing(name: String, bluetoothDevice: BluetoothDevice) {
+ if (!(name.contains("2301"))) return;
+ Log.d("SmartRingDeviceFound", "This is the Device Name: $name")
+ val bluetoothDeviceJson = JSONObject().apply {
+ put("name", bluetoothDevice.name ?: "")
+ put("macAddr", bluetoothDevice.address ?: "")
+ put("type", bluetoothDevice.type)
+ // Add more properties as needed
+ }
+
+ println("EventDeviceFound : ${bluetoothDeviceJson.toString()}")
+
+ val returnData = mapOf("type" to "DevicesListSmartRing", "data" to bluetoothDeviceJson.toString())
+ eventSink?.success(returnData)
+
+ }
+
+ @RequiresApi(Build.VERSION_CODES.M)
+ private fun scanForSmartRing(bleScanManager: BleScanManager) {
+ initVar()
+ bleScanManager.initScan(this.mainActivity)
+ bleScanManager.setCallBack(object : BleScanManager.Scan {
+ override fun scanReturn(name: String, bluetoothDevice: BluetoothDevice) {
+ onScanResultSmartRing(name, bluetoothDevice)
+ }
+ })
+ }
+
+ private fun connectDeviceSmartRing(deviceAddress: String) {
+ BleManager.instance?.connectDevice(deviceAddress, true, object : BleConnectionListener {
+ override fun BleStatus(status: Int, newState: Int) { //蓝牙4.0连接状态 Bluetooth 4.0 connection status
+ Log.e("BleStatus", "BleStatus: $status***$newState")
+ }
+
+ override fun ConnectionSucceeded() { //连接设备成功 Successfully connected the device
+ Log.e("BleStatus", "ConnectionSucceeded")
+ getBatteryLevelSmartRing()
+ }
+
+ override fun Connecting() { //设备连接中 Device is connected
+ Log.e("BleStatus", "Connecting")
+ }
+
+ override fun ConnectionFailed() { //设备连接失败 Device connection failed
+ Log.e("BleStatus", "ConnectionFailed")
+ }
+
+ override fun OnReconnect() { //重新连接中 Reconnecting
+ Log.e("BleStatus", "OnReconnect")
+ }
+
+ override fun BluetoothSwitchIsTurnedOff() { //蓝牙开关被关闭 Bluetooth switch is turned off
+ Log.e("BleStatus", "BluetoothSwitchIsTurnedOff")
+ }
+ })
+ }
+
+ private fun disConnectDeviceSmartRing() {
+ BleManager.instance?.disconnectDevice();
+ }
+
+
+ private fun getBatteryLevelSmartRing() {
+ sendValue(BleSDK.GetDeviceBatteryLevel());
+ }
+
+ private fun getTemperatureSmartRing(mode: Byte) {
+ sendValue(GetTemperature_historyData(mode, ""))
+ }
+
+ private fun getDynamicHeartRateSmartRing(mode: Byte) {
+ sendValue(GetDynamicHRWithMode(mode, ""))
+ }
+
+ private fun getStaticHeartRateSmartRing(mode: Byte) {
+ sendValue(GetStaticHRWithMode(mode, ""))
+ }
+
+ private fun getHRVSmartRing(mode: Byte) {
+
+ sendValue(GetHRVDataWithMode(mode, ""))
+ }
+
+ private fun getBloodOxygenSmartRing(mode: Byte) {
+
+ sendValue(Oxygen_data(mode))
+ }
+
+ private fun getTotalStepCountSmartRing(mode: Byte) {
+ sendValue(GetTotalActivityDataWithMode(mode, ""))
+ }
+
+ private fun getSportsModeDataSmartRing(mode: Byte) {
+ // 9 for walt
+ sendValue(EnterActivityMode(2, 9, ExerciseMode.Status_START))
+
+ }
+
+ private fun stopSportsModeDataSmartRing(mode: Byte) {
+ // 9 for walt
+ sendValue(EnterActivityMode(2, 9, ExerciseMode.Status_FINISH))
+
+ }
+
+
+ private var temperatureDataCount = 0
+ private var dynamicHeartRateDataCount = 0
+ private var staticHeartRateDataCount = 0
+ private var hrvDataCount = 0
+ private var bloodOxygenDataCount = 0
+ private var totalActityDataCount = 0
+ override fun dataCallback(maps: Map?) {
+ super.dataCallback(maps)
+ val dataType = getDataType(maps)
+ Log.d("dataCallbackType", dataType.toString())
+ val finish = getEnd(maps!!)
+
+ when (dataType) {
+ BleConst.GetDeviceBatteryLevel -> {
+ Log.d("BatteryLevel", maps.toString())
+ val returnData = mapOf("type" to "BatteryLevelSmartRing", "data" to gson.toJson(maps))
+ eventSink?.success(returnData)
+ }
+
+ BleConst.Temperature_history -> {
+ temperatureDataCount++
+ if (finish) {
+ temperatureDataCount = 0
+ Log.d("TemperatureSmartRing", maps.toString())
+ val returnData = mapOf("type" to "TemperatureSmartRing", "data" to gson.toJson(maps))
+ eventSink?.success(returnData)
+ }
+ if (temperatureDataCount == 50) {
+ temperatureDataCount = 0
+ if (finish) {
+ Log.d("TemperatureSmartRing", maps.toString())
+ val returnData = mapOf("type" to "TemperatureSmartRing", "data" to gson.toJson(maps))
+ eventSink?.success(returnData)
+ } else {
+ getTemperatureSmartRing(modeContinue)
+ }
+ }
+ }
+
+ BleConst.GetDynamicHR -> {
+ dynamicHeartRateDataCount++
+ if (finish) {
+ dynamicHeartRateDataCount = 0
+ Log.d("DynamicHRSmartRing", maps.toString())
+ val returnData = mapOf("type" to "DynamicHeartRateSmartRing", "data" to gson.toJson(maps))
+ eventSink?.success(returnData)
+ }
+ if (dynamicHeartRateDataCount == 50) {
+ dynamicHeartRateDataCount = 0
+ if (finish) {
+ Log.d("DynamicHRSmartRing", maps.toString())
+ val returnData = mapOf("type" to "DynamicHeartRateSmartRing", "data" to gson.toJson(maps))
+ eventSink?.success(returnData)
+ } else {
+ getDynamicHeartRateSmartRing(modeContinue)
+ }
+ }
+
+ }
+
+
+ BleConst.GetStaticHR -> {
+ staticHeartRateDataCount++
+ if (finish) {
+ staticHeartRateDataCount = 0
+ Log.d("StaticHRSmartRing", maps.toString())
+ val returnData = mapOf("type" to "StaticHeartRateSmartRing", "data" to gson.toJson(maps))
+ eventSink?.success(returnData)
+ }
+ if (staticHeartRateDataCount == 50) {
+ staticHeartRateDataCount = 0
+ if (finish) {
+ Log.d("StaticHRSmartRing", maps.toString())
+ val returnData = mapOf("type" to "StaticHeartRateSmartRing", "data" to gson.toJson(maps))
+ eventSink?.success(returnData)
+ } else {
+ getStaticHeartRateSmartRing(modeContinue)
+ }
+ }
+
+ }
+
+ BleConst.GetHRVData -> {
+ hrvDataCount++
+ if (finish) {
+ hrvDataCount = 0
+ Log.d("HrvSmartRing", maps.toString())
+ val returnData = mapOf("type" to "HrvSmartRing", "data" to gson.toJson(maps))
+ eventSink?.success(returnData)
+ }
+ if (hrvDataCount == 50) {
+ if (finish) {
+ hrvDataCount = 0
+ Log.d("HrvSmartRing", maps.toString())
+ val returnData = mapOf("type" to "HrvSmartRing", "data" to gson.toJson(maps))
+ eventSink?.success(returnData)
+ } else {
+ getHRVSmartRing(modeContinue)
+ }
+ }
+ }
+
+ BleConst.GetAutomaticSpo2Monitoring -> {
+ bloodOxygenDataCount++
+ if (finish) {
+ bloodOxygenDataCount = 0
+ Log.d("BloodOxygenSmartRing", maps.toString())
+ val returnData = mapOf("type" to "BloodOxygenSmartRing", "data" to gson.toJson(maps))
+ eventSink?.success(returnData)
+ }
+ if (bloodOxygenDataCount == 50) {
+ bloodOxygenDataCount = 0
+ if (finish) {
+ bloodOxygenDataCount = 0
+ Log.d("BloodOxygenSmartRing", maps.toString())
+ val returnData = mapOf("type" to "BloodOxygenSmartRing", "data" to gson.toJson(maps))
+ eventSink?.success(returnData)
+ } else {
+
+ getBloodOxygenSmartRing(modeContinue)
+ }
+ }
+ }
+
+ BleConst.GetTotalActivityData -> {
+ totalActityDataCount++
+ if (finish) {
+ totalActityDataCount = 0
+ Log.d("TotalStepCountSmartRing", maps.toString())
+ val returnData = mapOf("type" to "TotalStepCountSmartRing", "data" to gson.toJson(maps))
+ eventSink?.success(returnData)
+ }
+ if (totalActityDataCount == 50) {
+ totalActityDataCount = 0
+ if (finish) {
+ totalActityDataCount = 0
+ Log.d("TotalStepCountSmartRing", maps.toString())
+ val returnData = mapOf("type" to "TotalStepCountSmartRing", "data" to gson.toJson(maps))
+ eventSink?.success(returnData)
+ } else {
+ getTotalStepCountSmartRing(modeContinue) //继续读取
+ }
+ }
+ }
+
+ BleConst.SportData -> {
+ Log.d("RTSportDataSmartRing", maps.toString())
+ val returnData = mapOf("type" to "RTSportDataSmartRing", "data" to gson.toJson(maps))
+ eventSink?.success(returnData)
+ }
+ }
+ }
+
+
+}
\ No newline at end of file
diff --git a/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/ble/OxymeterModel.java b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/ble/OxymeterModel.java
new file mode 100644
index 00000000..54b74261
--- /dev/null
+++ b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/ble/OxymeterModel.java
@@ -0,0 +1,22 @@
+package com.cloud.diplomaticquarterapp.ble;
+
+public class OxymeterModel {
+
+ public int spo2;
+ public int pr;
+ public float pi;
+ public boolean isProbeOff;
+ public boolean isPulseSearching;
+ public boolean isCheckProbe;
+ public int battery;
+
+ public OxymeterModel(int spo2, int pr, float pi, boolean isProbeOff, boolean isPulseSearching, boolean isCheckProbe, int battery) {
+ this.spo2 = spo2;
+ this.pr = pr;
+ this.pi = pi;
+ this.isProbeOff = isProbeOff;
+ this.isPulseSearching = isPulseSearching;
+ this.isCheckProbe = isCheckProbe;
+ this.battery = battery;
+ }
+}
diff --git a/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/ble/utils/AlgorithmUtil.java b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/ble/utils/AlgorithmUtil.java
new file mode 100644
index 00000000..1b1f8b9c
--- /dev/null
+++ b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/ble/utils/AlgorithmUtil.java
@@ -0,0 +1,12 @@
+package com.cloud.diplomaticquarterapp.ble.utils;
+
+public class AlgorithmUtil {
+ public static native double[] filter(double var0, boolean var2);
+
+ public static native short[] shortFilter(short[] var0);
+
+ static {
+ System.loadLibrary("online-lib");
+ System.loadLibrary("offline-lib");
+ }
+}
diff --git a/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/ble/utils/EcgData.java b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/ble/utils/EcgData.java
new file mode 100644
index 00000000..23014698
--- /dev/null
+++ b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/ble/utils/EcgData.java
@@ -0,0 +1,57 @@
+package com.cloud.diplomaticquarterapp.ble.utils;
+
+import java.util.Arrays;
+
+public class EcgData {
+
+ private long startTime; // 时间戳s
+ private int duration;
+ private String fileName;
+ private byte[] data;
+ private short[] shortData;
+
+ public long getStartTime() {
+ return startTime;
+ }
+
+ public void setStartTime(long startTime) {
+ this.startTime = startTime;
+ }
+
+ public int getDuration() {
+ return duration;
+ }
+
+ public void setDuration(int duration) {
+ this.duration = duration;
+ }
+
+ public String getFileName() {
+ return fileName;
+ }
+
+ public void setFileName(String fileName) {
+ this.fileName = fileName;
+ }
+
+ public byte[] getData() {
+ return data;
+ }
+
+ public void setData(byte[] data) {
+ this.data = data;
+ }
+
+ public short[] getShortData() {
+ return shortData;
+ }
+
+ public void setShortData(short[] shortData) {
+ this.shortData = shortData;
+ }
+
+ @Override
+ public String toString() {
+ return "{" + "startTime:" + startTime + ", duration:" + duration + ", fileName:'" + fileName + '\'' + ", data:" + Arrays.toString(data) + ", shortData:" + Arrays.toString(shortData) + '}';
+ }
+}
diff --git a/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/ble/utils/FilterUtil.java b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/ble/utils/FilterUtil.java
new file mode 100644
index 00000000..ef2d86ca
--- /dev/null
+++ b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/ble/utils/FilterUtil.java
@@ -0,0 +1,49 @@
+//package com.cloud.diplomaticquarterapp.ble.utils;
+//
+////
+//// Source code recreated from a .class file by IntelliJ IDEA
+//// (powered by FernFlower decompiler)
+////
+//
+//
+//import doac.doba;
+//import doac.dobn;
+//import doac.docn;
+//
+//public class FilterUtil {
+// public FilterUtil() {
+// }
+//
+// public static short[] getEcgFileFilterData(int var0, byte[] var1) {
+// if (var0 != 7 && var0 != 8) {
+// if (var0 == 19) {
+// return AlgorithmUtil.shortFilter((new doba(var1)).doan());
+// }
+//
+// if (var0 == 52) {
+// return AlgorithmUtil.shortFilter((new docn("", var1, "")).doah());
+// }
+//
+// if (var0 != 77 && var0 != 127) {
+// if (var0 == 32) {
+// return AlgorithmUtil.shortFilter((new doba(var1)).doan());
+// }
+//
+// if (var0 != 33 && var0 != 74 && var0 != 75) {
+// switch (var0) {
+// case 145:
+// case 146:
+// case 147:
+// case 148:
+// case 149:
+// break;
+// default:
+// return new short[0];
+// }
+// }
+// }
+// }
+//
+// return AlgorithmUtil.shortFilter(DecompressUtil.er1Decompress((new dobn(var1)).doad()));
+// }
+//}
diff --git a/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/ble/utils/HexString.java b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/ble/utils/HexString.java
new file mode 100644
index 00000000..91db13db
--- /dev/null
+++ b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/ble/utils/HexString.java
@@ -0,0 +1,50 @@
+package com.cloud.diplomaticquarterapp.ble.utils;
+
+//
+// Source code recreated from a .class file by IntelliJ IDEA
+// (powered by FernFlower decompiler)
+//
+
+public class HexString {
+ private static final char[] HEX_ARRAY = "0123456789ABCDEF".toCharArray();
+
+ private HexString() {
+ }
+
+ public static String bytesToHex(byte[] var0) {
+ char[] var1 = new char[var0.length * 2];
+
+ for(int var2 = 0; var2 < var0.length; ++var2) {
+ int var3 = var0[var2] & 255;
+ int var4 = var2 * 2;
+ char[] var5;
+ char[] var10001 = var5 = HEX_ARRAY;
+ int var10002 = var3;
+ var1[var4] = var5[var3 >>> 4];
+ var3 = var4 + 1;
+ var1[var3] = var10001[var10002 & 15];
+ }
+
+ return new String(var1);
+ }
+
+ public static byte[] hexToBytes(String var0) {
+ if (var0.length() % 2 == 1) {
+ throw new IllegalArgumentException("hexToBytes requires an even-length String parameter");
+ } else {
+ int var1;
+ byte[] var2 = new byte[(var1 = var0.length()) / 2];
+
+ for(int var3 = 0; var3 < var1; var3 += 2) {
+ int var4 = var3 / 2;
+ var2[var4] = (byte)((Character.digit(var0.charAt(var3), 16) << 4) + Character.digit(var0.charAt(var3 + 1), 16));
+ }
+
+ return var2;
+ }
+ }
+
+ public static String trimStr(String var0) {
+ return var0.trim();
+ }
+}
diff --git a/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/check_me_pro/UiChannel.kt b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/check_me_pro/UiChannel.kt
new file mode 100644
index 00000000..d730cd2b
--- /dev/null
+++ b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/check_me_pro/UiChannel.kt
@@ -0,0 +1,7 @@
+package com.cloud.diplomaticquarterapp.check_me_pro
+
+import kotlinx.coroutines.channels.Channel
+
+//object UiChannel {
+// val progressChannel = Channel(Channel.CONFLATED)
+//}
\ No newline at end of file
diff --git a/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/check_me_pro/bean/BleBean.kt b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/check_me_pro/bean/BleBean.kt
new file mode 100644
index 00000000..8833f420
--- /dev/null
+++ b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/check_me_pro/bean/BleBean.kt
@@ -0,0 +1,5 @@
+package com.cloud.diplomaticquarterapp.check_me_pro.bean
+
+import android.bluetooth.BluetoothDevice
+
+data class BleBean(var name: String, var bluetoothDevice: BluetoothDevice)
diff --git a/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/check_me_pro/bean/BpBean.kt b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/check_me_pro/bean/BpBean.kt
new file mode 100644
index 00000000..12fc1eac
--- /dev/null
+++ b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/check_me_pro/bean/BpBean.kt
@@ -0,0 +1,11 @@
+package com.cloud.diplomaticquarterapp.check_me_pro.bean
+
+import java.util.*
+
+data class BpBean(
+ var date: Date = Date(),
+ var timeString: String = "",
+ var sys: Int = 0,
+ var dia: Int=0,
+ var pr: Int = 0
+)
diff --git a/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/check_me_pro/bean/DlcBean.kt b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/check_me_pro/bean/DlcBean.kt
new file mode 100644
index 00000000..29a84374
--- /dev/null
+++ b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/check_me_pro/bean/DlcBean.kt
@@ -0,0 +1,17 @@
+package com.cloud.diplomaticquarterapp.check_me_pro.bean
+
+import java.util.*
+
+data class DlcBean(
+ var date: Date = Date(),
+ var timeString: String = "",
+ var hr: Int = 0,
+ var eface: Int = 0,
+ var oxy: Int = 0,
+ var pi: Int = 0,
+ var oface: Int = 0,
+ var prFlag: Int = 0,
+ var pr: Int = 0,
+ var bpiFace: Int = 0,
+ var voice: Int = 0
+)
diff --git a/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/check_me_pro/bean/EcgBean.kt b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/check_me_pro/bean/EcgBean.kt
new file mode 100644
index 00000000..50ed81c5
--- /dev/null
+++ b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/check_me_pro/bean/EcgBean.kt
@@ -0,0 +1,11 @@
+package com.cloud.diplomaticquarterapp.check_me_pro.bean
+
+import java.util.*
+
+data class EcgBean(
+ var date: Date = Date(),
+ var timeString: String = "",
+ var way: Int = 0,
+ var face: Int = 0,
+ var voice: Int = 0
+)
diff --git a/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/check_me_pro/bean/GluBean.kt b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/check_me_pro/bean/GluBean.kt
new file mode 100644
index 00000000..0a61f3b6
--- /dev/null
+++ b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/check_me_pro/bean/GluBean.kt
@@ -0,0 +1,10 @@
+package com.cloud.diplomaticquarterapp.check_me_pro.bean
+
+import java.util.*
+
+data class GluBean(
+ var date: Date = Date(),
+ var timeString: String = "",
+ var glu:Float=0f,
+ var note:String=""
+)
diff --git a/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/check_me_pro/bean/OxyBean.kt b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/check_me_pro/bean/OxyBean.kt
new file mode 100644
index 00000000..b12829e3
--- /dev/null
+++ b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/check_me_pro/bean/OxyBean.kt
@@ -0,0 +1,13 @@
+package com.cloud.diplomaticquarterapp.check_me_pro.bean
+
+import java.util.*
+
+data class OxyBean(
+ var date: Date = Date(),
+ var timeString: String = "",
+ var way: Int = 0,
+ var oxy: Int = 0,
+ var pr: Int = 0,
+ var pi: Int = 0,
+ var face: Int = 0
+)
diff --git a/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/check_me_pro/bean/PedBean.kt b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/check_me_pro/bean/PedBean.kt
new file mode 100644
index 00000000..36e6aa09
--- /dev/null
+++ b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/check_me_pro/bean/PedBean.kt
@@ -0,0 +1,52 @@
+package com.cloud.diplomaticquarterapp.check_me_pro.bean
+
+import android.os.Parcel
+import android.os.Parcelable
+import java.util.*
+
+data class PedBean(
+ var date: Date = Date(),
+ var timeString: String? = "",
+ var step: Int = 0,
+ var dis: Float = 0f,
+ var speed: Float = 0f,
+ var cal: Float = 0f,
+ var fat: Float = 0f,
+ var time: Int = 0
+) : Parcelable {
+ constructor(parcel: Parcel) : this(
+ TODO("date"),
+ parcel.readString(),
+ parcel.readInt(),
+ parcel.readFloat(),
+ parcel.readFloat(),
+ parcel.readFloat(),
+ parcel.readFloat(),
+ parcel.readInt()
+ ) {
+ }
+
+ override fun writeToParcel(parcel: Parcel, flags: Int) {
+ parcel.writeString(timeString)
+ parcel.writeInt(step)
+ parcel.writeFloat(dis)
+ parcel.writeFloat(speed)
+ parcel.writeFloat(cal)
+ parcel.writeFloat(fat)
+ parcel.writeInt(time)
+ }
+
+ override fun describeContents(): Int {
+ return 0
+ }
+
+ companion object CREATOR : Parcelable.Creator {
+ override fun createFromParcel(parcel: Parcel): PedBean {
+ return PedBean(parcel)
+ }
+
+ override fun newArray(size: Int): Array {
+ return arrayOfNulls(size)
+ }
+ }
+}
diff --git a/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/check_me_pro/bean/SlpBean.kt b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/check_me_pro/bean/SlpBean.kt
new file mode 100644
index 00000000..d2a06abe
--- /dev/null
+++ b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/check_me_pro/bean/SlpBean.kt
@@ -0,0 +1,15 @@
+package com.cloud.diplomaticquarterapp.check_me_pro.bean
+
+import java.util.*
+
+data class SlpBean(
+ var date: Date = Date(),
+ var timeString: String = "",
+ var time: Int = 0,
+ var lowTime: Int = 0,
+ var lowCount: Int = 0,
+ var minO2: Int = 0,
+ var meanO2: Int = 0,
+ var face: Int = 0
+
+)
diff --git a/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/check_me_pro/bean/TmpBean.kt b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/check_me_pro/bean/TmpBean.kt
new file mode 100644
index 00000000..5128c569
--- /dev/null
+++ b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/check_me_pro/bean/TmpBean.kt
@@ -0,0 +1,11 @@
+package com.cloud.diplomaticquarterapp.check_me_pro.bean
+
+import java.util.*
+
+data class TmpBean(
+ var date: Date = Date(),
+ var timeString: String = "",
+ var way: Int = 0,
+ var tmp: Float = 0f,
+ var face: Int = 0
+)
diff --git a/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/check_me_pro/bean/UserBean.kt b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/check_me_pro/bean/UserBean.kt
new file mode 100644
index 00000000..c65812a1
--- /dev/null
+++ b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/check_me_pro/bean/UserBean.kt
@@ -0,0 +1,19 @@
+package com.cloud.diplomaticquarterapp.check_me_pro.bean
+
+import com.google.gson.Gson
+import java.util.*
+
+data class UserBean(
+ var id: String = "",
+ var name: String = "",
+ var ico: Int = 0,
+ var sex: Int = 0,
+ var birthday: Date = Date(),
+ var weight: Int = 0,
+ var height: Int = 0,
+ var color: Int = 0
+)
+
+fun UserBean.toJson(): String {
+ return Gson().toJson(this)
+}
diff --git a/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/check_me_pro/ble/constant/BTConstant.java b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/check_me_pro/ble/constant/BTConstant.java
new file mode 100644
index 00000000..627df162
--- /dev/null
+++ b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/check_me_pro/ble/constant/BTConstant.java
@@ -0,0 +1,40 @@
+package com.cloud.diplomaticquarterapp.check_me_pro.ble.constant;
+
+public class BTConstant {
+
+ // Bluetooth Command length
+ public final static int WRITE_CONTENT_PKG_DATA_LENGTH = 1024;
+ public final static int READ_CONTENT_ACK_DATA_LENGTH = 1024;
+ public final static int COMMON_PKG_LENGTH = 8;
+ public final static int COMMON_ACK_PKG_LENGTH = 12;
+ public final static int READ_CONTENT_ACK_PKG_FRONT_LENGTH = 8;
+ public final static int GET_INFO_ACK_PKG_LENGTH = 8 + 256;
+
+ public final static byte ACK_CMD_OK = 0;
+ public final static byte ACK_CMD_BAD = 1;
+
+ // Bluetooth Max file name length
+ public final static byte BT_WRITE_FILE_NAME_MAX_LENGTH = 30;
+ public final static byte BT_READ_FILE_NAME_MAX_LENGTH = 30;
+
+ // Bluetooth Command word
+ public final static byte CMD_WORD_START_WRITE = 0x00;
+ public final static byte CMD_WORD_WRITE_CONTENT = 0x01;
+ public final static byte CMD_WORD_END_WRITE = 0x02;
+ public final static byte CMD_WORD_START_READ = 0x03;
+ public final static byte CMD_WORD_READ_CONTENT = 0x04;
+ public final static byte CMD_WORD_END_READ = 0x05;
+ public final static byte CMD_WORD_DEL_FILE = 0x06;
+ public final static byte CMD_WORD_LIST_START = 0x07;
+ public final static byte CMD_WORD_LIST_DATA = 0x08;
+ public final static byte CMD_WORD_LIST_END = 0x09;
+ public final static byte CMD_WORD_LANG_UPDATE_START = 0x0A;
+ public final static byte CMD_WORD_LANG_UPDATE_DATA = 0x0B;
+ public final static byte CMD_WORD_LANG_UPDATE_END = 0x0C;
+ public final static byte CMD_WORD_APP_UPDATE_START = 0x0D;
+ public final static byte CMD_WORD_APP_UPDATE_DATA = 0x0E;
+ public final static byte CMD_WORD_APP_UPDATE_END = 0x0F;
+ public final static byte CMD_WORD_GET_INFO = 0x14;
+ public final static byte CMD_WORD_PING = 0x15;
+ public final static byte CMD_WORD_PARA_SYNC = 0x16;
+}
diff --git a/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/check_me_pro/ble/format/BpInfo.kt b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/check_me_pro/ble/format/BpInfo.kt
new file mode 100644
index 00000000..a8b89cff
--- /dev/null
+++ b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/check_me_pro/ble/format/BpInfo.kt
@@ -0,0 +1,58 @@
+package com.cloud.diplomaticquarterapp.check_me_pro.ble.format
+
+import com.cloud.diplomaticquarterapp.check_me_pro.bean.BpBean
+import com.cloud.diplomaticquarterapp.utils.toUInt
+import java.util.*
+
+
+class BpInfo constructor(var bytes: ByteArray) {
+ var size: Int = bytes.size/11
+ var Bp: ArrayList = arrayListOf()
+
+
+ init {
+
+ var start: Int
+ for (k in 0 until size) {
+ start = k * 11
+ val year: Int = toUInt(setRange(start, 2))
+ val month: Int = toUInt(setRange(start + 2, 1)) - 1
+ val date: Int = toUInt(setRange(start + 3, 1))
+ val hour: Int = toUInt(setRange(start + 4, 1))
+ val minute: Int = toUInt(setRange(start + 5, 1))
+ val second: Int = toUInt(setRange(start + 6, 1))
+ val calendar = Calendar.getInstance()
+ calendar[Calendar.YEAR] = year
+ calendar[Calendar.MONTH] = month
+ calendar[Calendar.DATE] = date
+ calendar[Calendar.HOUR] = hour
+ calendar[Calendar.MINUTE] = minute
+ calendar[Calendar.SECOND] = second
+ Bp.add(BpBean())
+ Bp[k].apply {
+ this.date = calendar.time
+ timeString = String.format(
+ "%04d%02d%02d%02d%02d%02d",
+ year,
+ month + 1,
+ date,
+ hour,
+ minute,
+ second
+ )
+ sys = toUInt(setRange(start + 7, 2))
+ dia = toUInt(setRange(start + 9, 1))
+ pr = toUInt(setRange(start + 10, 1))
+ }
+ }
+
+
+ }
+
+ private fun setRange(start: Int, len: Int): ByteArray {
+ return bytes.copyOfRange(start, start + len)
+ }
+
+
+}
+
diff --git a/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/check_me_pro/ble/format/CheckMeResponse.kt b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/check_me_pro/ble/format/CheckMeResponse.kt
new file mode 100644
index 00000000..5b3af112
--- /dev/null
+++ b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/check_me_pro/ble/format/CheckMeResponse.kt
@@ -0,0 +1,14 @@
+package com.cloud.diplomaticquarterapp.check_me_pro.ble.format
+
+import com.cloud.diplomaticquarterapp.utils.toUInt
+import com.cloud.diplomaticquarterapp.utils.unsigned
+
+
+class CheckMeResponse(var bytes: ByteArray) {
+ var cmd: Int = bytes[1].unsigned()
+ var pkgNo: Int = toUInt(bytes.copyOfRange(3, 5))
+ var len: Int = toUInt(bytes.copyOfRange(5, 7))
+ var content: ByteArray = bytes.copyOfRange(7, 7 + len)
+}
+
+
diff --git a/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/check_me_pro/ble/format/DeviceInfo.kt b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/check_me_pro/ble/format/DeviceInfo.kt
new file mode 100644
index 00000000..229942d1
--- /dev/null
+++ b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/check_me_pro/ble/format/DeviceInfo.kt
@@ -0,0 +1,20 @@
+package com.cloud.diplomaticquarterapp.check_me_pro.ble.format
+
+import android.util.Log
+import com.cloud.diplomaticquarterapp.utils.toUInt
+import org.json.JSONObject
+
+class DeviceInfo(buf: ByteArray) {
+ var len: Int = toUInt(buf.copyOfRange(5, 7))
+ var content: ByteArray = buf.copyOfRange(7, 7 + len)
+ var deviceStr: String = String(content)
+ var json: JSONObject
+
+ init {
+ Log.i("byteSize", buf.decodeToString())
+ Log.i("len::", len.toString())
+ deviceStr = deviceStr.substring(0, deviceStr.indexOf("}") + 1)
+ json = JSONObject(deviceStr)
+
+ }
+}
\ No newline at end of file
diff --git a/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/check_me_pro/ble/format/DlcInfo.kt b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/check_me_pro/ble/format/DlcInfo.kt
new file mode 100644
index 00000000..b0175118
--- /dev/null
+++ b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/check_me_pro/ble/format/DlcInfo.kt
@@ -0,0 +1,69 @@
+package com.cloud.diplomaticquarterapp.check_me_pro.ble.format
+
+import com.cloud.diplomaticquarterapp.check_me_pro.bean.DlcBean
+import com.cloud.diplomaticquarterapp.utils.toUInt
+import java.util.*
+
+
+class DlcInfo(var bytes: ByteArray) {
+ var size: Int = bytes.size / 17
+ var dlc: ArrayList = arrayListOf()
+
+
+ init {
+
+ var start: Int
+ for (k in 0 until size) {
+ start = k * 17
+ val year: Int = toUInt(setRange(start, 2))
+ val month: Int = toUInt(setRange(start + 2, 1))
+ val date: Int = toUInt(setRange(start + 3, 1))
+ val hour: Int = toUInt(setRange(start + 4, 1))
+ val minute: Int = toUInt(setRange(start + 5, 1))
+ val second: Int = toUInt(setRange(start + 6, 1))
+ val calendar = Calendar.getInstance()
+ calendar[Calendar.YEAR] = year
+ calendar[Calendar.MONTH] = month - 1
+ calendar[Calendar.DATE] = date
+ calendar[Calendar.HOUR] = hour
+ calendar[Calendar.MINUTE] = minute
+ calendar[Calendar.SECOND] = second
+ dlc.add(DlcBean())
+ dlc[k].apply {
+ this.date = calendar.time
+ timeString = String.format(
+ "%04d%02d%02d%02d%02d%02d",
+ year,
+ month,
+ date,
+ hour,
+ minute,
+ second
+ )
+ hr = toUInt(setRange(start + 7, 2))
+ eface = toUInt(setRange(start + 9, 1))
+ if (eface > 2) eface = 2
+ oxy = toUInt(setRange(start + 10, 1))
+ pi = toUInt(setRange(start + 11, 1))
+ oface = toUInt(setRange(start + 12, 1))
+ if (oface > 2) oface = 2
+ prFlag = toUInt(setRange(start + 13, 1))
+ pr = toUInt(setRange(start + 14, 1))
+ bpiFace = toUInt(setRange(start + 15, 1))
+ if (bpiFace > 2) bpiFace = 2
+ voice = toUInt(setRange(start + 16, 1))
+ }
+
+
+ }
+
+
+ }
+
+ private fun setRange(start: Int, len: Int): ByteArray {
+ return bytes.copyOfRange(start, start + len)
+ }
+
+
+}
+
diff --git a/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/check_me_pro/ble/format/EcgInfo.kt b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/check_me_pro/ble/format/EcgInfo.kt
new file mode 100644
index 00000000..1aa8332e
--- /dev/null
+++ b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/check_me_pro/ble/format/EcgInfo.kt
@@ -0,0 +1,60 @@
+package com.cloud.diplomaticquarterapp.check_me_pro.ble.format
+
+import com.cloud.diplomaticquarterapp.check_me_pro.bean.EcgBean
+import com.cloud.diplomaticquarterapp.utils.toUInt
+import java.util.*
+
+
+class EcgInfo(var bytes: ByteArray) {
+ var size: Int = bytes.size / 10
+ var ecg: ArrayList = arrayListOf()
+
+
+ init {
+ var start: Int
+ for (k in 0 until size) {
+ start = k * 10
+ val year: Int = toUInt(setRange(start, 2))
+ val month: Int = toUInt(setRange(start + 2, 1)) - 1
+ val date: Int = toUInt(setRange(start + 3, 1))
+ val hour: Int = toUInt(setRange(start + 4, 1))
+ val minute: Int = toUInt(setRange(start + 5, 1))
+ val second: Int = toUInt(setRange(start + 6, 1))
+ val calendar = Calendar.getInstance()
+ calendar[Calendar.YEAR] = year
+ calendar[Calendar.MONTH] = month
+ calendar[Calendar.DATE] = date
+ calendar[Calendar.HOUR] = hour
+ calendar[Calendar.MINUTE] = minute
+ calendar[Calendar.SECOND] = second
+ ecg.add(EcgBean())
+ ecg[k].apply {
+ this.date = calendar.time
+ timeString = String.format(
+ "%04d%02d%02d%02d%02d%02d",
+ year,
+ month + 1,
+ date,
+ hour,
+ minute,
+ second
+ )
+ way = toUInt(setRange(start + 7, 1))
+ face = toUInt(setRange(start + 8, 1))
+ if (face > 2) face = 2
+ voice = toUInt(setRange(start + 9, 1))
+ }
+
+
+ }
+
+
+ }
+
+ private fun setRange(start: Int, len: Int): ByteArray {
+ return bytes.copyOfRange(start, start + len)
+ }
+
+
+}
+
diff --git a/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/check_me_pro/ble/format/EcgWaveInfo.kt b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/check_me_pro/ble/format/EcgWaveInfo.kt
new file mode 100644
index 00000000..bfacff86
--- /dev/null
+++ b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/check_me_pro/ble/format/EcgWaveInfo.kt
@@ -0,0 +1,64 @@
+package com.cloud.diplomaticquarterapp.check_me_pro.ble.format
+
+import com.cloud.diplomaticquarterapp.utils.toUInt
+
+
+class EcgWaveInfo constructor(var bytes: ByteArray) {
+
+ var hrSize: Int = toUInt(bytes.copyOfRange(0, 2))
+ var waveSize: Int = toUInt(bytes.copyOfRange(2, 6)) - 4
+ var hr: Int = toUInt(bytes.copyOfRange(6, 8))
+ var st: Int = toUInt(bytes.copyOfRange(8, 10))
+ var qrs: Int = toUInt(bytes.copyOfRange(10, 12))
+ var pvcs: Int = toUInt(bytes.copyOfRange(12, 14))
+ var qtc: Int = toUInt(bytes.copyOfRange(14, 16))
+ var result: Int = toUInt(bytes.copyOfRange(16, 17))
+ var qt: Int = toUInt(bytes.copyOfRange(19, 21))
+ var hrList: IntArray = IntArray(hrSize / 2)
+ var waveList: IntArray = IntArray(waveSize / 2)
+ var waveIntSize = waveSize / 2
+ val total = 2500
+ var waveViewSize = waveIntSize / total
+
+
+ init {
+
+ for (index in 0 until hrSize / 2) {
+ hrList[index] = toUInt(setRange(index * 2 + 22, 2))
+ }
+// for (index in 0 until waveSize / 2) {
+
+// waveList[index] =
+// bytes[23 + index * 2 + hrSize].toInt() * 256 + bytes[22 + index * 2 + hrSize].toInt()
+// }
+
+
+// waveList= ECGInnerItem(bytes).ecgData
+ waveIntSize=waveList.size
+ waveViewSize=waveIntSize/total
+ if (waveViewSize * total < waveIntSize) {
+ waveViewSize++
+ }
+ }
+
+ private fun setRange(start: Int, len: Int): ByteArray {
+ return bytes.copyOfRange(start, start + len)
+ }
+
+
+ fun getWave(index: Int): IntArray {
+ val result = IntArray(total)
+ for (k in 0 until total) {
+ result[k] = if (k + index * total < waveList.size) {
+ waveList[k + index * total]
+ } else {
+ 1000000
+ }
+
+ }
+ return result
+
+ }
+
+
+}
\ No newline at end of file
diff --git a/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/check_me_pro/ble/format/GluInfo.kt b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/check_me_pro/ble/format/GluInfo.kt
new file mode 100644
index 00000000..35b6ffd6
--- /dev/null
+++ b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/check_me_pro/ble/format/GluInfo.kt
@@ -0,0 +1,57 @@
+package com.cloud.diplomaticquarterapp.check_me_pro.ble.format
+
+import com.cloud.diplomaticquarterapp.check_me_pro.bean.GluBean
+import com.cloud.diplomaticquarterapp.utils.toUInt
+import java.util.*
+
+
+class GluInfo constructor(var bytes: ByteArray) {
+ var size: Int = bytes.size / 32
+ var Glu: ArrayList = arrayListOf()
+
+
+ init {
+
+ var start: Int
+ for (k in 0 until size) {
+ start = k * 32
+ val year: Int = toUInt(setRange(start, 2))
+ val month: Int = toUInt(setRange(start + 2, 1)) - 1
+ val date: Int = toUInt(setRange(start + 3, 1))
+ val hour: Int = toUInt(setRange(start + 4, 1))
+ val minute: Int = toUInt(setRange(start + 5, 1))
+ val second: Int = toUInt(setRange(start + 6, 1))
+ val calendar = Calendar.getInstance()
+ calendar[Calendar.YEAR] = year
+ calendar[Calendar.MONTH] = month
+ calendar[Calendar.DATE] = date
+ calendar[Calendar.HOUR] = hour
+ calendar[Calendar.MINUTE] = minute
+ calendar[Calendar.SECOND] = second
+ Glu.add(GluBean())
+ Glu[k].apply {
+ this.date = calendar.time
+ timeString = String.format(
+ "%04d%02d%02d%02d%02d%02d",
+ year,
+ month + 1,
+ date,
+ hour,
+ minute,
+ second
+ )
+ glu= toUInt(setRange(start+7,2)).toFloat()/10f
+ note=String(setRange(start+12,20))
+ }
+ }
+
+
+ }
+
+ private fun setRange(start: Int, len: Int): ByteArray {
+ return bytes.copyOfRange(start, start + len)
+ }
+
+
+}
+
diff --git a/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/check_me_pro/ble/format/OxyInfo.kt b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/check_me_pro/ble/format/OxyInfo.kt
new file mode 100644
index 00000000..ab5d8643
--- /dev/null
+++ b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/check_me_pro/ble/format/OxyInfo.kt
@@ -0,0 +1,56 @@
+package com.cloud.diplomaticquarterapp.check_me_pro.ble.format
+
+import com.cloud.diplomaticquarterapp.check_me_pro.bean.OxyBean
+import com.cloud.diplomaticquarterapp.utils.toUInt
+import java.util.*
+
+
+class OxyInfo constructor(var bytes: ByteArray) {
+ var size: Int = bytes.size / 12
+ var Oxy: ArrayList = arrayListOf()
+
+ init {
+ var start: Int
+ for (k in 0 until size) {
+ start = k * 12
+ val year: Int = toUInt(setRange(start, 2))
+ val month: Int = toUInt(setRange(start + 2, 1)) - 1
+ val date: Int = toUInt(setRange(start + 3, 1))
+ val hour: Int = toUInt(setRange(start + 4, 1))
+ val minute: Int = toUInt(setRange(start + 5, 1))
+ val second: Int = toUInt(setRange(start + 6, 1))
+ val calendar = Calendar.getInstance()
+ calendar[Calendar.YEAR] = year
+ calendar[Calendar.MONTH] = month
+ calendar[Calendar.DATE] = date
+ calendar[Calendar.HOUR] = hour
+ calendar[Calendar.MINUTE] = minute
+ calendar[Calendar.SECOND] = second
+ Oxy.add(OxyBean())
+ Oxy[k].apply {
+ this.date = calendar.time
+ timeString = String.format(
+ "%04d%02d%02d%02d%02d%02d",
+ year,
+ month + 1,
+ date,
+ hour,
+ minute,
+ second
+ )
+ way = toUInt(setRange(start + 7, 1))
+ if (way > 2) way = 2
+ oxy = toUInt(setRange(start + 8, 1))
+ pr = toUInt(setRange(start + 9, 1))
+ pi = toUInt(setRange(start + 10, 1))
+ face = toUInt(setRange(start + 11, 1))
+ if (face > 2) face = 2
+ }
+ }
+ }
+
+ private fun setRange(start: Int, len: Int): ByteArray {
+ return bytes.copyOfRange(start, start + len)
+ }
+}
+
diff --git a/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/check_me_pro/ble/format/OxyWaveInfo.kt b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/check_me_pro/ble/format/OxyWaveInfo.kt
new file mode 100644
index 00000000..7c3b6f55
--- /dev/null
+++ b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/check_me_pro/ble/format/OxyWaveInfo.kt
@@ -0,0 +1,28 @@
+package com.cloud.diplomaticquarterapp.check_me_pro.ble.format
+
+import com.cloud.diplomaticquarterapp.utils.toUInt
+import com.cloud.diplomaticquarterapp.utils.unsigned
+
+
+class OxyWaveInfo constructor(var bytes: ByteArray) {
+ val size1: Int = toUInt(bytes.copyOfRange(0, 2))
+ val size2: Int = toUInt(bytes.copyOfRange(2, 6))
+ val o2Array = IntArray(size1 / 3)
+ val pulseArray = IntArray(size1 / 3)
+ val waveArray = IntArray(size2)
+
+
+ init {
+ for (k in 0 until size1 / 3) {
+ o2Array[k] = bytes[k * 3 + 6].unsigned()
+ pulseArray[k] =
+ bytes[k * 3 + 7].unsigned() + bytes[k * 3 + 8].unsigned() * 256
+ }
+ for (k in 0 until size2) {
+ waveArray[k] = bytes[k + 6 + size1].unsigned()
+ }
+ }
+
+
+}
+
diff --git a/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/check_me_pro/ble/format/PedInfo.kt b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/check_me_pro/ble/format/PedInfo.kt
new file mode 100644
index 00000000..58f74688
--- /dev/null
+++ b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/check_me_pro/ble/format/PedInfo.kt
@@ -0,0 +1,63 @@
+package com.cloud.diplomaticquarterapp.check_me_pro.ble.format
+
+import android.util.Log
+import com.cloud.diplomaticquarterapp.check_me_pro.bean.PedBean
+import com.cloud.diplomaticquarterapp.utils.toUInt
+import java.util.*
+
+
+class PedInfo constructor(var bytes: ByteArray) {
+ var size: Int = bytes.size / 29
+ var Ped: ArrayList = arrayListOf()
+
+
+ init {
+ Log.i("pedoSize",bytes.size.toString())
+
+ var start: Int
+ for (k in 0 until size) {
+ start = k * 29
+ val year: Int = toUInt(setRange(start, 2))
+ val month: Int = toUInt(setRange(start + 2, 1)) - 1
+ val date: Int = toUInt(setRange(start + 3, 1))
+ val hour: Int = toUInt(setRange(start + 4, 1))
+ val minute: Int = toUInt(setRange(start + 5, 1))
+ val second: Int = toUInt(setRange(start + 6, 1))
+ val calendar = Calendar.getInstance()
+ calendar[Calendar.YEAR] = year
+ calendar[Calendar.MONTH] = month
+ calendar[Calendar.DATE] = date
+ calendar[Calendar.HOUR] = hour
+ calendar[Calendar.MINUTE] = minute
+ calendar[Calendar.SECOND] = second
+ Ped.add(PedBean())
+ Ped[k].apply {
+ this.date = calendar.time
+ timeString = String.format(
+ "%04d%02d%02d%02d%02d%02d",
+ year,
+ month + 1,
+ date,
+ hour,
+ minute,
+ second
+ )
+ step = toUInt(setRange(start + 7, 4))
+ dis = toUInt(setRange(start + 11, 4)).toFloat() / 100f
+ speed = toUInt(setRange(start + 15, 4)).toFloat() / 10f
+ cal = toUInt(setRange(start + 19, 4)).toFloat() / 100f
+ fat = toUInt(setRange(start + 23, 2)).toFloat() / 100f
+ time = toUInt(setRange(start + 25, 2))
+ }
+ }
+
+
+ }
+
+ private fun setRange(start: Int, len: Int): ByteArray {
+ return bytes.copyOfRange(start, start + len)
+ }
+
+
+}
+
diff --git a/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/check_me_pro/ble/format/SlpInfo.kt b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/check_me_pro/ble/format/SlpInfo.kt
new file mode 100644
index 00000000..e3f396e5
--- /dev/null
+++ b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/check_me_pro/ble/format/SlpInfo.kt
@@ -0,0 +1,71 @@
+package com.cloud.diplomaticquarterapp.check_me_pro.ble.format
+
+import android.util.Log
+import com.cloud.diplomaticquarterapp.check_me_pro.bean.SlpBean
+import com.cloud.diplomaticquarterapp.utils.toUInt
+import java.util.*
+
+
+class SlpInfo constructor(var bytes: ByteArray) {
+ var size: Int = bytes.size / 18
+ var Slp: ArrayList = arrayListOf()
+ fun byteArray2String(byteArray: ByteArray): String {
+ var fuc = ""
+ for (b in byteArray) {
+ val st = String.format("%02X", b)
+ fuc += ("$st ");
+ }
+ return fuc
+ }
+
+ init {
+
+ Log.e("sleepSIze",byteArray2String(bytes))
+ var start: Int
+ for (k in 0 until size) {
+ start = k *18
+ val year: Int = toUInt(setRange(start, 2))
+ val month: Int = toUInt(setRange(start + 2, 1)) - 1
+ val date: Int = toUInt(setRange(start + 3, 1))
+ val hour: Int = toUInt(setRange(start + 4, 1))
+ val minute: Int = toUInt(setRange(start + 5, 1))
+ val second: Int = toUInt(setRange(start + 6, 1))
+ val calendar = Calendar.getInstance()
+ calendar[Calendar.YEAR] = year
+ calendar[Calendar.MONTH] = month
+ calendar[Calendar.DATE] = date
+ calendar[Calendar.HOUR] = hour
+ calendar[Calendar.MINUTE] = minute
+ calendar[Calendar.SECOND] = second
+ Slp.add(SlpBean())
+ Slp[k].apply {
+ this.date = calendar.time
+ timeString = String.format(
+ "%04d%02d%02d%02d%02d%02d",
+ year,
+ month + 1,
+ date,
+ hour,
+ minute,
+ second
+ )
+ time = toUInt(setRange(start + 7, 4))
+ lowTime = toUInt(setRange(start + 11, 2))
+ lowCount = toUInt(setRange(start + 13, 2))
+ minO2 = toUInt(setRange(start + 15, 1))
+ meanO2 = toUInt(setRange(start + 16, 1))
+ face = toUInt(setRange(start + 17, 1))
+ if (face > 2) face = 2
+ }
+ }
+
+
+ }
+
+ private fun setRange(start: Int, len: Int): ByteArray {
+ return bytes.copyOfRange(start, start + len)
+ }
+
+
+}
+
diff --git a/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/check_me_pro/ble/format/TmpInfo.kt b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/check_me_pro/ble/format/TmpInfo.kt
new file mode 100644
index 00000000..2f9f23d6
--- /dev/null
+++ b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/check_me_pro/ble/format/TmpInfo.kt
@@ -0,0 +1,59 @@
+
+import com.cloud.diplomaticquarterapp.check_me_pro.bean.TmpBean
+import com.cloud.diplomaticquarterapp.utils.toUInt
+import java.util.*
+
+
+class TmpInfo constructor(var bytes: ByteArray) {
+ var size: Int = bytes.size / 11
+ var Tmp: ArrayList = arrayListOf()
+
+
+ init {
+
+ var start: Int
+ for (k in 0 until size) {
+ start = k * 11
+ val year: Int = toUInt(setRange(start, 2))
+ val month: Int = toUInt(setRange(start + 2, 1)) - 1
+ val date: Int = toUInt(setRange(start + 3, 1))
+ val hour: Int = toUInt(setRange(start + 4, 1))
+ val minute: Int = toUInt(setRange(start + 5, 1))
+ val second: Int = toUInt(setRange(start + 6, 1))
+ val calendar = Calendar.getInstance()
+ calendar[Calendar.YEAR] = year
+ calendar[Calendar.MONTH] = month
+ calendar[Calendar.DATE] = date
+ calendar[Calendar.HOUR] = hour
+ calendar[Calendar.MINUTE] = minute
+ calendar[Calendar.SECOND] = second
+ Tmp.add(TmpBean())
+ Tmp[k].apply {
+ this.date = calendar.time
+ timeString = String.format(
+ "%04d%02d%02d%02d%02d%02d",
+ year,
+ month + 1,
+ date,
+ hour,
+ minute,
+ second
+ )
+ way = toUInt(setRange(start + 7, 1))
+ if (way > 2) way = 2
+ tmp = toUInt(setRange(start + 8, 2)).toFloat() / 10f
+ face = toUInt(setRange(start + 10, 1))
+ if (face > 2) face = 2
+ }
+ }
+
+
+ }
+
+ private fun setRange(start: Int, len: Int): ByteArray {
+ return bytes.copyOfRange(start, start + len)
+ }
+
+
+}
+
diff --git a/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/check_me_pro/ble/format/UserInfo.kt b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/check_me_pro/ble/format/UserInfo.kt
new file mode 100644
index 00000000..ddc2ed85
--- /dev/null
+++ b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/check_me_pro/ble/format/UserInfo.kt
@@ -0,0 +1,46 @@
+package com.cloud.diplomaticquarterapp.check_me_pro.ble.format
+
+import com.cloud.diplomaticquarterapp.check_me_pro.bean.UserBean
+import com.cloud.diplomaticquarterapp.utils.toUInt
+import com.cloud.diplomaticquarterapp.utils.unsigned
+import java.util.*
+
+
+class UserInfo constructor(var bytes: ByteArray) {
+ var size: Int = bytes.size / 27
+ var user: Array = Array(size) {
+ UserBean()
+ }
+
+ init {
+
+ var start: Int
+ for (k in 0 until size) {
+ start = k * 27
+ user[k].id = bytes[start].unsigned().toString()
+ user[k].name = String(setRange(start + 1, 16))
+ user[k].ico = bytes[start + 17].unsigned()
+ user[k].sex = bytes[start + 18].unsigned()
+ val year: Int = toUInt(setRange(start + 19, 2))
+ val month: Int = toUInt(setRange(start + 21, 1)) - 1
+ val date: Int = toUInt(setRange(start + 22, 1))
+ val calendar = Calendar.getInstance()
+ calendar[Calendar.YEAR] = year
+ calendar[Calendar.MONTH] = month
+ calendar[Calendar.DATE] = date
+ user[k].birthday = calendar.time
+ user[k].weight = toUInt(setRange(start + 23, 2)) /10
+ user[k].height = toUInt(setRange(start + 25, 2))
+
+ }
+
+
+ }
+
+ private fun setRange(start: Int, len: Int): ByteArray {
+ return bytes.copyOfRange(start, start + len)
+ }
+
+
+}
+
diff --git a/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/check_me_pro/ble/manager/BleDataManager.java b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/check_me_pro/ble/manager/BleDataManager.java
new file mode 100644
index 00000000..d0122096
--- /dev/null
+++ b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/check_me_pro/ble/manager/BleDataManager.java
@@ -0,0 +1,111 @@
+package com.cloud.diplomaticquarterapp.check_me_pro.ble.manager;
+
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothGatt;
+import android.bluetooth.BluetoothGattCharacteristic;
+import android.bluetooth.BluetoothGattService;
+import android.content.Context;
+import android.util.Log;
+
+import java.util.Arrays;
+import java.util.UUID;
+
+import no.nordicsemi.android.ble.BleManager;
+import no.nordicsemi.android.ble.data.Data;
+
+public class BleDataManager extends BleManager {
+ private BluetoothGattCharacteristic write_char;
+ private BluetoothGattCharacteristic notify_char;
+ private OnNotifyListener listener;
+
+ public BleDataManager(Context context) {
+ super(context);
+ }
+
+ public void setNotifyListener(OnNotifyListener listener) {
+ this.listener = listener;
+ }
+
+ @Override
+ protected BleManagerGattCallback getGattCallback() {
+ return new MyManagerGattCallback();
+ }
+
+ public void sendCmd(byte[] bytes) {
+// Log.i("----- Current Write Command -----", "");
+ writeCharacteristic(write_char, bytes)
+ .split()
+ .done(device -> {
+ })
+ .enqueue();
+ }
+
+
+ public interface OnNotifyListener {
+ void onNotify(BluetoothDevice device, Data data);
+ }
+
+ private class MyManagerGattCallback extends BleManagerGattCallback {
+ @Override
+ public boolean isRequiredServiceSupported(BluetoothGatt gatt) {
+ final BluetoothGattService service = gatt.getService(service_uuid);
+
+
+ if (service != null) {
+ write_char = service.getCharacteristic(write_uuid);
+ notify_char = service.getCharacteristic(notify_uuid);
+ }
+
+ boolean notify = false;
+ if (notify_char != null) {
+ final int properties = notify_char.getProperties();
+ notify = (properties & BluetoothGattCharacteristic.PROPERTY_NOTIFY) != 0;
+ }
+ boolean writeRequest = false;
+ if (write_char != null) {
+ final int properties = write_char.getProperties();
+ int writeType = BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT;
+ if ((properties & BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE) != 0) {
+ writeType = BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE;
+ }
+ write_char.setWriteType(writeType);
+ writeRequest = (properties & BluetoothGattCharacteristic.PROPERTY_WRITE) != 0 || (properties & BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE) != 0;
+ /// LepuBleLog.d(TAG, "writeChar writeRequest == " + writeRequest);
+
+ }
+ // Return true if all required services have been found
+ return write_char != null && notify_char != null
+ && notify && writeRequest;
+ }
+
+ @Override
+ public boolean isOptionalServiceSupported(BluetoothGatt gatt) {
+ return super.isOptionalServiceSupported(gatt);
+ }
+
+ @Override
+ protected void initialize() {
+ beginAtomicRequestQueue()
+ .add(requestMtu(23) // Remember, GATT needs 3 bytes extra. This will allow packet size of 244 bytes.
+ .with((device, mtu) -> Log.d("TAG", "MTU set to " + mtu))
+ .fail((device, status) -> log(Log.WARN, "Requested MTU not supported: " + status)))
+ .add(enableNotifications(notify_char))
+ .done(device -> Log.d("TAG", "Target initialized"))
+ .enqueue();
+ setNotificationCallback(notify_char)
+ .with((device, data) -> {
+ listener.onNotify(device, data);
+ });
+ }
+
+ @Override
+ public void onDeviceDisconnected() {
+ write_char = null;
+ notify_char = null;
+ }
+ }
+
+ public static final UUID service_uuid = UUID.fromString("14839ac4-7d7e-415c-9a42-167340cf2339");
+ public static final UUID write_uuid = UUID.fromString("8B00ACE7-EB0B-49B0-BBE9-9AEE0A26E1A3");
+ public static final UUID notify_uuid = UUID.fromString("0734594A-A8E7-4B1A-A6B1-CD5243059A57");
+}
\ No newline at end of file
diff --git a/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/check_me_pro/ble/manager/BleScanManager.kt b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/check_me_pro/ble/manager/BleScanManager.kt
new file mode 100644
index 00000000..2c41034e
--- /dev/null
+++ b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/check_me_pro/ble/manager/BleScanManager.kt
@@ -0,0 +1,64 @@
+package com.cloud.diplomaticquarterapp.check_me_pro.ble.manager
+
+import android.annotation.SuppressLint
+import android.bluetooth.BluetoothAdapter
+import android.bluetooth.BluetoothDevice
+import android.bluetooth.BluetoothManager
+import android.bluetooth.le.BluetoothLeScanner
+import android.bluetooth.le.ScanCallback
+import android.bluetooth.le.ScanResult
+import android.bluetooth.le.ScanSettings
+import android.content.Context
+import android.os.Build
+import android.util.Log
+import androidx.annotation.RequiresApi
+import com.cloud.diplomaticquarterapp.utils.Constant
+
+
+class BleScanManager {
+ interface Scan {
+ fun scanReturn(name: String, bluetoothDevice: BluetoothDevice)
+ }
+
+ private var bluetoothAdapter: BluetoothAdapter? = null
+ private lateinit var leScanner: BluetoothLeScanner
+ private var scan: Scan? = null
+ private val leScanCallback: ScanCallback = object : ScanCallback() {
+ @SuppressLint("MissingPermission")
+ override fun onScanResult(
+ callbackType: Int, result: ScanResult
+ ) {
+ super.onScanResult(callbackType, result)
+ val device = result.device
+ if (device?.name == null) return;
+ scan?.apply {
+ scanReturn(device.name, device)
+ }
+ Log.i("scanned ble", " ${device.name}")
+ }
+
+ override fun onBatchScanResults(results: List) {}
+ override fun onScanFailed(errorCode: Int) {}
+ }
+
+ fun setCallBack(scan: Scan) {
+ this.scan = scan
+ }
+
+ @SuppressLint("MissingPermission")
+ @RequiresApi(Build.VERSION_CODES.M)
+ fun initScan(context: Context) {
+ context.apply {
+ val settings: ScanSettings = ScanSettings.Builder().setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY).setCallbackType(ScanSettings.CALLBACK_TYPE_ALL_MATCHES).build()
+ val bluetoothManager = getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager
+ bluetoothAdapter = bluetoothManager.adapter
+ leScanner = bluetoothAdapter!!.bluetoothLeScanner
+ leScanner.startScan(null, settings, leScanCallback)
+ }
+ }
+
+ @SuppressLint("MissingPermission")
+ fun stopScan() {
+ leScanner.stopScan(leScanCallback)
+ }
+}
\ No newline at end of file
diff --git a/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/check_me_pro/ble/pkg/EndReadPkg.kt b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/check_me_pro/ble/pkg/EndReadPkg.kt
new file mode 100644
index 00000000..ce4a0a91
--- /dev/null
+++ b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/check_me_pro/ble/pkg/EndReadPkg.kt
@@ -0,0 +1,21 @@
+package com.cloud.diplomaticquarterapp.check_me_pro.ble.pkg
+
+import com.cloud.diplomaticquarterapp.check_me_pro.ble.constant.BTConstant
+import com.cloud.diplomaticquarterapp.utils.CRCUtils
+import kotlin.experimental.inv
+
+class EndReadPkg {
+ var buf: ByteArray = ByteArray(BTConstant.COMMON_PKG_LENGTH)
+
+ init {
+ // TODO Auto-generated constructor stub
+ buf[0] = 0xAA.toByte()
+ buf[1] = BTConstant.CMD_WORD_END_READ
+ buf[2] = BTConstant.CMD_WORD_END_READ.inv()
+ buf[3] = 0 //Package number, the default is 0
+ buf[4] = 0
+ buf[5] = 0 //data chunk size, the default is 0
+ buf[6] = 0
+ buf[buf.size - 1] = CRCUtils.calCRC8(buf)
+ }
+}
\ No newline at end of file
diff --git a/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/check_me_pro/ble/pkg/GetDeviceInfoPkg.kt b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/check_me_pro/ble/pkg/GetDeviceInfoPkg.kt
new file mode 100644
index 00000000..e5e7c720
--- /dev/null
+++ b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/check_me_pro/ble/pkg/GetDeviceInfoPkg.kt
@@ -0,0 +1,21 @@
+package com.cloud.diplomaticquarterapp.check_me_pro.ble.pkg
+
+import com.cloud.diplomaticquarterapp.check_me_pro.ble.constant.BTConstant
+import com.cloud.diplomaticquarterapp.utils.CRCUtils
+import kotlin.experimental.inv
+
+
+class GetDeviceInfoPkg() {
+ val buf: ByteArray = ByteArray(BTConstant.COMMON_PKG_LENGTH)
+
+ init {
+ buf[0] = 0xAA.toByte()
+ buf[1] = BTConstant.CMD_WORD_GET_INFO
+ buf[2] = BTConstant.CMD_WORD_GET_INFO.inv()
+ buf[3] = 0.toByte() //Package number
+ buf[4] = 0.toByte()
+ buf[5] = 0.toByte()
+ buf[6] = 0.toByte()
+ buf[buf.size - 1] = CRCUtils.calCRC8(buf)
+ }
+}
\ No newline at end of file
diff --git a/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/check_me_pro/ble/pkg/ReadContentPkg.kt b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/check_me_pro/ble/pkg/ReadContentPkg.kt
new file mode 100644
index 00000000..57ec69c4
--- /dev/null
+++ b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/check_me_pro/ble/pkg/ReadContentPkg.kt
@@ -0,0 +1,19 @@
+package com.cloud.diplomaticquarterapp.check_me_pro.ble.pkg
+import com.cloud.diplomaticquarterapp.check_me_pro.ble.constant.BTConstant
+import com.cloud.diplomaticquarterapp.utils.CRCUtils
+import kotlin.experimental.inv
+
+class ReadContentPkg(pkgNum: Int) {
+ val buf: ByteArray = ByteArray(BTConstant.COMMON_PKG_LENGTH)
+
+ init {
+ buf[0] = 0xAA.toByte()
+ buf[1] = BTConstant.CMD_WORD_READ_CONTENT
+ buf[2] = BTConstant.CMD_WORD_READ_CONTENT.inv()
+ buf[3] = pkgNum.toByte() //Package number
+ buf[4] = (pkgNum shr 8).toByte()
+ buf[5] = 0 //data chunk size, the default is 0
+ buf[6] = 0
+ buf[buf.size - 1] = CRCUtils.calCRC8(buf)
+ }
+}
\ No newline at end of file
diff --git a/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/check_me_pro/ble/pkg/StartReadPkg.kt b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/check_me_pro/ble/pkg/StartReadPkg.kt
new file mode 100644
index 00000000..e71003aa
--- /dev/null
+++ b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/check_me_pro/ble/pkg/StartReadPkg.kt
@@ -0,0 +1,23 @@
+package com.cloud.diplomaticquarterapp.check_me_pro.ble.pkg
+import com.cloud.diplomaticquarterapp.check_me_pro.ble.constant.BTConstant
+import com.cloud.diplomaticquarterapp.utils.CRCUtils
+import kotlin.experimental.inv
+
+class StartReadPkg(fileName: String?) {
+ val buf: ByteArray = ByteArray(BTConstant.COMMON_PKG_LENGTH + fileName!!.length + 1)
+
+ init {
+ buf[0] = (0xAA).toByte()
+ buf[1] = BTConstant.CMD_WORD_START_READ
+ buf[2] = BTConstant.CMD_WORD_START_READ.inv()
+ buf[3] = 0 //Package number, the default is 0
+ buf[4] = 0
+ buf[5] = (buf.size - BTConstant.COMMON_PKG_LENGTH).toByte() //data chunk size
+ buf[6] = (buf.size - BTConstant.COMMON_PKG_LENGTH shr 8).toByte()
+ val tempFileName = fileName!!.toCharArray()
+ for (i in tempFileName.indices) {
+ buf[i + 7] = tempFileName[i].toByte()
+ }
+ buf[buf.size - 1] = CRCUtils.calCRC8(buf)
+ }
+}
\ No newline at end of file
diff --git a/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/check_me_pro/ble/worker/BleDataWorker.kt b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/check_me_pro/ble/worker/BleDataWorker.kt
new file mode 100644
index 00000000..ba9fab1b
--- /dev/null
+++ b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/check_me_pro/ble/worker/BleDataWorker.kt
@@ -0,0 +1,224 @@
+package com.cloud.diplomaticquarterapp.check_me_pro.ble.worker
+
+import android.bluetooth.BluetoothDevice
+import android.content.Context
+import android.os.Environment
+import android.util.Log
+import com.cloud.diplomaticquarterapp.check_me_pro.ble.format.CheckMeResponse
+import com.cloud.diplomaticquarterapp.check_me_pro.ble.format.DeviceInfo
+import com.cloud.diplomaticquarterapp.check_me_pro.ble.format.UserInfo
+import com.cloud.diplomaticquarterapp.check_me_pro.ble.manager.BleDataManager
+import com.cloud.diplomaticquarterapp.check_me_pro.ble.pkg.EndReadPkg
+import com.cloud.diplomaticquarterapp.check_me_pro.ble.pkg.GetDeviceInfoPkg
+import com.cloud.diplomaticquarterapp.check_me_pro.ble.pkg.ReadContentPkg
+import com.cloud.diplomaticquarterapp.check_me_pro.ble.pkg.StartReadPkg
+import com.cloud.diplomaticquarterapp.utils.CRCUtils
+import com.cloud.diplomaticquarterapp.utils.Constant
+import com.cloud.diplomaticquarterapp.utils.add
+import com.cloud.diplomaticquarterapp.utils.toUInt
+import com.google.gson.Gson
+import io.flutter.plugin.common.EventChannel
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.channels.Channel
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.sync.Mutex
+import kotlinx.coroutines.sync.withLock
+import no.nordicsemi.android.ble.data.Data
+import java.io.File
+import kotlin.experimental.inv
+
+class BleDataWorker(private val updateEventSink: (Map) -> Unit) {
+ private var pool: ByteArray? = null
+ private val deviceChannel = Channel(Channel.CONFLATED)
+ private val fileChannel = Channel(Channel.CONFLATED)
+ private val connectChannel = Channel(Channel.CONFLATED)
+ private var myBleDataManager: BleDataManager? = null
+ private val dataScope = CoroutineScope(Dispatchers.IO)
+ private val mutex = Mutex()
+
+ private var cmdState = 0;
+ var pkgTotal = 0;
+ var currentPkg = 0;
+ var fileData: ByteArray? = null
+ var currentFileName = ""
+ var result = 1;
+ var currentFileSize = 0
+
+
+ companion object {
+ val fileProgressChannel = Channel(Channel.CONFLATED)
+ }
+
+ data class FileProgress(
+ var name: String = "", var progress: Int = 0, var success: Boolean = false
+ )
+
+ private val comeData = object : BleDataManager.OnNotifyListener {
+ override fun onNotify(device: BluetoothDevice?, data: Data?) {
+ data?.value?.apply {
+ pool = add(pool, this)
+ }
+ pool?.apply {
+ pool = handleDataPool(pool)
+ }
+ }
+
+ }
+
+ val gson = Gson()
+
+
+ private fun handleDataPool(bytes: ByteArray?): ByteArray? {
+ val bytesLeft: ByteArray? = bytes
+
+ if (bytes == null || bytes.size < 8) {
+ return bytes
+ }
+ loop@ for (i in 0 until bytes.size - 7) {
+ if (bytes[i] != 0x55.toByte() || bytes[i + 1] != bytes[i + 2].inv()) {
+ continue@loop
+ }
+
+ // need content length
+ val len = toUInt(bytes.copyOfRange(i + 5, i + 7))
+ if (i + 8 + len > bytes.size) {
+ continue@loop
+ }
+
+ val temp: ByteArray = bytes.copyOfRange(i, i + 8 + len)
+ if (temp.last() == CRCUtils.calCRC8(temp)) {
+
+ if (cmdState in 1..3) {
+ val bleResponse = CheckMeResponse(temp)
+ if (cmdState == 1) {
+ currentFileSize = toUInt(bleResponse.content)
+ pkgTotal = currentFileSize / 512
+ if (bleResponse.cmd == 1) {
+ result = 1
+ val pkg = EndReadPkg()
+ sendCmd(pkg.buf)
+ cmdState = 3
+ } else if (bleResponse.cmd == 0) {
+ val pkg = ReadContentPkg(currentPkg)
+ sendCmd(pkg.buf)
+ currentPkg++
+ cmdState = 2;
+ }
+
+
+ } else if (cmdState == 2) {
+ bleResponse.content.apply {
+ fileData = add(fileData, this)
+ fileData?.let {
+ dataScope.launch {
+ fileProgressChannel.send(
+ FileProgress(
+ currentFileName, it.size * 100 / currentFileSize, true
+ )
+ )
+ }
+ }
+ }
+
+ if (currentPkg > pkgTotal) {
+ fileData?.apply {
+ result = 0
+ Log.i("file", "receive $currentFileName")
+ File(Constant.getPathX(currentFileName)).writeBytes(this)
+
+ }
+ val pkg = EndReadPkg()
+ Log.i("file", "bytes ${pkg.buf}")
+ sendCmd(pkg.buf)
+ cmdState = 3
+
+
+ } else {
+ val pkg = ReadContentPkg(currentPkg)
+ sendCmd(pkg.buf)
+ currentPkg++
+ }
+
+ } else if (cmdState == 3) {
+ fileData = null
+ currentPkg = 0
+ cmdState = 0
+ dataScope.launch {
+ fileProgressChannel.send(
+ FileProgress(
+ currentFileName, 100, result == 0
+ )
+ )
+ fileChannel.send(result)
+ }
+ }
+ } else if (cmdState == 4) {
+ val deviceInfo = DeviceInfo(temp)
+
+ val json = deviceInfo.json
+ var s: String = ""
+ for (k in json.keys()) {
+ s += "$k: "
+ s += "${json.get(k)} "
+ }
+ val returnData = mapOf("type" to "infoDataCheckMe", "data" to gson.toJson(json))
+ updateEventSink(returnData)
+ }
+
+
+ val tempBytes: ByteArray? = if (i + 8 + len == bytes.size) null else bytes.copyOfRange(
+ i + 8 + len, bytes.size
+ )
+
+ return handleDataPool(tempBytes)
+ }
+ }
+
+ return bytesLeft
+ }
+
+ private fun sendCmd(bs: ByteArray) {
+ myBleDataManager?.sendCmd(bs)
+ }
+
+
+ fun initWorker(context: Context, bluetoothDevice: BluetoothDevice?) {
+ myBleDataManager = BleDataManager(context)
+ myBleDataManager?.setNotifyListener(comeData)
+ bluetoothDevice?.let {
+ myBleDataManager?.connect(it)?.useAutoConnect(false)?.retry(150, 100)?.done {
+ dataScope.launch {
+ connectChannel.send("yes")
+ }
+ }?.enqueue()
+ }
+ }
+
+ suspend fun waitConnect() {
+ connectChannel.receive()
+ }
+
+ suspend fun getFile(name: String): Int {
+ mutex.withLock {
+ this.currentFileName = name
+ cmdState = 1
+ val pkg = StartReadPkg(name)
+ Log.i("----- Current PKG -----", pkg.buf.toString());
+ sendCmd(pkg.buf)
+ return fileChannel.receive()
+ }
+ }
+
+ private fun getDeviceInfo() {
+ cmdState = 4
+ val pkg = GetDeviceInfoPkg()
+ sendCmd(pkg.buf)
+
+ }
+
+ fun disconnect() {
+ myBleDataManager?.disconnect()?.enqueue()
+ }
+
+}
\ No newline at end of file
diff --git a/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/hmgwifi/HMG_Guest.kt b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/hmgwifi/HMG_Guest.kt
index 7b3cc2d1..e92070b5 100644
--- a/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/hmgwifi/HMG_Guest.kt
+++ b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/hmgwifi/HMG_Guest.kt
@@ -6,11 +6,10 @@ import android.content.Intent
import android.net.*
import android.net.wifi.*
import android.os.Build
-import android.os.PatternMatcher
import android.provider.Settings
import android.util.Log
import androidx.annotation.RequiresApi
-import com.ejada.hmg.MainActivity
+import com.cloud.diplomaticquarterapp.MainActivity
import com.ejada.hmg.utils.HMGUtils
diff --git a/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/hmgwifi/HMG_Internet.kt b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/hmgwifi/HMG_Internet.kt
index dcfa5488..8641d37a 100644
--- a/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/hmgwifi/HMG_Internet.kt
+++ b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/hmgwifi/HMG_Internet.kt
@@ -2,7 +2,7 @@ package com.ejada.hmg.hmgwifi
import android.annotation.SuppressLint
import com.ejada.hmg.utils.API
-import com.ejada.hmg.MainActivity
+import com.cloud.diplomaticquarterapp.MainActivity
import com.github.kittinunf.fuel.core.extensions.jsonBody
import com.github.kittinunf.fuel.httpGet
import com.github.kittinunf.fuel.httpPost
diff --git a/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/hmgwifi/WPA.kt b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/hmgwifi/WPA.kt
index 61cc4f9f..6a8a2590 100644
--- a/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/hmgwifi/WPA.kt
+++ b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/hmgwifi/WPA.kt
@@ -7,7 +7,7 @@ import android.net.wifi.*
import android.net.wifi.SupplicantState.ASSOCIATED
import android.net.wifi.SupplicantState.COMPLETED
import android.util.Log
-import com.ejada.hmg.MainActivity
+import com.cloud.diplomaticquarterapp.MainActivity
import com.ejada.hmg.utils.HMGUtils
class WPA(mainActivity: MainActivity, SSID:String) {
diff --git a/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/hmgwifi/WpaEnterprise.kt b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/hmgwifi/WpaEnterprise.kt
index e8e39344..9e0605f7 100644
--- a/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/hmgwifi/WpaEnterprise.kt
+++ b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/hmgwifi/WpaEnterprise.kt
@@ -12,9 +12,8 @@ import android.net.wifi.SupplicantState.COMPLETED
import android.os.Build
import android.util.Log
import androidx.annotation.RequiresApi
-import com.ejada.hmg.MainActivity
+import com.cloud.diplomaticquarterapp.MainActivity
import com.ejada.hmg.utils.HMGUtils
-import java.security.cert.X509Certificate
class WpaEnterprise(private val mainActivity: MainActivity, private var SSID: String) {
private var TAG = "WpaEnterprise"
diff --git a/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/smart_ring_2301/BaseActivity.kt b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/smart_ring_2301/BaseActivity.kt
new file mode 100644
index 00000000..0060cb10
--- /dev/null
+++ b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/smart_ring_2301/BaseActivity.kt
@@ -0,0 +1,106 @@
+import android.os.Bundle
+import android.util.Log
+import android.widget.Toast
+import androidx.appcompat.app.AppCompatActivity
+import com.cloud.diplomaticquarterapp.smart_ring_2301.BleManager
+import com.cloud.diplomaticquarterapp.smart_ring_2301.BleService
+import com.cloud.diplomaticquarterapp.smart_ring_2301.Util.BleData
+import com.cloud.diplomaticquarterapp.smart_ring_2301.Util.RxBus
+import com.jstyle.blesdk2301.Util.BleSDK.DataParsingWithData
+import com.jstyle.blesdk2301.callback.DataListener2301
+import com.jstyle.blesdk2301.constant.DeviceKey
+import io.reactivex.rxjava3.disposables.Disposable
+import io.reactivex.rxjava3.schedulers.Schedulers
+import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
+
+import javax.annotation.Nullable
+
+
+open class BaseActivity : AppCompatActivity(), DataListener2301 {
+ private var subscription: Disposable? = null
+
+ public override fun onCreate(@Nullable savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+// subscribeForSmartRingEvents()
+ }
+
+ protected open fun subscribeForSmartRingEvents() {
+ subscription = RxBus.instance.toObservable(BleData::class.java).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe { bleData ->
+ val action = bleData.action
+ if (action == BleService.ACTION_DATA_AVAILABLE) {
+ val value = bleData.value
+ if (value != null) {
+ DataParsingWithData(value, this@BaseActivity)
+ }
+ }
+ }
+ }
+
+ protected fun unSubscribe(disposable: Disposable?) {
+ if (disposable != null && !disposable.isDisposed) {
+ disposable.dispose()
+ }
+ }
+
+ protected override fun onDestroy() {
+ super.onDestroy()
+ unSubscribe(subscription)
+ }
+
+ protected fun Baseunsubscribe() {
+ unSubscribe(subscription)
+ }
+
+ protected fun Gateaus(count: Int) {}
+ override fun dataCallback(maps: Map?) {
+ Log.e("info", maps.toString())
+ }
+
+
+ override fun dataCallback(value: ByteArray?) {
+
+ }
+
+ /**
+ * 当蓝牙设备是连接的状态,发送指令给设备
+ * When the Bluetooth device is connected, send instructions to the device
+ * @param value
+ */
+ protected fun sendValue(value: ByteArray?) {
+
+ if (value == null) return
+ BleManager.instance?.writeValue(value)
+ }
+
+ protected fun showToast(text: String?) {
+ Toast.makeText(this, text, Toast.LENGTH_SHORT).show()
+ }
+
+
+ protected fun getEnd(maps: Map): Boolean {
+ return maps[DeviceKey.End] as Boolean
+ }
+
+ protected fun offerData(value: ByteArray?) {
+ BleManager.instance?.offerValue(value)
+ }
+
+ protected fun offerData() {
+ BleManager.instance?.writeValue()
+ }
+
+
+ companion object {
+ @JvmStatic
+ protected fun getDataType(maps: Map?): String? {
+ return if (null == maps) {
+ ""
+ } else maps[DeviceKey.DataType] as String?
+ }
+
+ @JvmStatic
+ protected fun getData(maps: Map): Map? {
+ return maps[DeviceKey.Data] as Map?
+ }
+ }
+}
\ No newline at end of file
diff --git a/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/smart_ring_2301/BleManager.kt b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/smart_ring_2301/BleManager.kt
new file mode 100644
index 00000000..bdead682
--- /dev/null
+++ b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/smart_ring_2301/BleManager.kt
@@ -0,0 +1,235 @@
+package com.cloud.diplomaticquarterapp.smart_ring_2301
+
+import android.annotation.SuppressLint
+import android.annotation.TargetApi
+import android.app.Service
+import android.bluetooth.BluetoothAdapter
+import android.bluetooth.BluetoothManager
+import android.bluetooth.le.ScanCallback
+import android.bluetooth.le.ScanResult
+import android.bluetooth.le.ScanSettings
+import android.content.ComponentName
+import android.content.Context
+import android.content.Intent
+import android.content.ServiceConnection
+import android.os.Build
+import android.os.IBinder
+import android.text.TextUtils
+import com.jstyle.blesdk2301.callback.BleConnectionListener
+import com.jstyle.blesdk2301.callback.OnScanResults
+import com.jstyle.blesdk2301.model.Device
+import java.util.Locale
+
+/**
+ * 蓝牙管理类 Bluetooth management class
+ */
+@SuppressLint("MissingPermission")
+class BleManager private constructor(var context: Context) {
+ private var address: String? = null
+ private var bleService: BleService? = null
+ private val serviceConnection: ServiceConnection = object : ServiceConnection {
+ override fun onServiceDisconnected(name: ComponentName) { // TODO Auto-generated method stub
+ bleService = null
+ }
+
+ override fun onServiceConnected(name: ComponentName, service: IBinder) {
+ bleService = (service as BleService.LocalBinder).service
+ if (!TextUtils.isEmpty(address)) {
+ bleService!!.initBluetoothDevice(address, context, NeedReconnect1, bleConnectionListener1)
+ }
+ }
+ }
+ private var serviceIntent: Intent? = null
+ var bluetoothAdapter: BluetoothAdapter?
+ val isBleEnable: Boolean
+ get() = bluetoothAdapter!!.enable()
+
+ /**
+ * 连接蓝牙设备
+ * Connect Bluetooth device
+ *
+ * @param address
+ */
+ protected var NeedReconnect1 = true
+ protected var bleConnectionListener1: BleConnectionListener? = null
+ fun connectDevice(address: String?, NeedReconnect: Boolean, bleConnectionListener: BleConnectionListener?) {
+ NeedReconnect1 = NeedReconnect
+ bleConnectionListener1 = bleConnectionListener
+ if (!bluetoothAdapter!!.isEnabled || TextUtils.isEmpty(address) || isConnected) return
+ if (bleService == null) {
+ this.address = address
+ } else {
+ bleService!!.initBluetoothDevice(address, context, NeedReconnect, bleConnectionListener)
+ }
+ }
+
+ /**
+ * 关闭蓝牙服务通知
+ * Turn off Bluetooth service notification
+ */
+ fun enableNotifaction() {
+ if (bleService == null) return
+ bleService!!.setCharacteristicNotification(true)
+ }
+
+ /**
+ * 写入指令到蓝牙设备
+ * Write command to Bluetooth device
+ */
+ fun writeValue(value: ByteArray?) {
+ if (bleService == null || instance == null) return
+ bleService!!.writeValue(value)
+ }
+
+ /**
+ * 多条指令同时添加后发送到设备
+ * Multiple instructions are added at the same time and sent to the device
+ *
+ * @param data
+ */
+ fun offerValue(data: ByteArray?) {
+ if (bleService == null) return
+ if (data != null) {
+ bleService!!.offerValue(data)
+ }
+ }
+
+ /**
+ * 写入指令到蓝牙设备
+ * Write command to Bluetooth device
+ */
+ fun writeValue() {
+ if (bleService == null) return
+ bleService!!.nextQueue()
+ }
+
+ /**
+ * 断开设备 Disconnect the device
+ */
+ fun disconnectDevice() {
+ if (bleService == null) return
+ bleService!!.disconnect()
+ }
+
+ val isConnected: Boolean
+ /**
+ * 查询设备是否已经连接
+ * Query whether the device is connected
+ *
+ * @return
+ */
+ get() = if (bleService == null) false else bleService!!.isConnected
+
+ /**
+ * 取消扫描设备
+ * Cancel scanning device
+ */
+ @TargetApi(Build.VERSION_CODES.LOLLIPOP)
+ fun StopDeviceScan() {
+ isinScan = false
+ if (null != bluetoothAdapter && null != bluetoothAdapter!!.bluetoothLeScanner) {
+ bluetoothAdapter!!.bluetoothLeScanner.stopScan(scanCallback)
+ }
+ }
+
+ /**
+ * 扫描蓝牙设备,用于开始绑定设备
+ * Scan Bluetooth device to start binding device
+ */
+ private var DevicesName: Array? = null
+ private var onScanResult: OnScanResults? = null
+ private var isinScan = false
+ @TargetApi(Build.VERSION_CODES.LOLLIPOP)
+ fun DeviceScanResults(Devicesname: Array, onScanResults: OnScanResults) {
+ if (!isinScan && instance!!.isBleEnable) { //如果蓝牙开关是开启的就进行扫描
+ isinScan = true
+ DevicesName = Devicesname
+ onScanResult = onScanResults
+ //返回已配对设备
+ //获得已配对的远程蓝牙设备的集合
+ val devices = bluetoothAdapter!!.bondedDevices
+ if (devices.size > 0) {
+ for (bluetoothDevice in devices) {
+ if (Canctians(bluetoothDevice.name.lowercase(Locale.getDefault()))) {
+ val device = Device()
+ device.bluetoothDevice = bluetoothDevice
+ device.isIsconted = false
+ device.isPaired = true
+ device.isIsdfu = bluetoothDevice.name.lowercase(Locale.getDefault()).contains("dfu")
+ device.name = bluetoothDevice.name
+ device.mac = bluetoothDevice.address
+ onScanResult!!.Success(device)
+ }
+ }
+ }
+ bluetoothAdapter!!.bluetoothLeScanner.startScan(null, ScanSettings.Builder().setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY).build(), scanCallback)
+ }
+ }
+
+ private val scanCallback: ScanCallback = object : ScanCallback() {
+ override fun onScanResult(callbackType: Int, result: ScanResult) {
+ super.onScanResult(callbackType, result)
+ val deviceName = result.device.name
+ if (isinScan && !TextUtils.isEmpty(deviceName) && Canctians(deviceName.lowercase(Locale.getDefault()).trim { it <= ' ' })) {
+ val device = Device()
+ device.bluetoothDevice = result.device
+ device.isIsconted = false
+ device.isPaired = false
+ device.isIsdfu = deviceName.lowercase(Locale.getDefault()).contains("dfu")
+ device.name = result.device.name
+ device.mac = result.device.address
+ device.riss = result.rssi
+ onScanResult!!.Success(device)
+ }
+ }
+
+ override fun onBatchScanResults(results: List) {
+ super.onBatchScanResults(results)
+ }
+
+ override fun onScanFailed(errorCode: Int) {
+ super.onScanFailed(errorCode)
+ onScanResult!!.Fail(errorCode)
+ }
+ }
+
+ init {
+ if (serviceIntent == null) {
+ serviceIntent = Intent(context, BleService::class.java)
+ context.bindService(serviceIntent, serviceConnection, Service.BIND_AUTO_CREATE)
+ }
+ val bluetoothManager = context.getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager
+ bluetoothAdapter = bluetoothManager.adapter
+ }
+
+ private fun Canctians(name: String): Boolean {
+ var catian = false
+ if (null == DevicesName || DevicesName!!.size == 0) {
+ return false
+ } else {
+ for (devicesname in DevicesName!!) {
+ if (name.lowercase(Locale.getDefault()).contains(devicesname.lowercase(Locale.getDefault()))) {
+ catian = true
+ break
+ }
+ }
+ }
+ return catian
+ }
+
+ companion object {
+ var instance: BleManager? = null
+ private set
+
+ fun init(context: Context) {
+ if (instance != null) {
+ return
+ }
+ synchronized(BleManager::class.java) {
+ if (instance == null) {
+ instance = BleManager(context)
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/smart_ring_2301/BleService.kt b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/smart_ring_2301/BleService.kt
new file mode 100644
index 00000000..3c868808
--- /dev/null
+++ b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/smart_ring_2301/BleService.kt
@@ -0,0 +1 @@
+package com.cloud.diplomaticquarterapp.smart_ring_2301
import android.annotation.SuppressLint
import android.app.Service
import android.bluetooth.BluetoothAdapter
import android.bluetooth.BluetoothDevice
import android.bluetooth.BluetoothGatt
import android.bluetooth.BluetoothGattCallback
import android.bluetooth.BluetoothGattCharacteristic
import android.bluetooth.BluetoothGattDescriptor
import android.bluetooth.BluetoothGattService
import android.bluetooth.BluetoothManager
import android.bluetooth.BluetoothProfile
import android.content.Context
import android.content.Intent
import android.os.Binder
import android.os.Build
import android.os.Handler
import android.os.IBinder
import android.os.Looper
import android.text.TextUtils
import android.util.Log
import com.cloud.diplomaticquarterapp.smart_ring_2301.Util.BleData
import com.cloud.diplomaticquarterapp.smart_ring_2301.Util.ResolveData.byte2Hex
import com.cloud.diplomaticquarterapp.smart_ring_2301.Util.ResolveData.decodeDeviceName
import com.cloud.diplomaticquarterapp.smart_ring_2301.Util.RxBus.Companion.instance
import com.jstyle.blesdk2301.callback.BleConnectionListener
import java.util.LinkedList
import java.util.Queue
import java.util.UUID
@SuppressLint("MissingPermission")
class BleService : Service() {
private var NeedReconnect = false
var fastconnect = false //是否连接成功过设备
var hasp = HashMap()
private val kBinder: IBinder = LocalBinder()
private val gattHash = HashMap()
private var bluetoothManager: BluetoothManager? = null
private var mBluetoothAdapter: BluetoothAdapter? = null
private var mGatt: BluetoothGatt? = null
var isConnected = false
private set
private val handler = Handler()
override fun onBind(intent: Intent): IBinder? {
initAdapter()
return kBinder
}
override fun onUnbind(intent: Intent): Boolean {
return super.onUnbind(intent)
}
/**
* 初始化BLE 如果已经连接就不用再次连
*
* @param bleDevice
* @return
*/
private var address: String? = null
private var mContext: Context? = null
protected var bleConnectionListener: BleConnectionListener? = null
fun initBluetoothDevice(address: String?, context: Context?, needReconnect: Boolean, bleConnectionListener1: BleConnectionListener?) {
if (null != bleConnectionListener1) {
bleConnectionListener = bleConnectionListener1
NeedReconnect = needReconnect
}
//MyLog.i("开始连接");
fastconnect = false
this.address = address
mContext = context
val device = mBluetoothAdapter!!.getRemoteDevice(address)
if (isConnected) return
if (null != mGatt) {
refreshDeviceCache(mGatt)
mGatt = null
}
mGatt = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
device.connectGatt(context, false, bleGattCallback, BluetoothDevice.TRANSPORT_LE)
} else {
device.connectGatt(context, false, bleGattCallback)
}
if (mGatt == null) {
println(device.address + "gatt is null")
}
if (null != bleConnectionListener) {
bleConnectionListener!!.Connecting()
}
// this.mContext=context;
// reconnect(true);
}
private var scanToConnect = true
private var isScaning = false
@SuppressLint("MissingPermission")
fun startScan(enable: Boolean) {
Log.i(TAG, "startScan: $enable")
if (!mBluetoothAdapter!!.isEnabled) {
return
}
if (scanToConnect) {
startScanDevice(true)
} else {
initBluetoothDevice(address, mContext, NeedReconnect, bleConnectionListener)
}
scanToConnect = !scanToConnect
}
@SuppressLint("MissingPermission")
private fun startScanDevice(enable: Boolean) {
if (enable) {
if (isScaning) return
handler.postDelayed({
mBluetoothAdapter!!.stopLeScan(mLeScanCallback)
isScaning = false
startScan(true)
}, 20000)
fastconnect = false
mBluetoothAdapter!!.startLeScan(mLeScanCallback)
} else {
if (isScaning) {
mBluetoothAdapter!!.stopLeScan(mLeScanCallback)
handler.removeCallbacksAndMessages(null)
}
}
isScaning = enable
}
private val mLeScanCallback = BluetoothAdapter.LeScanCallback { device, rssi, scanRecord ->
if (device.address == address) {
val name = decodeDeviceName(device, scanRecord)
if (!TextUtils.isEmpty(name) && name == "DfuTarg") return@LeScanCallback
if (mGatt != null) return@LeScanCallback
handler.post {
startScanDevice(false)
mGatt = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
device.connectGatt(mContext, false, bleGattCallback, BluetoothDevice.TRANSPORT_LE)
} else {
device.connectGatt(mContext, false, bleGattCallback)
}
if (mGatt == null) {
println("gatt is null ")
}
}
}
}
private fun initAdapter() {
if (bluetoothManager == null) {
bluetoothManager = getSystemService(BLUETOOTH_SERVICE) as BluetoothManager
if (bluetoothManager == null) {
return
}
}
mBluetoothAdapter = bluetoothManager!!.adapter
}
/**
* 断开连接
*/
fun disconnect() {
NeedReconnect = false
broadcastUpdate(ACTION_GATT_DISCONNECTED)
if (mGatt != null) {
mGatt = if (isConnected) {
mGatt!!.disconnect()
mGatt!!.close()
null
} else {
Log.i(TAG, "close: ")
mGatt!!.close()
null
}
}
isConnected = false
}
/**
* 根据设备的Mac地址断开连接
*
* @param address
*/
fun disconnect(address: String) {
val gatts = ArrayList()
for (gatt in arrayGatts) {
if (gatt != null && gatt.device.address == address) {
gatts.add(gatt)
// gatt.disconnect();
gatt.close()
// gatt = null;
}
}
arrayGatts.removeAll(gatts)
}
inner class LocalBinder : Binder() {
val service: BleService
get() = this@BleService
}
private var discoverCount = 0
private val ob = Any()
private val bleGattCallback: BluetoothGattCallback = object : BluetoothGattCallback() {
override fun onConnectionStateChange(gatt: BluetoothGatt, status: Int, newState: Int) {
var gatt: BluetoothGatt? = gatt
if (status == 133 || newState == 0) {
if (null != bleConnectionListener) {
bleConnectionListener!!.BleStatus(status, newState)
}
if (mGatt != null) {
mGatt!!.disconnect()
mGatt!!.close()
refreshDeviceCache(mGatt)
mGatt = null
}
if (gatt != null) {
gatt.disconnect()
gatt.close()
refreshDeviceCache(gatt)
gatt = null
}
if (NeedReconnect) startScan(true)
return
}
var action: String? = null
Log.i(TAG, "onConnectionStateChange: status$status newstate $newState")
if (newState == BluetoothProfile.STATE_CONNECTED) {
try {
gatt!!.discoverServices()
} catch (e: Exception) {
e.printStackTrace()
}
} else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
if (null != bleConnectionListener) {
bleConnectionListener!!.ConnectionFailed()
}
isConnected = false
Log.i(TAG, "onConnectionStateChange: " + ACTION_GATT_DISCONNECTED)
if (mGatt != null) {
mGatt!!.close()
mGatt = null
}
queues.clear()
if (!NeedReconnect) {
action = ACTION_GATT_DISCONNECTED
broadcastUpdate(action)
}
if (!NeedReconnect) {
} else {
if (fastconnect) {
fastconnect = false
Log.e(TAG, "发送异常断开")
}
if (status == 133) {
refreshDeviceCache(gatt)
}
if (null != bleConnectionListener) {
bleConnectionListener!!.OnReconnect()
}
startScan(true)
}
} else {
Log.e("MainActivity", "onConnectionStateChange: status$status newstate $newState")
if (0 == status && 0 == newState) {
if (null != bleConnectionListener) {
bleConnectionListener!!.BluetoothSwitchIsTurnedOff()
}
} else {
if (null != bleConnectionListener) {
bleConnectionListener!!.BleStatus(status, newState)
}
}
}
}
/*
* 搜索device中的services (non-Javadoc)
*
* @see
* android.bluetooth.BluetoothGattCallback#onServicesDiscovered(android
* .bluetooth.BluetoothGatt, int)
*/
override fun onServicesDiscovered(gatt: BluetoothGatt, status: Int) {
super.onServicesDiscovered(gatt, status)
// if (mGatt == null)
// return;
if (status == BluetoothGatt.GATT_SUCCESS) {
val address = gatt.device.address
val name = mBluetoothAdapter!!.getRemoteDevice(address)
.name
setCharacteristicNotification(true)
/* if (gatt != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
gatt.requestMtu(512);
}else{
setCharacteristicNotification(true);
}*/discoverCount = 0
} else {
// mGatt = null;
Log.w(
"servieDiscovered", "onServicesDiscovered received: "
+ status
)
}
}
override fun onMtuChanged(gatt: BluetoothGatt, mtu: Int, status: Int) {
super.onMtuChanged(gatt, mtu, status)
if (BluetoothGatt.GATT_SUCCESS == status) {
setCharacteristicNotification(true)
} else {
gatt.requestMtu(153)
}
}
/*
* 读取特征�?(non-Javadoc)
*
* @see
* android.bluetooth.BluetoothGattCallback#onCharacteristicRead(android
* .bluetooth.BluetoothGatt,
* android.bluetooth.BluetoothGattCharacteristic, int)
*/
override fun onCharacteristicRead(gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic, status: Int) {
super.onCharacteristicRead(gatt, characteristic, status)
if (status == BluetoothGatt.GATT_SUCCESS) {
broadcastUpdate(
ACTION_DATA_AVAILABLE, characteristic, gatt
.device.address
)
} else {
}
}
override fun onDescriptorWrite(
gatt: BluetoothGatt,
descriptor: BluetoothGattDescriptor, status: Int
) {
super.onDescriptorWrite(gatt, descriptor, status)
if (status == BluetoothGatt.GATT_SUCCESS) {
nextQueue()
isConnected = true
broadcastUpdate(ACTION_GATT_onDescriptorWrite)
if (null != bleConnectionListener) {
bleConnectionListener!!.ConnectionSucceeded()
}
} else {
Log.i(TAG, "onDescriptorWrite: failed")
}
}
/*
* 特征值的变化 (non-Javadoc)
*
* @see
* android.bluetooth.BluetoothGattCallback#onCharacteristicChanged(android
* .bluetooth.BluetoothGatt,
* android.bluetooth.BluetoothGattCharacteristic)
*/
override fun onCharacteristicChanged(
gatt: BluetoothGatt,
characteristic: BluetoothGattCharacteristic
) {
super.onCharacteristicChanged(gatt, characteristic)
if (mGatt == null) return
Log.i(TAG, "onCharacteristicChanged: " + byte2Hex(characteristic.value))
// SDUtil.saveBTLog("log", "Receiving: " + ResolveData.byte2Hex(characteristic.getValue()));
broadcastUpdate(
ACTION_DATA_AVAILABLE, characteristic, gatt
.device.address
)
// SendData.sendBus(ACTION_DATA_AVAILABLE, characteristic.getValue());
}
override fun onCharacteristicWrite(
gatt: BluetoothGatt,
characteristic: BluetoothGattCharacteristic, status: Int
) {
super.onCharacteristicWrite(gatt, characteristic, status)
if (status == BluetoothGatt.GATT_SUCCESS) {
nextQueue()
} else {
// MyLog.i("status" + status);
}
}
}
fun refreshDeviceCache(gatt: BluetoothGatt?): Boolean {
try {
val localMethod = gatt!!.javaClass.getMethod(
"refresh", *arrayOfNulls(0)
)
if (localMethod != null) {
return localMethod.invoke(
gatt, *arrayOfNulls(0)
) as Boolean
}
} catch (localException: Exception) {
Log.e("s", "An exception occured while refreshing device")
}
return false
}
/**
* 广播
*
* @param action
*/
private fun broadcastUpdate(action: String?) {
val bleData = BleData()
bleData.action = action
instance.post(bleData)
//Intent intent = new Intent(action);
//sendBroadcast(intent);
}
/**
* 发�?带蓝牙信息的到广�? *
*
* @param action
* @param characteristic
*/
private fun broadcastUpdate(
action: String,
characteristic: BluetoothGattCharacteristic, mac: String
) {
// Intent intent = new Intent(action);
val data = characteristic.value
val bleData = BleData()
bleData.action = action
bleData.value = data
instance.post(bleData)
}
/**
* 读取设备数据
*
* @param
* @param characteristic
*/
fun readValue(characteristic: BluetoothGattCharacteristic?) {
if (mGatt == null) return
mGatt!!.readCharacteristic(characteristic)
}
/**
* 写入设备数据
*/
fun writeValue(value: ByteArray?) {
Handler(Looper.getMainLooper()).postDelayed(Runnable {
if (mGatt == null || value == null) return@Runnable
val service = mGatt!!.getService(SERVICE_DATA) ?: return@Runnable
val characteristic = service.getCharacteristic(DATA_Characteristic) ?: return@Runnable
if (value[0] == 0x47.toByte()) {
NeedReconnect = false
}
characteristic.value = value
Log.i(TAG, "writeValue: " + byte2Hex(value))
mGatt!!.writeCharacteristic(characteristic)
// SDUtil.saveBTLog("log", "writeValue: " + ResolveData.byte2Hex(value));
}, 500)
}
fun setCharacteristicNotification(enable: Boolean) {
if (mGatt == null) return
val service = mGatt!!.getService(SERVICE_DATA) ?: return
val characteristic = service.getCharacteristic(NOTIY_Characteristic) ?: return
mGatt!!.setCharacteristicNotification(characteristic, enable)
try {
Thread.sleep(20)
} catch (e: InterruptedException) {
e.printStackTrace()
}
val descriptor = characteristic.getDescriptor(NOTIY)
?: //MyLog.e("setCharacteristicNotification descriptor=null,所以不能发送使能数据");
return
descriptor.value = BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE
if (mGatt == null) return
mGatt!!.writeDescriptor(descriptor)
}
val supportedGattServices: List?
/**
* 获取services
*
* @return
*/
get() = if (mGatt == null) {
//MyLog.e("getServices, gatt is null ");
null
} else mGatt!!.services
/**
* 根据设备的Mac地址从已经连接的设备中匹配对应的BluetoothGatt对象
*
* @param device
* @return
*/
private fun getBluetoothGatt(device: BluetoothDevice): BluetoothGatt? {
return mGatt
}
/**
* //读取信号
*
* @param device
*/
fun readRssi(device: BluetoothDevice?) {
mGatt!!.readRemoteRssi()
}
override fun onDestroy() {
super.onDestroy()
}
var queues: Queue = LinkedList()
fun offerValue(value: ByteArray) {
queues.offer(value)
}
fun nextQueue() {
val requests = queues
val data = if (requests != null) requests.poll() else null
writeValue(data)
}
companion object {
private const val TAG = "BleService"
private val NOTIY = UUID.fromString("00002902-0000-1000-8000-00805f9b34fb")
private val SERVICE_DATA = UUID.fromString("0000fff0-0000-1000-8000-00805f9b34fb")
private val DATA_Characteristic = UUID.fromString("0000fff6-0000-1000-8000-00805f9b34fb")
private val NOTIY_Characteristic = UUID.fromString("0000fff7-0000-1000-8000-00805f9b34fb")
const val ACTION_GATT_onDescriptorWrite = "com.jstylelife.ble.service.onDescriptorWrite"
const val ACTION_GATT_CONNECTED = "com.jstylelife.ble.service.ACTION_GATT_CONNECTED"
const val ACTION_GATT_DISCONNECTED = "com.jstylelife.ble.service.ACTION_GATT_DISCONNECTED"
const val ACTION_DATA_AVAILABLE = "com.jstylelife.ble.service.ACTION_DATA_AVAILABLE"
private val arrayGatts = ArrayList() // 存放BluetoothGatt的集�?
var colorCharacteristic: BluetoothGattCharacteristic? = null
private var bleServiceInstance: BleService? = null
fun getInstance(): BleService? {
return bleServiceInstance
}
}
}
\ No newline at end of file
diff --git a/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/smart_ring_2301/Util/BleData.kt b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/smart_ring_2301/Util/BleData.kt
new file mode 100644
index 00000000..0d52c3db
--- /dev/null
+++ b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/smart_ring_2301/Util/BleData.kt
@@ -0,0 +1,11 @@
+package com.cloud.diplomaticquarterapp.smart_ring_2301.Util
+
+/**
+ * Created by Administrator on 2017/4/11.
+ */
+class BleData {
+
+ var value: ByteArray? = null
+ var action: String? = null
+ var data: String? = null
+}
\ No newline at end of file
diff --git a/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/smart_ring_2301/Util/CustomCountDownTimer.kt b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/smart_ring_2301/Util/CustomCountDownTimer.kt
new file mode 100644
index 00000000..88574367
--- /dev/null
+++ b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/smart_ring_2301/Util/CustomCountDownTimer.kt
@@ -0,0 +1,137 @@
+package com.cloud.diplomaticquarterapp.smart_ring_2301.Util
+
+import android.os.Handler
+import android.os.Looper
+import java.util.concurrent.Executors
+import java.util.concurrent.ScheduledExecutorService
+import java.util.concurrent.TimeUnit
+
+/**
+ * 计时器
+ */
+class CustomCountDownTimer(
+//Millis since epoch when alarm should stop.
+ private var millisInFuture: Long, countDownInterval: Long,
+ tickListener: TimerTickListener
+) {
+ //thread on which the callbacks will be called
+ private val mainThreadHandler = Handler(Looper.getMainLooper())
+
+ //listener interface which is to be implemented by the users of the count down timer
+ interface TimerTickListener {
+ /**
+ * Callback on each tick
+ *
+ * @param millisLeft time left in millisec for the timer to shutdown
+ */
+ fun onTick(millisLeft: Long)
+
+ /**
+ * Callback to be invokded when timer's time finishes
+ */
+ fun onFinish()
+
+ /**
+ * Callback to be invokded when timer is canceled
+ */
+ fun onCancel()
+ }
+
+ /**
+ * Inner class which delegates the events to callbacks provided in the TimerTickListener
+ */
+ private inner class TimerRunnable : Runnable {
+ override fun run() {
+ if (isCancelled) {
+ scheduler.shutdown()
+ mainThreadHandler.post { tickListener.onCancel() }
+ } else if (isPaused) {
+ } else {
+ stopTimeInFuture = stopTimeInFuture - countdownInterval
+ mainThreadHandler.post(object : Runnable {
+ val millisLeft = stopTimeInFuture
+ override fun run() {
+ if (millisLeft <= 0) {
+ tickListener.onFinish()
+ scheduler.shutdown()
+ } else {
+ tickListener.onTick(millisLeft)
+ }
+ }
+ })
+ }
+ }
+ }
+
+ //The interval in millis that the user receives callbacks
+ private val countdownInterval: Long
+
+ //the time at which timer is to stop
+ private var stopTimeInFuture: Long
+
+ //boolean representing if the timer was cancelled
+ var isCancelled = false
+
+ //boolean representing if the timer was paused
+ private var isPaused = false
+
+ //listener which listens to the timer events
+ private val tickListener: TimerTickListener
+
+ //scheduler which provides the thread to create timer
+ private val scheduler: ScheduledExecutorService
+
+ /**
+ * Constructor
+ *
+ * @param millisInFuture time in millisec for which timer is to run
+ * @param countDownInterval interval frequency in millisec at which the callback will be invoked
+ * @param tickListener implementation of TimerTickListener which provides callbacks code
+ */
+ init {
+ stopTimeInFuture = millisInFuture
+ countdownInterval = countDownInterval
+ this.tickListener = tickListener
+ scheduler = Executors.newSingleThreadScheduledExecutor()
+ }
+
+ /**
+ * Start the countdown.
+ */
+ @Synchronized
+ fun start() {
+ isCancelled = false
+ isPaused = false
+ scheduler.scheduleWithFixedDelay(
+ TimerRunnable(), countdownInterval, countdownInterval,
+ TimeUnit.MILLISECONDS
+ )
+ }
+
+ /**
+ * Cancels the countdown timer
+ */
+ @Synchronized
+ fun cancel() {
+ isCancelled = true
+ }
+
+ @Synchronized
+ fun pause() {
+ isPaused = true
+ }
+
+ @Synchronized
+ fun resume() {
+ isPaused = false
+ }
+
+ fun extendTime(delta: Long) {
+ stopTimeInFuture = stopTimeInFuture + delta
+ millisInFuture = millisInFuture + delta
+ }
+
+ companion object {
+ const val TAG = "CustomCountDownTimer"
+ }
+}
\ No newline at end of file
diff --git a/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/smart_ring_2301/Util/DateUtil.kt b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/smart_ring_2301/Util/DateUtil.kt
new file mode 100644
index 00000000..866e1747
--- /dev/null
+++ b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/smart_ring_2301/Util/DateUtil.kt
@@ -0,0 +1,504 @@
+package com.cloud.diplomaticquarterapp.smart_ring_2301.Util
+
+import android.content.Context
+import java.text.NumberFormat
+import java.text.ParseException
+import java.text.SimpleDateFormat
+import java.util.Calendar
+import java.util.Date
+
+/**
+ * Created by Administrator on 2017/3/24.
+ */
+object DateUtil {
+ const val oneDayMillis = 24 * 60 * 60 * 1000L
+ private const val oneMinMillis = 60 * 1000L
+ const val Default_FormatString = "yy.MM.dd"
+ const val SHOW_FormatString = "MM-dd,yyyy"
+ val format = SimpleDateFormat("yy.MM.dd HH:mm:ss")
+ var defaultFormat = SimpleDateFormat(Default_FormatString)
+ const val DATA_MONTH_FormatString = "yy.MM"
+ const val DATA_Year_FormatString = "yyyy.M" //数据年日期
+ const val last_month = -1
+ const val this_month = 0
+ const val next_month = 1
+ const val nextWeek = 326
+ const val lastWeek = 396
+
+ /**
+ * 明天的日期
+ */
+ fun getTomorrowDateString(today: String?): String {
+ val c = Calendar.getInstance()
+ c.timeInMillis = getDateLong(today) + oneDayMillis
+ return getDefaultFormatTime(c.time)
+ }
+
+ fun getDateString(today: String?, days: Int): String {
+ val c = Calendar.getInstance()
+ c.timeInMillis = getDateLong(today) - oneDayMillis * days
+ return getDefaultFormatTime(c.time)
+ }
+
+ /**
+ * 昨天的日期
+ */
+ @Synchronized
+ fun getYesterdayDateString(today: String?): String {
+ val c = Calendar.getInstance()
+ c.timeInMillis = getDateLong(today) - oneDayMillis
+ return getDefaultFormatTime(c.time)
+ }
+
+ /**
+ * 获取指定格式的日期字符串
+ *
+ * @param time
+ * @param formatString
+ * @return
+ */
+ @Synchronized
+ fun getFormatTimeString(time: Long, formatString: String?): String {
+ val format = SimpleDateFormat(formatString)
+ return format.format(Date(time))
+ }
+
+ @Synchronized
+ fun getFormatTimeString(time: Long): String {
+ return format.format(Date(time))
+ }
+
+ @Synchronized
+ fun getEcgFormatTimeString(time: Long): String {
+ val simpleDateFormat = SimpleDateFormat("yyyy.MM.dd HH:mm:ss")
+ return simpleDateFormat.format(Date(time))
+ }
+
+ @Synchronized
+ fun getEcgFormatTodayString(time: Long): String {
+ val simpleDateFormat = SimpleDateFormat("yyyy.MM.dd")
+ return simpleDateFormat.format(Date(time))
+ }
+
+ /**
+ * 获取默认格式日期字符串
+ *
+ * @return
+ */
+ @Synchronized
+ fun getDefaultFormatTime(date: Date): String {
+ return getFormatTimeString(date.time, Default_FormatString)
+ }
+
+ /**字符串代表日期的毫秒数
+ * @param date
+ * @return
+ */
+ @Synchronized
+ fun getDateLong(date: String?): Long {
+ var time: Long = 0
+ try {
+ val dates = defaultFormat.parse(date)
+ time = dates.time
+ } catch (e: Exception) {
+ // TODO: handle exception
+ }
+ return time
+ }
+
+ @Synchronized
+ fun getGpsDateLong(date: String?): Long {
+ var time: Long = 0
+ try {
+ val dates = format.parse(date)
+ time = dates.time
+ } catch (e: Exception) {
+ // TODO: handle exception
+ }
+ return time
+ }
+
+ /**字符串转date
+ * @param dateString
+ * @return
+ */
+ fun String2Date(dateString: String?, formatString: String?): Date? {
+ val format = SimpleDateFormat(formatString)
+ var date: Date? = null
+ try {
+ date = format.parse(dateString)
+ } catch (e: ParseException) {
+ e.printStackTrace()
+ }
+ return date
+ }
+
+ fun getMonthString(thisMonth: String?, monthType: Int): String {
+ val calendar = Calendar.getInstance()
+ if (monthType != this_month) calendar.time = String2Date(thisMonth, DATA_MONTH_FormatString)
+ calendar.add(Calendar.MONTH, monthType)
+ return getFormatTimeString(calendar.timeInMillis, DATA_MONTH_FormatString)
+ }
+
+ fun getTodayWeek(time: Long): Array {
+ val format = SimpleDateFormat(Default_FormatString)
+ val week = arrayOfNulls(7)
+ val calendar = Calendar.getInstance()
+ if (time != 0L) {
+ calendar.timeInMillis = time
+ }
+ setToFirstDay(calendar)
+ for (i in 0..6) {
+ week[i] = format.format(calendar.time)
+ calendar.add(Calendar.DATE, 1)
+ }
+ return week
+ }
+
+ private fun setToFirstDay(calendar: Calendar) {
+ while (calendar[Calendar.DAY_OF_WEEK] != Calendar.MONDAY) {
+ calendar.add(Calendar.DATE, -1)
+ }
+ }
+
+ fun getWeekString(time: String?, type: Int): Array {
+ val format = SimpleDateFormat(Default_FormatString)
+ var date: Date? = null
+ try {
+ date = format.parse(time)
+ } catch (e: Exception) {
+ // TODO: handle exception
+ }
+ val nowL = if (type == lastWeek) date!!.time - oneDayMillis else date!!.time + oneDayMillis
+ return getTodayWeek(nowL)
+ }
+
+ /**时间段在一天中的时间节点(一分钟一点)
+ * @param time
+ * @param defaultTime
+ * @return
+ */
+ fun get1MIndex(time: String?, defaultTime: String?): Int {
+ val format = SimpleDateFormat("yy.MM.dd HH:mm:ss")
+ var count = 0
+ try {
+ val date = format.parse(time)
+ val dateBase = format.parse(defaultTime)
+ val min = date.time - dateBase.time
+ count = (min / oneMinMillis).toInt()
+ } catch (e: Exception) {
+ // TODO: handle exception
+ }
+ return count
+ }
+
+ fun get1SIndex(time: String?, defaultTime: String?): Int {
+ val format = SimpleDateFormat("yy.MM.dd HH:mm:ss")
+ var count = 0
+ try {
+ val date = format.parse(time)
+ val dateBase = format.parse(defaultTime)
+ val min = date.time - dateBase.time
+ count = (min / 1000L).toInt()
+ } catch (e: Exception) {
+ // TODO: handle exception
+ }
+ return count
+ }
+
+ fun get5MIndex(time: String?, defaultTime: String?): Int {
+ val format = SimpleDateFormat("yy.MM.dd HH:mm:ss")
+ var count = 0
+ try {
+ val date = format.parse(time)
+ val dateBase = format.parse(defaultTime)
+ val min = date.time - dateBase.time
+ count = (min / (oneMinMillis * 5)).toInt()
+ } catch (e: Exception) {
+ // TODO: handle exception
+ }
+ return count
+ }
+
+ fun getMIndex(time: String?, defaultTime: String?, countMIN: Int): Int {
+ val format = SimpleDateFormat("yy.MM.dd HH:mm:ss")
+ var count = 0
+ try {
+ val date = format.parse(time)
+ val dateBase = format.parse(defaultTime)
+ val min = date.time - dateBase.time
+ count = (min / (oneMinMillis * countMIN)).toInt()
+ } catch (e: Exception) {
+ // TODO: handle exception
+ }
+ return count
+ }
+
+ @Synchronized
+ fun getHistoryTime(time: Long): String {
+ val defaultTime = getDateLong("00.01.01")
+ return getFormatTimeString(time * 1000 * 5 + defaultTime)
+ }
+
+ private const val TAG = "DateUtil"
+ @Synchronized
+ fun getDefaultLongMi(defaultTime: String?): Long {
+ var date = Date()
+ // SimpleDateFormat format = new SimpleDateFormat("yy.MM.dd HH:mm:ss");
+ // Log.i(TAG, "getDefaultLongMi: "+defaultTime);
+ try {
+ date = format.parse(defaultTime)
+ } catch (e: Exception) {
+ // Log.i(TAG, "getDefaultLongMi: "+e.getMessage());
+ }
+ return date.time
+ }
+
+ fun getSleepTime(minute: Int): String {
+ val hour = minute / 60
+ val min = minute % 60
+ return String.format("%1$02dh%2$02dmin", hour, min)
+ }
+
+ fun getActivityShowTime(minute: Int): String {
+ val hour = minute / 60
+ val min = minute % 60
+ return String.format("%1$02d:%2$02d", hour, min)
+ }
+
+ fun getSportTime(seconds: Int): String {
+ var hour = 0
+ var min = seconds / 60
+ var second = seconds % 60
+ if (min >= 60) {
+ hour = min / 60
+ min = min % 60
+ second = seconds - hour * 3600 - min * 60
+ }
+ return String.format("%1$02d:%2$02d:%3$02d", hour, min, second)
+ }
+
+ fun getExerciseListTotalTime(seconds: Int): String {
+ var hour = 0
+ var min = seconds / 60
+ // int second=seconds%60;
+ if (min >= 60) {
+ hour = min / 60
+ min = min % 60
+ //second = seconds - hour * 3600 - min * 60;
+ }
+ return String.format("%1$02dh%2$02dmin", hour, min)
+ }
+
+ fun getSportAxieTime(seconds: Int): String {
+ var hour = 0
+ var min = seconds / 60
+ var second = seconds % 60
+ if (min >= 60) {
+ hour = min / 60
+ min = min % 60
+ second = seconds - hour * 3600 - min * 60
+ }
+ return if (hour == 0) String.format("%1$02d:%2$02d", min, second) else String.format("%1$02d:%2$02d:%3$02d", hour, min, second)
+ }
+
+ fun getPaceTime(seconds: Int): String {
+ val min = seconds / 60
+ val second = seconds % 60
+ // if(min>60){
+// hour=min/60;
+// min=min%60;
+// second = seconds - hour * 3600 - min * 60;
+// }
+ return String.format("%1$02d'%2$02d\"", min, second)
+ }
+
+ fun getActivityTime(seconds: Int): String {
+ var hour = 0
+ var min = seconds / 60
+ var second = seconds % 60
+ if (min > 60) {
+ hour = min / 60
+ min = min % 60
+ second = seconds - hour * 3600 - min * 60
+ }
+ return if (hour == 0) String.format("%1$02d:%2$02d", min, second) else String.format("%1$02d:%2$02d:%3$02d", hour, min, second)
+ }
+
+ fun getPointSleepTime(minute: Float): String {
+ val nf = NumberFormat.getInstance()
+ nf.minimumFractionDigits = 2
+ return nf.format((minute / 60f).toDouble()) + "h"
+ }
+
+ fun getSleepTimeFloat(minute: Int): Float {
+ val hour = minute / 60
+ val min = minute % 60
+ return hour + min / 60f
+ }
+
+ /**时间点换成时间段
+ * @param count
+ * @return
+ */
+ fun getCountTime(count: Int, defaultTime: String?): String {
+ val format = SimpleDateFormat("yy.MM.dd HH:mm:ss")
+ // String base = "00:00:00";
+ var time: Long = 0
+ try {
+ time = format.parse(defaultTime).time + count * oneMinMillis
+ } catch (e: Exception) {
+ // TODO: handle exception
+ }
+ return format.format(Date(time))
+ }
+
+ fun getSleepCountTime(count: Int, defaultTime: String?): String {
+ val format = SimpleDateFormat("yy.MM.dd HH:mm:ss")
+ // String base = "00:00:00";
+ var time: Long = 0
+ try {
+ time = format.parse(defaultTime).time + 5 * count * oneMinMillis
+ } catch (e: Exception) {
+ // TODO: handle exception
+ }
+ return format.format(Date(time))
+ }
+
+ val thisMonth: Array
+ get() {
+ val format = SimpleDateFormat("yy.MM.dd")
+ // String[] month = new String[3];
+ val cal_1 = Calendar.getInstance() // 获取当前日期
+ val max = cal_1.getActualMaximum(Calendar.DAY_OF_MONTH)
+ val month = arrayOfNulls(max)
+ for (i in 1 until max + 1) {
+ cal_1[Calendar.DAY_OF_MONTH] = i
+ month[i - 1] = format.format(cal_1.time)
+ }
+ return month
+ }
+
+ fun getNextMonth(date: String?): Array {
+ val format = SimpleDateFormat("yy.MM.dd")
+ var dates: Date? = Date()
+ try {
+ dates = format.parse(date)
+ } catch (e: Exception) {
+ // TODO: handle exception
+ }
+ val cal_1 = Calendar.getInstance() // 获取当前日期
+ cal_1.time = dates
+ var months = cal_1[Calendar.MONTH]
+ var year = cal_1[Calendar.YEAR]
+ if (months == Calendar.DECEMBER) {
+ year++
+ months = Calendar.JANUARY
+ } else {
+ months++
+ }
+ cal_1[Calendar.YEAR] = year
+ cal_1[Calendar.MONTH] = months
+ val max = cal_1.getActualMaximum(Calendar.DAY_OF_MONTH)
+ val month = arrayOfNulls(max)
+ for (i in 1 until max + 1) {
+ cal_1[Calendar.DAY_OF_MONTH] = i
+ month[i - 1] = format.format(cal_1.time)
+ }
+
+// cal_1.set(Calendar.DAY_OF_MONTH,
+// cal_1.getActualMaximum(Calendar.DAY_OF_MONTH));// 设置为1号,当前日期既为本月第一天
+// month[1] = format.format(cal_1.getTime());
+// month[2]=cal_1.getActualMaximum(Calendar.DAY_OF_MONTH)+"";
+ return month
+ }
+
+ fun getMonth(year: Int, month: Int): Array {
+ val format = SimpleDateFormat("yy.MM.dd")
+ val cal_1 = Calendar.getInstance() // 获取当前日期
+ cal_1[Calendar.YEAR] = year
+ cal_1[Calendar.MONTH] = month
+ val max = cal_1.getActualMaximum(Calendar.DAY_OF_MONTH)
+ val months = arrayOfNulls(max)
+ for (i in 1 until max + 1) {
+ cal_1[Calendar.DAY_OF_MONTH] = i
+ months[i - 1] = format.format(cal_1.time)
+ }
+ return months
+ }
+
+ fun getLastMonth(date: String?): Array {
+ val format = SimpleDateFormat("yy.MM.dd")
+ var dates: Date? = Date()
+ try {
+ dates = format.parse(date)
+ } catch (e: Exception) {
+ // TODO: handle exception
+ }
+ val cal_1 = Calendar.getInstance() // 获取当前日期
+ cal_1.time = dates
+ var months = cal_1[Calendar.MONTH]
+ var year = cal_1[Calendar.YEAR]
+ if (months == Calendar.JANUARY) {
+ year--
+ months = Calendar.DECEMBER
+ } else {
+ months--
+ }
+ cal_1[Calendar.YEAR] = year
+ cal_1[Calendar.MONTH] = months
+ val max = cal_1.getActualMaximum(Calendar.DAY_OF_MONTH)
+ val month = arrayOfNulls(max)
+ for (i in 1 until max + 1) {
+ cal_1[Calendar.DAY_OF_MONTH] = i
+ month[i - 1] = format.format(cal_1.time)
+ }
+ // 设置为1号,当前日期既为本月第一天
+
+
+// cal_1.set(Calendar.DAY_OF_MONTH,
+// cal_1.getActualMaximum(Calendar.DAY_OF_MONTH));// 设置为1号,当前日期既为本月第一天
+// month[1] = format.format(cal_1.getTime());
+ return month
+ }
+
+ fun beforeData(lastTime: String?, bleTime: String?): Boolean {
+ var isBefore = false
+ try {
+ val lastDate = format.parse(lastTime)
+ val bleDate = format.parse(bleTime)
+ isBefore = lastDate.before(bleDate)
+ } catch (e: ParseException) {
+ e.printStackTrace()
+ }
+ return isBefore
+ }
+
+ fun getCountTime(count: Int): String {
+ val format = SimpleDateFormat("HH:mm")
+ val base = "12:00"
+ var time: Long = 0
+ try {
+ time = format.parse(base).time + count * 5 * 60 * 1000L
+ } catch (e: Exception) {
+ // TODO: handle exception
+ }
+ return format.format(Date(time))
+ }
+
+ fun getShowDate(context: Context, monthString: String?): String {
+ val locale = context.resources.configuration.locale
+ val language = locale.language
+ val time = getDateLong(monthString)
+ var format = ""
+ // if (language.equals("zh")) {
+ format = "yyyy-MM"
+ // } else {
+// format = "MM-yyyy";
+// }
+ return getFormatTimeString(time, format)
+ }
+
+ private val timestamp: String
+ private get() = (System.currentTimeMillis() / 1000).toString() + ""
+}
\ No newline at end of file
diff --git a/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/smart_ring_2301/Util/EcgGraphicView.kt b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/smart_ring_2301/Util/EcgGraphicView.kt
new file mode 100644
index 00000000..8844f931
--- /dev/null
+++ b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/smart_ring_2301/Util/EcgGraphicView.kt
@@ -0,0 +1,203 @@
+package com.cloud.diplomaticquarterapp.smart_ring_2301.Util
+
+import android.app.Activity
+import android.content.Context
+import android.graphics.Canvas
+import android.graphics.Color
+import android.graphics.Paint
+import android.graphics.Path
+import android.graphics.PorterDuff
+import android.util.AttributeSet
+import android.util.DisplayMetrics
+import android.util.Log
+import android.view.SurfaceHolder
+import android.view.SurfaceView
+
+/**
+ * 自定义心电图
+ *
+ * @author manhongjie
+ */
+// public class EcgGraphicView extends View {
+class EcgGraphicView(context: Context, attrs: AttributeSet?) : SurfaceView(context, attrs), SurfaceHolder.Callback, Runnable {
+ private val mSurfaceHolder: SurfaceHolder
+ private val mPaint: Paint
+ private val blackPaint: Paint? = null
+ private val wangge_Paint: Paint? = null
+ private val pixPaint: Paint? = null
+ private var mPath = Path()
+
+ // 屏幕的长�?
+ private var SCREEN_WIDTH = 480f
+
+ //
+ private val POINT_LEN = 3000
+ private val rulerStartX = 0
+
+ // public float[] x = new float[POINT_LEN];
+ // public float[] y = new float[POINT_LEN];
+ // 数组中有3000个点
+ var x = FloatArray(POINT_LEN)
+ var y = FloatArray(POINT_LEN)
+ var scale = 0f
+ var x0 = 240f
+ var y0 = 192f
+ private val context: Context
+ var distanceY = 0f
+ private var isDrawing = false
+ private var mCanvas: Canvas? = null
+
+ // @Override
+ // public void onDraw(Canvas canvas) {
+ //
+ // float width = SCREEN_WIDTH;
+ // scale = 1;
+ //
+ // // String ruleStr = new String(scale * 10 + "mm/100mv");
+ // // 移动后的坐标
+ // makeFollowPathOne();
+ //
+ // // rulerStartX = SCREEN_WIDTH - 60;
+ // // 每列的间隔是32
+ // // int colomn = (int) (width / 32);
+ //
+ // // 画网格线行和�?
+ //
+ // // for (int i = 0; i <= 13; i++) {
+ // // canvas.drawLine(0, i * 32, width, i * 32, wangge_Paint);
+ // // }
+ // // for (int i = 0; i <= colomn; i++) {
+ // // canvas.drawLine(i * 32, 0, i * 32, 12 * 32, wangge_Paint);
+ // // }
+ //
+ // // 画标�?
+ //
+ // // canvas.drawLine(rulerStartX + 0, 192, rulerStartX + 8, 192,
+ // // pixPaint);
+ // // canvas.drawLine(rulerStartX + 24, 192, rulerStartX + 32, 192,
+ // // pixPaint);
+ // // canvas.drawLine(rulerStartX + 8, 128, rulerStartX + 24, 128,
+ // // pixPaint);
+ // // canvas.drawLine(rulerStartX + 8, 128, rulerStartX + 8, 192,
+ // // pixPaint);
+ // // canvas.drawLine(rulerStartX + 24, 128, rulerStartX + 24, 192,
+ // // pixPaint);
+ //
+ // // 文本�?25mm/s 10.0mm/100mv
+ // // canvas.drawText(ruleStr, rulerStartX - 120, 40, pixPaint);
+ // // canvas.drawText("25mm/s", rulerStartX - 200, 40, pixPaint);
+ //
+ // canvas.drawPath(mPath, mPaint);
+ // }
+ override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
+ // TODO Auto-generated method stub
+ if (MeasureSpec.getMode(widthMeasureSpec) == MeasureSpec.EXACTLY) {
+ SCREEN_WIDTH = MeasureSpec.getSize(widthMeasureSpec).toFloat()
+ }
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec)
+ }
+
+ //
+ fun makeFollowPathOne(canvas: Canvas) {
+ val height = height.toFloat()
+ val size = ecgValues.size
+ if (size == 0) return
+ mPath.reset()
+ val width = SCREEN_WIDTH / maxSize
+ val heightI = height / 16000
+ mPath.moveTo(0f, (8000 - ecgValues[0]) * heightI)
+ for (i in 1 until size - 1) {
+ canvas.drawPoint(i * width, (8000 - ecgValues[i]) * heightI, mPaint)
+ // mPath.lineTo(i * width, (8000 - ecgValues.get(i)) * heightI);
+ }
+ }
+
+ var ecgValues: MutableList = ArrayList()
+ var maxSize = 1516f
+ fun setValue(value: Int) {
+ if (ecgValues.size > maxSize) ecgValues.removeAt(0)
+ ecgValues.add(value)
+ }
+
+ fun clearData() {
+ ecgValues.clear()
+ }
+
+ fun setYDistance(distance: Float) {
+ distanceY = distance
+ }
+
+ override fun surfaceCreated(holder: SurfaceHolder) {
+ isDrawing = true
+ Thread(this).start()
+ }
+
+ override fun surfaceChanged(holder: SurfaceHolder, format: Int, width: Int, height: Int) {}
+ override fun surfaceDestroyed(holder: SurfaceHolder) {
+ isDrawing = false
+ }
+
+ override fun run() {
+ while (isDrawing) {
+ drawing()
+ }
+ }
+
+ init {
+ // TODO Auto-generated constructor stub
+ // 分辨率矩�?
+ val dm = DisplayMetrics()
+ // 取得窗口属�??
+ if (context is Activity) {
+ context.windowManager.defaultDisplay.getMetrics(dm)
+ SCREEN_WIDTH = dm.widthPixels.toFloat()
+ Log.d("EcgGraphicView", "width: $SCREEN_WIDTH")
+ }
+ mPath = Path()
+
+ // 画心电图的画�?
+ mPaint = Paint(Paint.ANTI_ALIAS_FLAG)
+ mPaint.style = Paint.Style.STROKE
+ mPaint.strokeWidth = ScreenUtils.Companion.dip2px(context, 1f).toFloat()
+ mPaint.textSize = 2f
+ mPaint.color = Color.WHITE
+ this.context = context
+ // blackPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+ // blackPaint.setStyle(Paint.Style.STROKE);
+ // blackPaint.setStrokeWidth(2);
+ // blackPaint.setTextSize(2);
+ // blackPaint.setColor(Color.RED);
+ // 网格线画�?
+ // wangge_Paint = new Paint(Paint.ANTI_ALIAS_FLAG);
+ // wangge_Paint.setStyle(Paint.Style.STROKE);
+ // wangge_Paint.setStrokeWidth(1.0f);
+ // wangge_Paint.setTextSize(0.5f);
+ // wangge_Paint.setColor(Color.parseColor("#000000"));
+
+ // pixPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+ // pixPaint.setStyle(Paint.Style.FILL);
+ // pixPaint.setStrokeWidth(2.0f);
+ // pixPaint.setTextSize(20.0f);
+ // pixPaint.setColor(Color.RED);
+ mSurfaceHolder = holder
+ mSurfaceHolder.addCallback(this)
+ }
+
+ private fun drawing() {
+ try {
+ mCanvas = mSurfaceHolder.lockCanvas()
+ if (mCanvas == null) return
+ mCanvas!!.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR)
+ makeFollowPathOne(mCanvas!!)
+ // mCanvas.drawPath(mPath, mPaint);
+ } finally {
+ if (mCanvas != null) {
+ mSurfaceHolder.unlockCanvasAndPost(mCanvas)
+ }
+ }
+ }
+
+ companion object {
+ private const val TAG = "EcgGraphicView"
+ }
+}
\ No newline at end of file
diff --git a/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/smart_ring_2301/Util/ResolveData.kt b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/smart_ring_2301/Util/ResolveData.kt
new file mode 100644
index 00000000..cb1f42e2
--- /dev/null
+++ b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/smart_ring_2301/Util/ResolveData.kt
@@ -0,0 +1,248 @@
+package com.cloud.diplomaticquarterapp.smart_ring_2301.Util
+
+import android.annotation.SuppressLint
+import android.bluetooth.BluetoothDevice
+import android.content.ContentResolver
+import android.content.ContentUris
+import android.content.Context
+import android.database.Cursor
+import android.net.Uri
+import android.os.Build
+import android.os.Environment
+import android.provider.DocumentsContract
+import android.provider.MediaStore
+import android.text.TextUtils
+import android.util.Log
+import java.io.UnsupportedEncodingException
+
+/**
+ * Created by Administrator on 2017/4/6.
+ */
+object ResolveData {
+ private const val TAG = "ResolveData"
+ private const val SHORTENED_LOCAL_NAME = 0x08
+ private const val COMPLETE_LOCAL_NAME = 0x09
+ private const val oneMinMillis = 60 * 1000L
+ var startString = " 12:00:00"
+ fun getBcd(value: String): Int {
+ return value.toInt(16)
+ }
+
+ fun decodeDeviceName(data: ByteArray): String? {
+ var name: String? = null
+ var fieldLength: Int
+ var fieldName: Int
+ val packetLength = data.size
+ var index = 0
+ while (index < packetLength) {
+ fieldLength = data[index].toInt()
+ if (fieldLength <= 0) break
+ fieldName = data[++index].toInt()
+ if (fieldName == COMPLETE_LOCAL_NAME
+ || fieldName == SHORTENED_LOCAL_NAME
+ ) {
+ name = decodeLocalName(data, index + 1, fieldLength - 1)
+ break
+ }
+ index += fieldLength - 1
+ index++
+ }
+ return name
+ }
+
+ @SuppressLint("MissingPermission")
+ @JvmStatic
+ fun decodeDeviceName(bluetoothDevice: BluetoothDevice, data: ByteArray): String? {
+ var name = bluetoothDevice.name
+ if (!TextUtils.isEmpty(name)) return name
+ var fieldLength: Int
+ var fieldName: Int
+ val packetLength = data.size
+ var index = 0
+ while (index < packetLength) {
+ fieldLength = data[index].toInt()
+ if (fieldLength <= 0) break
+ fieldName = data[++index].toInt()
+ if (fieldName == COMPLETE_LOCAL_NAME
+ || fieldName == SHORTENED_LOCAL_NAME
+ ) {
+ name = decodeLocalName(data, index + 1, fieldLength - 1)
+ break
+ }
+ index += fieldLength - 1
+ index++
+ }
+ return name
+ }
+
+ /**
+ * Decodes the local name
+ */
+ fun decodeLocalName(
+ data: ByteArray?, start: Int,
+ length: Int
+ ): String? {
+ return try {
+ kotlin.String()
+ } catch (e: UnsupportedEncodingException) {
+ Log.e(
+ "scan", "Unable to convert the complete local name to UTF-8",
+ e
+ )
+ null
+ } catch (e: IndexOutOfBoundsException) {
+ Log.e("scan", "Error when reading complete local name", e)
+ null
+ }
+ }
+
+ /**
+ * 转十六进制字符串
+ *
+ * @param data
+ * @return
+ */
+ @JvmStatic
+ fun byte2Hex(data: ByteArray?): String {
+ if (data != null && data.size > 0) {
+ val sb = StringBuilder(data.size)
+ for (tmp in data) {
+ sb.append(String.format("%02X ", tmp))
+ }
+ return sb.toString()
+ }
+ return "no data"
+ }
+
+ fun getRealFilePath(context: Context, uri: Uri?): String? {
+ if (null == uri) return null
+ val scheme = uri.scheme
+ var data: String? = null
+ if (scheme == null) data = uri.path else if (ContentResolver.SCHEME_FILE == scheme) {
+ data = uri.path
+ } else if (ContentResolver.SCHEME_CONTENT == scheme) {
+ val cursor = context.contentResolver.query(uri, arrayOf(MediaStore.Images.ImageColumns.DATA), null, null, null)
+ if (null != cursor) {
+ if (cursor.moveToFirst()) {
+ val index = cursor.getColumnIndex(MediaStore.Images.ImageColumns.DATA)
+ if (index > -1) {
+ data = cursor.getString(index)
+ }
+ }
+ cursor.close()
+ }
+ }
+ return data
+ }
+
+ /**
+ * 专为Android4.4设计的从Uri获取文件绝对路径
+ */
+ fun getPath(context: Context, uri: Uri): String? {
+
+
+ // DocumentProvider
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT && DocumentsContract.isDocumentUri(context, uri)) {
+ // ExternalStorageProvider
+ if (isExternalStorageDocument(uri)) {
+ val docId = DocumentsContract.getDocumentId(uri)
+ val split = docId.split(":".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
+ val type = split[0]
+ if ("primary".equals(type, ignoreCase = true)) {
+ return Environment.getExternalStorageDirectory().toString() + "/" + split[1]
+ }
+
+ // TODO handle non-primary volumes
+ } else if (isDownloadsDocument(uri)) {
+ val id = DocumentsContract.getDocumentId(uri)
+ val contentUri = ContentUris.withAppendedId(
+ Uri.parse("content://downloads/public_downloads"), java.lang.Long.valueOf(id)
+ )
+ return getDataColumn(context, contentUri, null, null)
+ } else if (isMediaDocument(uri)) {
+ val docId = DocumentsContract.getDocumentId(uri)
+ val split = docId.split(":".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
+ val type = split[0]
+ var contentUri: Uri? = null
+ if ("image" == type) {
+ contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI
+ } else if ("video" == type) {
+ contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI
+ } else if ("audio" == type) {
+ contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI
+ }
+ val selection = "_id=?"
+ val selectionArgs = arrayOf(split[1])
+ return getDataColumn(context, contentUri, selection, selectionArgs)
+ }
+ } else if ("content".equals(uri.scheme, ignoreCase = true)) {
+ return getDataColumn(context, uri, null, null)
+ } else if ("file".equals(uri.scheme, ignoreCase = true)) {
+ return uri.path
+ }
+ return null
+ }
+
+ /**
+ * Get the value of the data column for this Uri. This is useful for
+ * MediaStore Uris, and other file-based ContentProviders.
+ *
+ * @param context
+ * The context.
+ * @param uri
+ * The Uri to query.
+ * @param selection
+ * (Optional) Filter used in the query.
+ * @param selectionArgs
+ * (Optional) Selection arguments used in the query.
+ * @return The value of the _data column, which is typically a file path.
+ */
+ fun getDataColumn(
+ context: Context, uri: Uri?, selection: String?,
+ selectionArgs: Array?
+ ): String? {
+ var cursor: Cursor? = null
+ val column = "_data"
+ val projection = arrayOf(column)
+ try {
+ cursor = context.contentResolver.query(
+ uri!!, projection, selection, selectionArgs,
+ null
+ )
+ if (cursor != null && cursor.moveToFirst()) {
+ val column_index = cursor.getColumnIndexOrThrow(column)
+ return cursor.getString(column_index)
+ }
+ } finally {
+ cursor?.close()
+ }
+ return null
+ }
+
+ /**
+ * @param uri
+ * The Uri to check.
+ * @return Whether the Uri authority is ExternalStorageProvider.
+ */
+ fun isExternalStorageDocument(uri: Uri): Boolean {
+ return "com.android.externalstorage.documents" == uri.authority
+ }
+
+ /**
+ * @param uri
+ * The Uri to check.
+ * @return Whether the Uri authority is DownloadsProvider.
+ */
+ fun isDownloadsDocument(uri: Uri): Boolean {
+ return "com.android.providers.downloads.documents" == uri.authority
+ }
+
+ /**
+ * @param uri
+ * The Uri to check.
+ * @return Whether the Uri authority is MediaProvider.
+ */
+ fun isMediaDocument(uri: Uri): Boolean {
+ return "com.android.providers.media.documents" == uri.authority
+ }
+}
\ No newline at end of file
diff --git a/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/smart_ring_2301/Util/RxBus.kt b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/smart_ring_2301/Util/RxBus.kt
new file mode 100644
index 00000000..c9f4cb11
--- /dev/null
+++ b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/smart_ring_2301/Util/RxBus.kt
@@ -0,0 +1,34 @@
+package com.cloud.diplomaticquarterapp.smart_ring_2301.Util
+
+import io.reactivex.rxjava3.core.Observable
+import io.reactivex.rxjava3.processors.FlowableProcessor
+import io.reactivex.rxjava3.processors.PublishProcessor
+import io.reactivex.rxjava3.subjects.PublishSubject
+import io.reactivex.rxjava3.subjects.Subject
+
+
+/**
+ * Created by zhuge on 2017/1/19 0019.
+ */
+class RxBus private constructor() {
+ private val bus: Subject
+ private val mBus: FlowableProcessor
+
+ init {
+ mBus = PublishProcessor.create().toSerialized()
+ bus = PublishSubject.create().toSerialized()
+ }
+
+ fun post(`object`: Any) {
+ bus.onNext(`object`)
+ }
+
+ fun toObservable(eventType: Class): Observable {
+ return bus.ofType(eventType)
+ }
+
+
+ companion object {
+ val instance = RxBus()
+ }
+}
\ No newline at end of file
diff --git a/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/smart_ring_2301/Util/SchedulersTransformer.kt b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/smart_ring_2301/Util/SchedulersTransformer.kt
new file mode 100644
index 00000000..c78844af
--- /dev/null
+++ b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/smart_ring_2301/Util/SchedulersTransformer.kt
@@ -0,0 +1,17 @@
+package com.cloud.diplomaticquarterapp.smart_ring_2301.Util
+
+import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
+import io.reactivex.rxjava3.core.ObservableTransformer
+import io.reactivex.rxjava3.schedulers.Schedulers
+
+
+/**
+ * Created by Administrator on 2017/4/26.
+ */
+object SchedulersTransformer {
+ private const val TAG = "SchedulersTransformer"
+ fun io2MainObservable(): ObservableTransformer {
+ return ObservableTransformer { upstream -> upstream.subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()) }
+ }
+
+}
\ No newline at end of file
diff --git a/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/smart_ring_2301/Util/ScreenUtils.kt b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/smart_ring_2301/Util/ScreenUtils.kt
new file mode 100644
index 00000000..4d6d8dcf
--- /dev/null
+++ b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/smart_ring_2301/Util/ScreenUtils.kt
@@ -0,0 +1,120 @@
+package com.cloud.diplomaticquarterapp.smart_ring_2301.Util
+
+import android.app.Activity
+import android.content.Context
+import android.graphics.Bitmap
+import android.graphics.Paint
+import android.util.DisplayMetrics
+import android.view.WindowManager
+
+/**
+ * 获得屏幕相关的辅助类
+ *
+ *
+ *
+ */
+class ScreenUtils private constructor() {
+ init {
+ /* cannot be instantiated */
+ throw UnsupportedOperationException("cannot be instantiated")
+ }
+
+ companion object {
+ /**
+ * 获得屏幕高度
+ *
+ * @param context
+ * @return
+ */
+ fun getScreenWidth(context: Context): Int {
+ val wm = context
+ .getSystemService(Context.WINDOW_SERVICE) as WindowManager
+ val outMetrics = DisplayMetrics()
+ wm.defaultDisplay.getMetrics(outMetrics)
+ return outMetrics.widthPixels
+ }
+
+ /**
+ * 获得屏幕宽度
+ *
+ * @param context
+ * @return
+ */
+ fun getScreenHeight(context: Context): Int {
+ val wm = context
+ .getSystemService(Context.WINDOW_SERVICE) as WindowManager
+ val outMetrics = DisplayMetrics()
+ wm.defaultDisplay.getMetrics(outMetrics)
+ return outMetrics.heightPixels
+ }
+
+ /**
+ * 获得状态栏的高度
+ *
+ * @param context
+ * @return
+ */
+ fun getStatusHeight(context: Context): Int {
+ var statusHeight = -1
+ try {
+ val clazz = Class.forName("com.android.internal.R\$dimen")
+ val `object` = clazz.newInstance()
+ val height = clazz.getField("status_bar_height")[`object`].toString().toInt()
+ statusHeight = context.resources.getDimensionPixelSize(height)
+ } catch (e: Exception) {
+ e.printStackTrace()
+ }
+ return statusHeight
+ }
+
+ /**
+ * 获取当前屏幕截图,包含状态栏
+ *
+ * @param activity
+ * @return
+ */
+ fun snapShotWithStatusBar(activity: Activity): Bitmap? {
+ val view = activity.window.decorView
+ view.isDrawingCacheEnabled = true
+ view.buildDrawingCache()
+ val bmp = view.drawingCache
+ val width = getScreenWidth(activity)
+ val height = getScreenHeight(activity)
+ var bp: Bitmap? = null
+ bp = Bitmap.createBitmap(bmp, 0, 0, width, height)
+ view.destroyDrawingCache()
+ return bp
+ }
+ /**
+ * 获取当前屏幕截图,不包含状态栏
+ *
+ * @param activity
+ * @return
+ */
+ /**
+ * 根据手机的分辨率从 dp 的单位 转成为 px(像素)
+ */
+ fun dip2px(context: Context, dpValue: Float): Int {
+ val scale = context.resources.displayMetrics.density
+ return (dpValue * scale + 0.5f).toInt()
+ }
+
+ /**
+ * 根据手机的分辨率从 px(像素) 的单位 转成为 dp
+ */
+ fun px2dip(context: Context, pxValue: Float): Int {
+ val scale = context.resources.displayMetrics.density
+ return (pxValue / scale + 0.5f).toInt()
+ }
+
+ fun sp2px(context: Context, spValue: Float): Int {
+ val scale = context.resources.displayMetrics.density
+ return (scale * spValue).toInt()
+ }
+
+ fun getFontHeight(paint: Paint): Float {
+ val fm = paint.fontMetrics
+ return fm.descent - fm.ascent
+ }
+ }
+}
\ No newline at end of file
diff --git a/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/smart_ring_2301/Util/SharedPreferenceUtils.kt b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/smart_ring_2301/Util/SharedPreferenceUtils.kt
new file mode 100644
index 00000000..6016173c
--- /dev/null
+++ b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/smart_ring_2301/Util/SharedPreferenceUtils.kt
@@ -0,0 +1,93 @@
+package com.cloud.diplomaticquarterapp.smart_ring_2301.Util
+
+import android.app.Activity
+import android.content.Context
+import android.content.SharedPreferences
+
+object SharedPreferenceUtils {
+ const val KEY_REMIND_PSD = "REMIND_PSD"
+ const val KEY_account_type = "KEY_account_type"
+ const val KEY_countryCode = "KEY_countryCode"
+ const val KEY_TimeZone = "KEY_TimeZone"
+ const val KEY_DFU_ADDRESS = "KEY_DFU_ADDRESS"
+ const val KEY_Exercise_Mode = "KEY_Exercise_Mode"
+ const val KEY_Enable_ActivityClock = "KEY_Enable_ActivityClock"
+ const val KEY_UNIT_WEIGHT = "KEY_UNIT_WEIGHT"
+ const val KEY_UNIT_HEIGHT = "KEY_UNIT_HEIGHT"
+ const val KEY_RES_FILEPATH = "KEY_RES_FILEPATH"
+ const val KEY_Is_First = "KEY_Is_First"
+ const val KEY_From_Login = "KEY_From_Login"
+ const val KEY_PHONE = "PHONE"
+ const val KEY_UID = "uid"
+ const val KEY_PSD = "PSD"
+ const val KEY_ADDRESS = "ADDRESS"
+ const val KEY_Language = "Language"
+ const val KEY_LOGIN = "lOGIN"
+ const val KEY_NICKNAME = "nickname"
+ private const val spName = "jstyle_sleep"
+ const val KEY_AUTH_USERID = "KEY_AUTH_USERID"
+ const val KEY_highHeart = "highHeart"
+ const val KEY_highBrEATH = "highBrEATH"
+ const val KEY_lowHeart = "lowHeart"
+ const val KEY_lowBreath = "lowBreath"
+ const val KEY_deviceName = "deviceName"
+ const val KEY_PLAN_ON = "PLAN_ON"
+ const val KEY_PLAN_first = "PLAN_first"
+ const val KEY_Sleep_goal = "KEY_Sleep_goal"
+ const val KEY_Step_goal = "KEY_Step_goal"
+ const val KEY_deviceType = "KEY_deviceType"
+ var sharedPreferences: SharedPreferences? = null
+ private set
+ const val DefaultCountryCode = "0086-"
+ fun init(context: Context) {
+ sharedPreferences = context.getSharedPreferences(spName, Activity.MODE_PRIVATE)
+ }
+
+ fun setSpBoolean(name: String?, bool: Boolean) {
+ sharedPreferences!!.edit().putBoolean(name, bool).commit()
+ }
+
+ fun getSpBoolean(name: String?): Boolean {
+ return sharedPreferences!!.getBoolean(name, false)
+ }
+
+ fun getBoolean(name: String?, defaultValue: Boolean): Boolean {
+ return sharedPreferences!!.getBoolean(name, defaultValue)
+ }
+
+ fun setSpString(name: String?, value: String?) {
+ sharedPreferences!!.edit().putString(name, value).commit()
+ }
+
+ fun getSpString(name: String?): String? {
+ return getString(name, null)
+ }
+
+ fun getString(name: String?, defaultValue: String?): String? {
+ return sharedPreferences!!.getString(name, defaultValue)
+ }
+
+ fun setSpInteger(name: String?, value: Int) {
+ sharedPreferences!!.edit().putInt(name, value).commit()
+ }
+
+ fun getInteger(name: String?, defaultValue: Int): Int {
+ return sharedPreferences!!.getInt(name, defaultValue)
+ }
+
+ fun getSpInteger(name: String?): Int {
+ return getInteger(name, -1)
+ }
+
+ fun setSpFloat(name: String?, value: Float) {
+ sharedPreferences!!.edit().putFloat(name, value).commit()
+ }
+
+ fun getSpFloat(name: String?): Float {
+ return getFloat(name, 0.0f)
+ }
+
+ fun getFloat(name: String?, defaultValue: Float): Float {
+ return sharedPreferences!!.getFloat(name, defaultValue)
+ }
+}
\ No newline at end of file
diff --git a/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/utils/ByteArray.kt b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/utils/ByteArray.kt
new file mode 100644
index 00000000..2a8225eb
--- /dev/null
+++ b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/utils/ByteArray.kt
@@ -0,0 +1,34 @@
+package com.cloud.diplomaticquarterapp.utils
+
+
+fun add(ori: ByteArray?, add: ByteArray): ByteArray {
+ if (ori == null) {
+ return add
+ }
+
+ val new: ByteArray = ByteArray(ori.size + add.size)
+ for ((index, value) in ori.withIndex()) {
+ new[index] = value
+ }
+
+ for ((index, value) in add.withIndex()) {
+ new[index + ori.size] = value
+ }
+
+ return new
+}
+
+
+fun toUInt(bytes: ByteArray): Int {
+ var result = 0
+ for ((i, v) in bytes.withIndex()) {
+ result += v.unsigned().shl(i * 8)
+ }
+ return result
+}
+
+
+fun Byte.unsigned(): Int = when {
+ (toInt() < 0) -> 255 + toInt() + 1
+ else -> toInt()
+}
\ No newline at end of file
diff --git a/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/utils/CRCUtils.java b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/utils/CRCUtils.java
new file mode 100644
index 00000000..981c48fb
--- /dev/null
+++ b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/utils/CRCUtils.java
@@ -0,0 +1,58 @@
+package com.cloud.diplomaticquarterapp.utils;
+
+
+/**
+ * Tools used to generate CRC8 code
+ *
+ * @author zouhao
+ */
+public class CRCUtils {
+
+ /**
+ * CRC8 code table
+ */
+ private static final char[] Table_CRC8 = {0x00, 0x07, 0x0E, 0x09, 0x1C,
+ 0x1B, 0x12, 0x15, 0x38, 0x3F, 0x36, 0x31, 0x24, 0x23, 0x2A, 0x2D,
+ 0x70, 0x77, 0x7E, 0x79, 0x6C, 0x6B, 0x62, 0x65, 0x48, 0x4F, 0x46,
+ 0x41, 0x54, 0x53, 0x5A, 0x5D, 0xE0, 0xE7, 0xEE, 0xE9, 0xFC, 0xFB,
+ 0xF2, 0xF5, 0xD8, 0xDF, 0xD6, 0xD1, 0xC4, 0xC3, 0xCA, 0xCD, 0x90,
+ 0x97, 0x9E, 0x99, 0x8C, 0x8B, 0x82, 0x85, 0xA8, 0xAF, 0xA6, 0xA1,
+ 0xB4, 0xB3, 0xBA, 0xBD, 0xC7, 0xC0, 0xC9, 0xCE, 0xDB, 0xDC, 0xD5,
+ 0xD2, 0xFF, 0xF8, 0xF1, 0xF6, 0xE3, 0xE4, 0xED, 0xEA, 0xB7, 0xB0,
+ 0xB9, 0xBE, 0xAB, 0xAC, 0xA5, 0xA2, 0x8F, 0x88, 0x81, 0x86, 0x93,
+ 0x94, 0x9D, 0x9A, 0x27, 0x20, 0x29, 0x2E, 0x3B, 0x3C, 0x35, 0x32,
+ 0x1F, 0x18, 0x11, 0x16, 0x03, 0x04, 0x0D, 0x0A, 0x57, 0x50, 0x59,
+ 0x5E, 0x4B, 0x4C, 0x45, 0x42, 0x6F, 0x68, 0x61, 0x66, 0x73, 0x74,
+ 0x7D, 0x7A, 0x89, 0x8E, 0x87, 0x80, 0x95, 0x92, 0x9B, 0x9C, 0xB1,
+ 0xB6, 0xBF, 0xB8, 0xAD, 0xAA, 0xA3, 0xA4, 0xF9, 0xFE, 0xF7, 0xF0,
+ 0xE5, 0xE2, 0xEB, 0xEC, 0xC1, 0xC6, 0xCF, 0xC8, 0xDD, 0xDA, 0xD3,
+ 0xD4, 0x69, 0x6E, 0x67, 0x60, 0x75, 0x72, 0x7B, 0x7C, 0x51, 0x56,
+ 0x5F, 0x58, 0x4D, 0x4A, 0x43, 0x44, 0x19, 0x1E, 0x17, 0x10, 0x05,
+ 0x02, 0x0B, 0x0C, 0x21, 0x26, 0x2F, 0x28, 0x3D, 0x3A, 0x33, 0x34,
+ 0x4E, 0x49, 0x40, 0x47, 0x52, 0x55, 0x5C, 0x5B, 0x76, 0x71, 0x78,
+ 0x7F, 0x6A, 0x6D, 0x64, 0x63, 0x3E, 0x39, 0x30, 0x37, 0x22, 0x25,
+ 0x2C, 0x2B, 0x06, 0x01, 0x08, 0x0F, 0x1A, 0x1D, 0x14, 0x13, 0xAE,
+ 0xA9, 0xA0, 0xA7, 0xB2, 0xB5, 0xBC, 0xBB, 0x96, 0x91, 0x98, 0x9F,
+ 0x8A, 0x8D, 0x84, 0x83, 0xDE, 0xD9, 0xD0, 0xD7, 0xC2, 0xC5, 0xCC,
+ 0xCB, 0xE6, 0xE1, 0xE8, 0xEF, 0xFA, 0xFD, 0xF4, 0xF3};
+
+ /**
+ * Generate CRC8 code
+ *
+ * @param buf Data buffer
+ * @return CRC8 code, return 0 when the parameter is error
+ */
+ public static byte calCRC8(byte[] buf) {
+ if (buf == null || buf.length == 0) {
+ return 0;
+ }
+
+ byte crc = 0;
+
+ for (int i = 0; i < buf.length - 1; i++) {
+ crc = (byte) Table_CRC8[0x00ff & (crc ^ (buf[i]))];
+ }
+ return crc;
+ }
+
+}
diff --git a/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/utils/Constant.java b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/utils/Constant.java
new file mode 100644
index 00000000..0a92c478
--- /dev/null
+++ b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/utils/Constant.java
@@ -0,0 +1,23 @@
+package com.cloud.diplomaticquarterapp.utils;
+
+import android.content.Context;
+
+import java.io.File;
+
+public class Constant {
+ public final static String[] EcgWay = {"Hand-Hand", "Hand-Chest", "1-Lead", "2-Lead"};
+ public final static String[] OxyWay = {"Internal", "External", ""};
+ public final static String[] TmpWay = {"Body", "Thing", ""};
+ public static String filePath;
+
+ public static String getPathX(String s) {
+ return filePath + s;
+ }
+
+ public static void initVar(Context context) {
+ File[] fs = context.getExternalFilesDirs(null);
+ if (fs != null && fs.length >= 1) {
+ filePath = fs[0].getAbsolutePath() + "/";
+ }
+ }
+}
diff --git a/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/utils/FlutterText.kt b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/utils/FlutterText.kt
index 1a650074..977defbb 100644
--- a/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/utils/FlutterText.kt
+++ b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/utils/FlutterText.kt
@@ -1,7 +1,6 @@
package com.ejada.hmg.utils
import io.flutter.plugin.common.MethodChannel
-import io.flutter.plugin.common.MethodChannel.Result
class FlutterText{
diff --git a/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/utils/HMGUtils.kt b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/utils/HMGUtils.kt
index 7fbf859a..9b6f5b79 100644
--- a/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/utils/HMGUtils.kt
+++ b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/utils/HMGUtils.kt
@@ -1,5 +1,6 @@
package com.ejada.hmg.utils
+import android.annotation.SuppressLint
import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.PendingIntent
@@ -13,10 +14,8 @@ import android.widget.Toast
import androidx.annotation.Nullable
import androidx.core.app.NotificationCompat
import androidx.core.app.TaskStackBuilder
-import com.ejada.hmg.BuildConfig
-import com.ejada.hmg.MainActivity
+import com.cloud.diplomaticquarterapp.MainActivity
import com.ejada.hmg.R
-import com.ejada.hmg.geofence.GeoZoneModel
import com.github.kittinunf.fuel.core.extensions.jsonBody
import com.github.kittinunf.fuel.httpPost
import com.google.gson.Gson
@@ -29,22 +28,24 @@ import org.json.JSONObject
import java.text.SimpleDateFormat
import java.util.*
import kotlin.concurrent.timerTask
+import com.ejada.hmg.BuildConfig
class HMGUtils {
- companion object{
+ companion object {
private lateinit var platformChannel: MethodChannel
- fun getPlatformChannel():MethodChannel{
+ fun getPlatformChannel(): MethodChannel {
return platformChannel
}
- fun setPlatformChannel(channel: MethodChannel){
+
+ fun setPlatformChannel(channel: MethodChannel) {
platformChannel = channel
}
- fun timer(delay: Long, repeat: Boolean, tick: (Timer) -> Unit) : Timer{
+ fun timer(delay: Long, repeat: Boolean, tick: (Timer) -> Unit): Timer {
val timer = Timer()
- if(repeat)
+ if (repeat)
timer.schedule(timerTask {
tick(timer)
}, delay, delay)
@@ -56,42 +57,44 @@ class HMGUtils {
return timer
}
- fun popMessage(context: MainActivity, message: String){
+ fun popMessage(context: MainActivity, message: String) {
context.runOnUiThread {
Toast.makeText(context, message, Toast.LENGTH_LONG).show()
}
}
- fun popFlutterText(context: MainActivity, key: String){
+ fun popFlutterText(context: MainActivity, key: String) {
context.runOnUiThread {
- FlutterText.with(key){
+ FlutterText.with(key) {
Toast.makeText(context, it, Toast.LENGTH_LONG).show()
}
}
}
- fun getLanguageCode(context: Context) : Int {
+ fun getLanguageCode(context: Context): Int {
val pref = context.getSharedPreferences(PREFS_STORAGE, Context.MODE_PRIVATE)
val lang = pref.getString(PREF_KEY_LANGUAGE, "ar")
return if (lang == "ar") 2 else 1
}
- fun defaultHTTPParams(context: Context) : Map{
+ fun defaultHTTPParams(context: Context): Map {
return mapOf(
- "ZipCode" to "966",
- "VersionID" to 5.8,
- "Channel" to 3,
- "LanguageID" to getLanguageCode(context),
- "IPAdress" to "10.20.10.20",
- "generalid" to "Cs2020@2016$2958",
- "PatientOutSA" to 0,
- "SessionID" to null,
- "isDentalAllowedBackend" to false,
- "DeviceTypeID" to 2)
+ "ZipCode" to "966",
+ "VersionID" to 5.8,
+ "Channel" to 3,
+ "LanguageID" to getLanguageCode(context),
+ "IPAdress" to "10.20.10.20",
+ "generalid" to "Cs2020@2016$2958",
+ "PatientOutSA" to 0,
+ "SessionID" to null,
+ "isDentalAllowedBackend" to false,
+ "DeviceTypeID" to 2
+ )
}
- fun scheduleJob(context: Context, pendingIntentClassType:Class, jobId:Int, intervalDuration:String, deadlineMillis:Long = (30 * 1000)) { // default deadline: 30 Seconds
+ @SuppressLint("MissingPermission")
+ fun scheduleJob(context: Context, pendingIntentClassType: Class, jobId: Int, intervalDuration: String, deadlineMillis: Long = (30 * 1000)) { // default deadline: 30 Seconds
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
val jobScheduler: JobScheduler = context.getSystemService(JobScheduler::class.java)
@@ -100,17 +103,22 @@ class HMGUtils {
builder.setPersisted(true)
builder.setBackoffCriteria(30000, JobInfo.BACKOFF_POLICY_LINEAR)
- val intervalMillis = timeToMillis(intervalDuration,"HH:mm:ss")
+ val intervalMillis = timeToMillis(intervalDuration, "HH:mm:ss")
builder.setMinimumLatency(intervalMillis) // wait at least
builder.setOverrideDeadline((intervalMillis + deadlineMillis)) // maximum delay
- if (jobScheduler.schedule(builder.build()) == JobScheduler.RESULT_SUCCESS){
- Logs.save(context,"ScheduleJob", "${pendingIntentClassType.simpleName}: Job scheduled to trigger after duration $intervalDuration >> HH:mm:ss --('MinimumLatency:$intervalMillis Deadline:${(intervalMillis + deadlineMillis)}')--",Logs.STATUS.SUCCESS)
- }else{
- Logs.save(context,"ScheduleJob", "${pendingIntentClassType.simpleName}: Failed to scheduled Job",Logs.STATUS.ERROR)
+ if (jobScheduler.schedule(builder.build()) == JobScheduler.RESULT_SUCCESS) {
+ Logs.save(
+ context,
+ "ScheduleJob",
+ "${pendingIntentClassType.simpleName}: Job scheduled to trigger after duration $intervalDuration >> HH:mm:ss --('MinimumLatency:$intervalMillis Deadline:${(intervalMillis + deadlineMillis)}')--",
+ Logs.STATUS.SUCCESS
+ )
+ } else {
+ Logs.save(context, "ScheduleJob", "${pendingIntentClassType.simpleName}: Failed to scheduled Job", Logs.STATUS.ERROR)
}
} else {
- Logs.save(context,"ScheduleJob", "${pendingIntentClassType.simpleName}: Failed to scheduled Job on VERSION.SDK_INT < ${android.os.Build.VERSION_CODES.M}",Logs.STATUS.ERROR)
+ Logs.save(context, "ScheduleJob", "${pendingIntentClassType.simpleName}: Failed to scheduled Job on VERSION.SDK_INT < ${android.os.Build.VERSION_CODES.M}", Logs.STATUS.ERROR)
}
}
@@ -122,7 +130,7 @@ class HMGUtils {
private const val NOTIFICATION_CHANNEL_ID = BuildConfig.APPLICATION_ID + ".channel"
-fun timeToMillis(time:String, format:String):Long{
+fun timeToMillis(time: String, format: String): Long {
val sdf = SimpleDateFormat(format, Locale.US)
val millis = sdf.parse(time).time + TimeZone.getDefault().rawOffset
return millis
@@ -132,11 +140,14 @@ fun sendNotification(context: Context, title: String, @Nullable subtitle: String
val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O
- && notificationManager.getNotificationChannel(NOTIFICATION_CHANNEL_ID) == null) {
+ && notificationManager.getNotificationChannel(NOTIFICATION_CHANNEL_ID) == null
+ ) {
val name = context.getString(R.string.app_name)
- val channel = NotificationChannel(NOTIFICATION_CHANNEL_ID,
- name,
- NotificationManager.IMPORTANCE_DEFAULT)
+ val channel = NotificationChannel(
+ NOTIFICATION_CHANNEL_ID,
+ name,
+ NotificationManager.IMPORTANCE_DEFAULT
+ )
notificationManager.createNotificationChannel(channel)
}
@@ -144,15 +155,15 @@ fun sendNotification(context: Context, title: String, @Nullable subtitle: String
val intent = Intent(context, MainActivity::class.java)
val stackBuilder = TaskStackBuilder.create(context)
- .addParentStack(MainActivity::class.java)
- .addNextIntent(intent)
+ .addParentStack(MainActivity::class.java)
+ .addNextIntent(intent)
val notificationPendingIntent = stackBuilder.getPendingIntent(getUniqueId(), PendingIntent.FLAG_UPDATE_CURRENT)
val notification = NotificationCompat.Builder(context, NOTIFICATION_CHANNEL_ID)
- .setSmallIcon(R.mipmap.ic_launcher)
- .setContentIntent(notificationPendingIntent)
- .setAutoCancel(true)
- .setContentTitle(title)
+ .setSmallIcon(R.mipmap.ic_launcher)
+ .setContentIntent(notificationPendingIntent)
+ .setAutoCancel(true)
+ .setContentTitle(title)
subtitle.let { notification.setContentText(it) }
message.let { notification.setSubText(it) }
@@ -167,59 +178,63 @@ fun getUniqueId() = ((System.currentTimeMillis() % 10000).toInt())
object DateUtils {
@JvmStatic
- fun dateTimeNow() : String {
+ fun dateTimeNow(): String {
val format = SimpleDateFormat("dd-MMM-yyy hh:mm:ss")
return format.format(Date())
}
}
fun isJSONValid(jsonString: String?): Boolean {
- try { JSONObject(jsonString) } catch (ex: JSONException) {
- try { JSONArray(jsonString) } catch (ex1: JSONException) {
+ try {
+ JSONObject(jsonString)
+ } catch (ex: JSONException) {
+ try {
+ JSONArray(jsonString)
+ } catch (ex1: JSONException) {
return false
}
}
return true
}
-fun saveLog(context: Context, tag: String, message: String){
+fun saveLog(context: Context, tag: String, message: String) {
val pref = context.getSharedPreferences(PREFS_STORAGE, Context.MODE_PRIVATE)
var logs = pref.getString("LOGS", "")
logs += "$tag -> $message \n"
pref.edit().putString("LOGS", logs).apply();
}
-fun getLogs(context: Context) : String?{
+fun getLogs(context: Context): String? {
val pref = context.getSharedPreferences(PREFS_STORAGE, Context.MODE_PRIVATE)
return pref.getString("LOGS", "")
}
-class HTTPResponse(data: T){
- final var data:T = data
+class HTTPResponse(data: T) {
+ final var data: T = data
}
-fun httpPost(url: String, body: Map, onSuccess: (response: HTTPResponse) -> Unit, onError: (error: Exception) -> Unit){
+fun httpPost(url: String, body: Map, onSuccess: (response: HTTPResponse) -> Unit, onError: (error: Exception) -> Unit) {
val gson = Gson()
- val type = object : TypeToken() {}.type
+ val type = object : TypeToken() {}.type
val jsonBody = gson.toJson(body)
url.httpPost()
- .jsonBody(jsonBody, Charsets.UTF_8)
- .timeout(10000)
- .header("Content-Type", "application/json")
- .header("Allow", "*/*")
- .response { request, response, result ->
- result.doAsyncResult { }
- result.fold({ data ->
- val dataString = String(data)
- if (isJSONValid(dataString)) {
- val responseData = gson.fromJson(dataString, type)
- onSuccess(HTTPResponse(responseData))
- } else {
- onError(Exception("Invalid response from server (Not a valid JSON)"))
- }
- }, {
- onError(it)
- })
+ .jsonBody(jsonBody, Charsets.UTF_8)
+ .timeout(10000)
+ .header("Content-Type", "application/json")
+ .header("Allow", "*/*")
+ .response { request, response, result ->
+ result.doAsyncResult { }
+ result.fold({ data ->
+ val dataString = String(data)
+ if (isJSONValid(dataString)) {
+ val responseData = gson.fromJson(dataString, type)
+ onSuccess(HTTPResponse(responseData))
+ } else {
+ onError(Exception("Invalid response from server (Not a valid JSON)"))
+ }
+ }, {
+ onError(it)
+ })
- }
+ }
}
\ No newline at end of file
diff --git a/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/utils/Logs.kt b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/utils/Logs.kt
index d9dc209e..db4f911a 100644
--- a/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/utils/Logs.kt
+++ b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/utils/Logs.kt
@@ -2,128 +2,128 @@ package com.ejada.hmg.utils
import android.content.Context
import android.content.SharedPreferences
-import android.os.Build
import com.ejada.hmg.BuildConfig
import com.google.gson.Gson
class Logs {
- enum class STATUS{
+ enum class STATUS {
SUCCESS,
ERROR;
}
- class GeofenceEvent{
- companion object{
- fun save(context: Context, tag:String, message:String, status:Logs.STATUS = STATUS.SUCCESS){
- Logs.Common.save(context,"GeofenceEvent", tag, message, status)
+
+ class GeofenceEvent {
+ companion object {
+ fun save(context: Context, tag: String, message: String, status: Logs.STATUS = STATUS.SUCCESS) {
+ Logs.Common.save(context, "GeofenceEvent", tag, message, status)
}
- fun list(context: Context, tag:String? = null, status:Logs.STATUS? = null):List{
- return Logs.Common.list(context,"GeofenceEvent", tag, status)
+ fun list(context: Context, tag: String? = null, status: Logs.STATUS? = null): List {
+ return Logs.Common.list(context, "GeofenceEvent", tag, status)
}
- fun raw(context: Context):String{
- return Logs.Common.raw(context,"GeofenceEvent")
+ fun raw(context: Context): String {
+ return Logs.Common.raw(context, "GeofenceEvent")
}
}
}
- class RegisterGeofence{
- companion object{
- fun save(context: Context, tag:String, message:String, status:Logs.STATUS = STATUS.SUCCESS){
- Logs.Common.save(context,"RegisterGeofence", tag, message, status)
+ class RegisterGeofence {
+ companion object {
+ fun save(context: Context, tag: String, message: String, status: Logs.STATUS = STATUS.SUCCESS) {
+ Logs.Common.save(context, "RegisterGeofence", tag, message, status)
}
- fun list(context: Context, tag:String? = null, status:Logs.STATUS? = null):List{
- return Logs.Common.list(context,"RegisterGeofence", tag, status)
+ fun list(context: Context, tag: String? = null, status: Logs.STATUS? = null): List {
+ return Logs.Common.list(context, "RegisterGeofence", tag, status)
}
- fun raw(context: Context):String{
- return Logs.Common.raw(context,"RegisterGeofence");
+ fun raw(context: Context): String {
+ return Logs.Common.raw(context, "RegisterGeofence");
}
}
}
- companion object{
- private var pref:SharedPreferences? = null
- fun save(context: Context, tag:String, message:String, status:Logs.STATUS = STATUS.SUCCESS){
- Logs.Common.save(context,"Logs", tag, message, status)
+ companion object {
+ private var pref: SharedPreferences? = null
+ fun save(context: Context, tag: String, message: String, status: Logs.STATUS = STATUS.SUCCESS) {
+ Logs.Common.save(context, "Logs", tag, message, status)
}
- fun list(context: Context, tag:String? = null, status:Logs.STATUS? = null):List{
- return Logs.Common.list(context,"Logs", tag, status)
+ fun list(context: Context, tag: String? = null, status: Logs.STATUS? = null): List {
+ return Logs.Common.list(context, "Logs", tag, status)
}
- fun raw(context: Context):String{
- return Logs.Common.raw(context,"Logs");
+ fun raw(context: Context): String {
+ return Logs.Common.raw(context, "Logs");
}
- private fun storage(context: Context):SharedPreferences{
- if(pref == null) {
+ private fun storage(context: Context): SharedPreferences {
+ if (pref == null) {
pref = context.getSharedPreferences(PREFS_STORAGE, Context.MODE_PRIVATE)
}
return pref!!
}
}
- private class Common{
- companion object{
+ private class Common {
+ companion object {
private val gson = Gson()
- fun save(context: Context, key:String, tag:String, message:String, status:Logs.STATUS = STATUS.SUCCESS){
- if(!BuildConfig.DEBUG)
+ fun save(context: Context, key: String, tag: String, message: String, status: Logs.STATUS = STATUS.SUCCESS) {
+ if (!BuildConfig.DEBUG)
return
-
+
val pref = Logs.storage(context)
- val string = pref.getString(key,"{}")
- val json = gson.fromJson(string,LogsContainerModel::class.java)
+ val string = pref.getString(key, "{}")
+ val json = gson.fromJson(string, LogsContainerModel::class.java)
json.add(
- LogModel().apply {
- this.TAG = tag
- this.MESSAGE = message
- this.STATUS = status.name
- this.DATE = DateUtils.dateTimeNow()
- }
+ LogModel().apply {
+ this.TAG = tag
+ this.MESSAGE = message
+ this.STATUS = status.name
+ this.DATE = DateUtils.dateTimeNow()
+ }
)
- pref.edit().putString(key,gson.toJson(json)).apply()
+ pref.edit().putString(key, gson.toJson(json)).apply()
}
- fun list(context: Context, key:String, tag:String? = null, status:Logs.STATUS? = null):List{
+ fun list(context: Context, key: String, tag: String? = null, status: Logs.STATUS? = null): List {
val pref = Logs.storage(context)
- val string = pref.getString(key,"{}")
- val json = gson.fromJson(string,LogsContainerModel::class.java)
- if(tag == null && status == null) {
+ val string = pref.getString(key, "{}")
+ val json = gson.fromJson(string, LogsContainerModel::class.java)
+ if (tag == null && status == null) {
return json.LOGS
- }else if(tag != null && status != null){
+ } else if (tag != null && status != null) {
return json.LOGS.filter { (it.TAG == tag && it.STATUS == status.name) }
- }else if(tag != null){
+ } else if (tag != null) {
return json.LOGS.filter { (it.TAG == tag) }
- }else if(status != null){
+ } else if (status != null) {
return json.LOGS.filter { (it.STATUS == status.name) }
}
return listOf()
}
- fun raw(context: Context, key:String):String{
+ fun raw(context: Context, key: String): String {
val pref = Logs.storage(context)
- val string = pref.getString(key,"{}")
+ val string = pref.getString(key, "{}")
return string!!
}
}
}
- class LogModel{
- lateinit var TAG:String
- lateinit var MESSAGE:String
- lateinit var STATUS:String
- lateinit var DATE:String
+ class LogModel {
+ lateinit var TAG: String
+ lateinit var MESSAGE: String
+ lateinit var STATUS: String
+ lateinit var DATE: String
- companion object{
- fun with(tag:String, message:String, status:String):LogModel{
+ companion object {
+ fun with(tag: String, message: String, status: String): LogModel {
return LogModel().apply {
this.TAG = tag
this.MESSAGE = message
@@ -134,9 +134,9 @@ class Logs {
}
}
- class LogsContainerModel{
+ class LogsContainerModel {
var LOGS = mutableListOf()
- fun add(log:LogModel){
+ fun add(log: LogModel) {
LOGS.add(log)
}
}
diff --git a/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/utils/OpenTokPlatformBridge.kt b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/utils/OpenTokPlatformBridge.kt
index ebb04456..84e27e29 100644
--- a/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/utils/OpenTokPlatformBridge.kt
+++ b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/utils/OpenTokPlatformBridge.kt
@@ -1,6 +1,6 @@
package com.ejada.hmg.utils
-import com.ejada.hmg.MainActivity
+import com.cloud.diplomaticquarterapp.MainActivity
import com.ejada.hmg.opentok.OpenTok
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.MethodCall
diff --git a/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/utils/PlatformBridge.kt b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/utils/PlatformBridge.kt
index 9a66f4b3..c6b2476d 100644
--- a/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/utils/PlatformBridge.kt
+++ b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/utils/PlatformBridge.kt
@@ -11,7 +11,7 @@ import android.widget.Toast
import androidx.core.app.ActivityCompat.startActivityForResult
import android.net.wifi.WifiManager
import android.util.Log
-import com.ejada.hmg.MainActivity
+import com.cloud.diplomaticquarterapp.MainActivity
import com.ejada.hmg.hmgwifi.HMG_Guest
import com.ejada.hmg.geofence.GeoZoneModel
import com.ejada.hmg.geofence.HMG_Geofence
diff --git a/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/utils/UiChannel.kt b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/utils/UiChannel.kt
new file mode 100644
index 00000000..6437981e
--- /dev/null
+++ b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/utils/UiChannel.kt
@@ -0,0 +1,7 @@
+package com.cloud.diplomaticquarterapp.utils
+
+import kotlinx.coroutines.channels.Channel
+
+object UiChannel {
+ val progressChannel = Channel(Channel.CONFLATED)
+}
\ No newline at end of file
diff --git a/android/blesdk_2301/build.gradle b/android/blesdk_2301/build.gradle
new file mode 100644
index 00000000..0c6cd1c7
--- /dev/null
+++ b/android/blesdk_2301/build.gradle
@@ -0,0 +1,54 @@
+apply plugin: 'com.android.library'
+apply plugin: 'org.jetbrains.kotlin.android'
+
+
+android {
+ compileSdkVersion 33
+
+ lintOptions{
+ abortOnError false
+ }
+
+ defaultConfig {
+
+ minSdkVersion 18
+ targetSdkVersion 33
+ versionCode 3
+ versionName "3.0"
+ testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
+ }
+
+ buildTypes {
+ release {
+ minifyEnabled true
+ proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+ consumerProguardFiles 'proguard-rules.pro'
+ }
+ debug {
+ minifyEnabled true
+ proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+ consumerProguardFiles 'proguard-rules.pro'
+ }
+ }
+ task makejar(type: Jar) {
+ from file('build/intermediates/javac/debug/classes')
+ archiveName = '2301sdk' + defaultConfig.versionName + '.jar'
+ destinationDir = file('build/libs')
+ include "com/jstyle/blesdk2301/**"
+
+
+ }
+ makejar.dependsOn(build)
+
+}
+
+
+dependencies {
+ implementation fileTree(dir: 'libs', include: ['*.jar'])
+ implementation 'androidx.core:core-ktx:+'
+
+
+ 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'
+}
diff --git a/android/blesdk_2301/java_pid10828.hprof b/android/blesdk_2301/java_pid10828.hprof
new file mode 100644
index 00000000..506d22d2
Binary files /dev/null and b/android/blesdk_2301/java_pid10828.hprof differ
diff --git a/android/blesdk_2301/src/androidTest/java/com/jstyle/blesdk2301/ExampleInstrumentedTest.java b/android/blesdk_2301/src/androidTest/java/com/jstyle/blesdk2301/ExampleInstrumentedTest.java
new file mode 100644
index 00000000..84547b8a
--- /dev/null
+++ b/android/blesdk_2301/src/androidTest/java/com/jstyle/blesdk2301/ExampleInstrumentedTest.java
@@ -0,0 +1,26 @@
+package com.jstyle.blesdk2301;
+
+import android.content.Context;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import static org.junit.Assert.*;
+
+/**
+ * Instrumented test, which will execute on an Android device.
+ *
+ * @see Testing documentation
+ */
+@RunWith(AndroidJUnit4.class)
+public class ExampleInstrumentedTest {
+ @Test
+ public void useAppContext() throws Exception {
+ // Context of the app under test.
+ Context appContext = InstrumentationRegistry.getTargetContext();
+
+ assertEquals("com.jstyle.blesdk.test", appContext.getPackageName());
+ }
+}
diff --git a/android/blesdk_2301/src/main/java/com/jstyle/blesdk2301/Util/BleSDK.kt b/android/blesdk_2301/src/main/java/com/jstyle/blesdk2301/Util/BleSDK.kt
new file mode 100644
index 00000000..992416a8
--- /dev/null
+++ b/android/blesdk_2301/src/main/java/com/jstyle/blesdk2301/Util/BleSDK.kt
@@ -0,0 +1,886 @@
+package com.jstyle.blesdk2301.Util
+
+import android.text.TextUtils
+import com.jstyle.blesdk2301.callback.DataListener2301
+import com.jstyle.blesdk2301.constant.BleConst
+import com.jstyle.blesdk2301.constant.DeviceConst
+import com.jstyle.blesdk2301.constant.DeviceKey
+import com.jstyle.blesdk2301.model.AutoMode
+import com.jstyle.blesdk2301.model.AutoTestMode
+import com.jstyle.blesdk2301.model.MyAutomaticHRMonitoring
+import com.jstyle.blesdk2301.model.MyDeviceTime
+import com.jstyle.blesdk2301.model.MyPersonalInfo
+import com.jstyle.blesdk2301.other.ResolveUtil
+import java.io.UnsupportedEncodingException
+import java.text.SimpleDateFormat
+import java.util.Arrays
+import java.util.Date
+import kotlin.experimental.and
+
+/**
+ * Created by Administrator on 2018/4/9.
+ */
+object BleSDK {
+ const val DATA_READ_START = 0
+ const val DATA_READ_CONTINUE = 2
+ const val DATA_DELETE = 99
+ const val DistanceMode_MILE = 0x81.toByte()
+ const val DistanceMode_KM = 0x80.toByte()
+ const val TimeMode_12h = 0x81.toByte()
+ const val TimeMode_24h = 0x80.toByte()
+ const val WristOn_Enable = 0x81.toByte()
+ const val WristOn_DisEnable = 0x80.toByte()
+ const val TempUnit_C = 0x80.toByte()
+ const val TempUnit_F = 0x81.toByte()
+ const val TAG = "BleSDK"
+ var isruning = false
+ private fun getByteArray(f: Float): ByteArray {
+ val intbits = java.lang.Float.floatToIntBits(f) //将float里面的二进制串解释为int整数
+ return getByteArrays(intbits)
+ }
+
+ fun getByteArrays(i: Int): ByteArray {
+ val b = ByteArray(4)
+ b[3] = (i and -0x1000000 shr 24).toByte()
+ b[2] = (i and 0x00ff0000 shr 16).toByte()
+ b[1] = (i and 0x0000ff00 shr 8).toByte()
+ b[0] = (i and 0x000000ff).toByte()
+ return b
+ }
+
+ fun DataParsingWithData(value: ByteArray, dataListener: DataListener2301) {
+ val map: Map = HashMap()
+ when (value[0]) {
+ DeviceConst.CMD_Set_UseInfo -> dataListener.dataCallback(ResolveUtil.setMethodSuccessful(BleConst.SetPersonalInfo))
+ DeviceConst.CMD_Set_Auto -> dataListener.dataCallback(ResolveUtil.setMethodSuccessful(BleConst.SetAutomatic))
+ DeviceConst.CMD_Set_DeviceID -> dataListener.dataCallback(ResolveUtil.setMacSuccessful())
+ DeviceConst.CMD_SET_TIME -> dataListener.dataCallback(ResolveUtil.setTimeSuccessful(value))
+ DeviceConst.CMD_GET_TIME -> dataListener.dataCallback(ResolveUtil.getDeviceTime(value))
+ DeviceConst.CMD_GET_USERINFO -> dataListener.dataCallback(ResolveUtil.getUserInfo(value))
+ DeviceConst.CMD_Enable_Activity -> dataListener.dataCallback(ResolveUtil.getActivityData(value))
+ DeviceConst.CMD_Get_BatteryLevel -> dataListener.dataCallback(ResolveUtil.getDeviceBattery(value))
+ DeviceConst.CMD_Get_Address -> dataListener.dataCallback(ResolveUtil.getDeviceAddress(value))
+ DeviceConst.CMD_Get_Version -> dataListener.dataCallback(ResolveUtil.getDeviceVersion(value))
+ DeviceConst.CMD_Get_Auto -> dataListener.dataCallback(ResolveUtil.getAutoHeart(value))
+ DeviceConst.CMD_Reset -> dataListener.dataCallback(ResolveUtil.Reset())
+ DeviceConst.CMD_Mcu_Reset -> dataListener.dataCallback(ResolveUtil.MCUReset())
+ DeviceConst.CMD_Get_TotalData -> if (GetTotalActivityDataWithMode) {
+ dataListener.dataCallback(ResolveUtil.setMethodSuccessful(BleConst.Delete_GetTotalActivityData))
+ } else {
+ dataListener.dataCallback(ResolveUtil.getTotalStepData(value))
+ }
+
+ DeviceConst.CMD_Get_DetailData -> if (GetDetailActivityDataWithMode) {
+ dataListener.dataCallback(ResolveUtil.setMethodSuccessful(BleConst.deleteGetDetailActivityDataWithMode))
+ } else {
+ dataListener.dataCallback(ResolveUtil.getDetailData(value))
+ }
+
+ DeviceConst.CMD_Get_SleepData -> if (Delete_GetDetailSleepData) {
+ dataListener.dataCallback(ResolveUtil.setMethodSuccessful(BleConst.Delete_GetDetailSleepData))
+ } else {
+ dataListener.dataCallback(ResolveUtil.getSleepData(value))
+ }
+
+ DeviceConst.CMD_Get_HeartData -> if (GetDynamicHRWithMode) {
+ dataListener.dataCallback(ResolveUtil.setMethodSuccessful(BleConst.Delete_GetDynamicHR))
+ } else {
+ dataListener.dataCallback(ResolveUtil.getHeartData(value))
+ }
+
+ DeviceConst.CMD_Get_OnceHeartData -> if (GetStaticHRWithMode) {
+ dataListener.dataCallback(ResolveUtil.setMethodSuccessful(BleConst.Delete_GetStaticHR))
+ } else {
+ dataListener.dataCallback(ResolveUtil.getOnceHeartData(value))
+ }
+
+ DeviceConst.CMD_Get_HrvTestData -> if (readhrv) {
+ dataListener.dataCallback(ResolveUtil.getHrvTestData(value))
+ } else {
+ dataListener.dataCallback(ResolveUtil.DeleteHrv())
+ }
+
+ DeviceConst.ReadTempHisrory -> if (GetTemperature_historyDataWithMode) {
+ dataListener.dataCallback(ResolveUtil.setMethodSuccessful(BleConst.deleteGetTemperature_historyDataWithMode))
+ } else {
+ dataListener.dataCallback(ResolveUtil.getTempData(value))
+ }
+
+ DeviceConst.Oxygen_data -> if (Obtain_The_data_of_manual_blood_oxygen_test) {
+ dataListener.dataCallback(ResolveUtil.setMethodSuccessful(BleConst.Delete_Obtain_The_data_of_manual_blood_oxygen_test))
+ } else {
+ dataListener.dataCallback(ResolveUtil.GetAutomaticSpo2Monitoring(value))
+ }
+
+ DeviceConst.CMD_HeartPackageFromDevice -> dataListener.dataCallback(ResolveUtil.getActivityExerciseData(value))
+ DeviceConst.CMD_Set_Name -> dataListener.dataCallback(ResolveUtil.setMethodSuccessful(BleConst.CMD_Set_Name))
+ DeviceConst.CMD_Get_Name -> dataListener.dataCallback(ResolveUtil.getDeviceName(value))
+ DeviceConst.CMD_Get_SPORTData -> if (GetActivityModeDataWithMode) {
+ dataListener.dataCallback(ResolveUtil.setMethodSuccessful(BleConst.Delete_ActivityModeData))
+ } else {
+ dataListener.dataCallback(ResolveUtil.getExerciseData(value))
+ }
+
+ DeviceConst.MeasurementWithType -> if (StartDeviceMeasurementWithType) {
+ val vv: MutableMap = HashMap()
+ when (value[1]) {
+ 1.toByte() -> {
+ vv[DeviceKey.DataType] = BleConst.MeasurementHrvCallback
+ vv[DeviceKey.End] = true
+ val lm: MutableMap = HashMap()
+ lm[DeviceKey.Type] = ResolveUtil.getValue(value[1], 0).toString() + ""
+ lm[DeviceKey.HeartRate] = ResolveUtil.getValue(value[2], 0).toString() + ""
+ lm[DeviceKey.Blood_oxygen] = ResolveUtil.getValue(value[3], 0).toString() + ""
+ lm[DeviceKey.HRV] = ResolveUtil.getValue(value[4], 0).toString() + ""
+ lm[DeviceKey.Stress] = ResolveUtil.getValue(value[5], 0).toString() + ""
+ lm[DeviceKey.HighPressure] = ResolveUtil.getValue(value[6], 0).toString() + ""
+ lm[DeviceKey.LowPressure] = ResolveUtil.getValue(value[7], 0).toString() + ""
+ vv[DeviceKey.Data] = lm
+ dataListener.dataCallback(vv)
+ }
+
+ 2.toByte() -> {
+ vv[DeviceKey.DataType] = BleConst.MeasurementHeartCallback
+ vv[DeviceKey.End] = true
+ val lmB: MutableMap = HashMap()
+ lmB[DeviceKey.Type] = ResolveUtil.getValue(value[1], 0).toString() + ""
+ lmB[DeviceKey.HeartRate] = ResolveUtil.getValue(value[2], 0).toString() + ""
+ lmB[DeviceKey.Blood_oxygen] = ResolveUtil.getValue(value[3], 0).toString() + ""
+ lmB[DeviceKey.HRV] = ResolveUtil.getValue(value[4], 0).toString() + ""
+ lmB[DeviceKey.Stress] = ResolveUtil.getValue(value[5], 0).toString() + ""
+ lmB[DeviceKey.HighPressure] = ResolveUtil.getValue(value[6], 0).toString() + ""
+ lmB[DeviceKey.LowPressure] = ResolveUtil.getValue(value[7], 0).toString() + ""
+ vv[DeviceKey.Data] = lmB
+ dataListener.dataCallback(vv)
+ }
+
+ 3.toByte() -> {
+ vv[DeviceKey.DataType] = BleConst.MeasurementOxygenCallback
+ vv[DeviceKey.End] = true
+ val lmC: MutableMap = HashMap()
+ lmC[DeviceKey.Type] = ResolveUtil.getValue(value[1], 0).toString() + ""
+ lmC[DeviceKey.HeartRate] = ResolveUtil.getValue(value[2], 0).toString() + ""
+ lmC[DeviceKey.Blood_oxygen] = ResolveUtil.getValue(value[3], 0).toString() + ""
+ lmC[DeviceKey.HRV] = ResolveUtil.getValue(value[4], 0).toString() + ""
+ lmC[DeviceKey.Stress] = ResolveUtil.getValue(value[5], 0).toString() + ""
+ lmC[DeviceKey.HighPressure] = ResolveUtil.getValue(value[6], 0).toString() + ""
+ lmC[DeviceKey.LowPressure] = ResolveUtil.getValue(value[7], 0).toString() + ""
+ vv[DeviceKey.Data] = lmC
+ dataListener.dataCallback(vv)
+ }
+ }
+ } else {
+ val vv: MutableMap = HashMap()
+ when (value[1]) {
+ 1.toByte() -> {
+ vv[DeviceKey.DataType] = BleConst.StopMeasurementHrvCallback
+ vv[DeviceKey.End] = true
+ vv[DeviceKey.Data] = HashMap()
+ dataListener.dataCallback(vv)
+ }
+
+ 2.toByte() -> {
+ vv[DeviceKey.DataType] = BleConst.StopMeasurementHeartCallback
+ vv[DeviceKey.End] = true
+ vv[DeviceKey.Data] = HashMap()
+ dataListener.dataCallback(vv)
+ }
+
+ 3.toByte() -> {
+ vv[DeviceKey.DataType] = BleConst.StopMeasurementOxygenCallback
+ vv[DeviceKey.End] = true
+ vv[DeviceKey.Data] = HashMap()
+ dataListener.dataCallback(vv)
+ }
+ }
+ }
+
+ DeviceConst.CMD_Get_Bloodsugar -> if (startBloodsugar) { //只有开始测量才返回状态
+ val vv: MutableMap = HashMap()
+ vv[DeviceKey.DataType] = BleConst.Blood_glucose_status
+ vv[DeviceKey.End] = true
+ val lm: MutableMap = HashMap()
+ lm[DeviceKey.Type] = ResolveUtil.getValue(value[1], 0).toString() + ""
+ vv[DeviceKey.Data] = lm
+ dataListener.dataCallback(vv)
+ }
+
+ DeviceConst.Bloodsugar_data -> {
+ val vv: MutableMap = HashMap()
+ vv[DeviceKey.DataType] = BleConst.Blood_glucose_data
+ vv[DeviceKey.End] = false
+ val lm: MutableMap = HashMap()
+ lm[DeviceKey.Time] = getFormatTimeString(System.currentTimeMillis(), "yyyy.MM.dd HH:mm:ss")
+ val simplePpg: MutableList = ArrayList()
+ if (value.size == 153) { //新血糖解析
+ val valueer = ByteArray(value.size - 3)
+ System.arraycopy(value, 3, valueer, 0, valueer.size)
+ var i = 0
+ while (i < valueer.size / 3) {
+ val valuedata = (ResolveUtil.getValue(valueer[3 * i], 2)
+ + ResolveUtil.getValue(valueer[3 * i + 1], 1)
+ + ResolveUtil.getValue(valueer[3 * i + 2], 0))
+ simplePpg.add(valuedata)
+ i++
+ }
+ lm[DeviceKey.PPG] = Arrays.toString(simplePpg.toTypedArray())
+ }
+ vv[DeviceKey.Data] = lm
+ dataListener.dataCallback(vv)
+ }
+
+ DeviceConst.CMD_Start_EXERCISE -> if (FindActivityMode) {
+ val mapEXERCISE: MutableMap = HashMap()
+ mapEXERCISE[DeviceKey.DataType] = BleConst.FindActivityMode
+ mapEXERCISE[DeviceKey.End] = true
+ val mapsx: MutableMap = HashMap()
+ mapsx[DeviceKey.Type] = ResolveUtil.getValue(value[1], 0).toString() + ""
+ mapsx[DeviceKey.Status] = ResolveUtil.getValue(value[2], 0).toString() + ""
+ mapEXERCISE[DeviceKey.Data] = mapsx
+ dataListener.dataCallback(mapEXERCISE)
+ } else {
+ val mapEXERCISE: MutableMap = HashMap()
+ mapEXERCISE[DeviceKey.DataType] = BleConst.EnterActivityMode
+ mapEXERCISE[DeviceKey.End] = true
+ val mapsx: MutableMap = HashMap()
+ mapsx[DeviceKey.enterActivityModeSuccess] = ResolveUtil.getValue(value[1], 0).toString() + ""
+ mapEXERCISE[DeviceKey.Data] = mapsx
+ dataListener.dataCallback(mapEXERCISE)
+ }
+ }
+ // return map;
+ }
+
+ internal var Delete_GetDetailSleepData = false
+ fun GetDetailSleepDataWithMode(mode: Byte, dateOfLastData: String): ByteArray {
+ Delete_GetDetailSleepData = 0x99.toByte() == mode
+ val value = ByteArray(16)
+ value[0] = DeviceConst.CMD_Get_SleepData
+ value[1] = mode
+ insertDateValue(value, dateOfLastData)
+ crcValue(value)
+ return value
+ }
+
+ /**
+ * 开始实时计步
+ *
+ * @return
+ */
+ fun RealTimeStep(enable: Boolean, tempEnable: Boolean): ByteArray {
+ val value = ByteArray(16)
+ value[0] = DeviceConst.CMD_Enable_Activity
+ value[1] = (if (enable) 1 else 0).toByte()
+ value[2] = (if (tempEnable) 1 else 0).toByte()
+ crcValue(value)
+ return value
+ }
+
+ /**
+ * 设置个人信息
+ *
+ * @param
+ * @return
+ */
+ fun SetPersonalInfo(info: MyPersonalInfo): ByteArray {
+ val value = ByteArray(16)
+ val male = info.sex
+ val age = info.age
+ val height = info.height
+ val weight = info.weight
+ val stepLength = info.stepLength
+ value[0] = DeviceConst.CMD_Set_UseInfo
+ value[1] = male.toByte()
+ value[2] = age.toByte()
+ value[3] = height.toByte()
+ value[4] = weight.toByte()
+ value[5] = stepLength.toByte()
+ crcValue(value)
+ return value
+ }
+
+ /**
+ * 获取个人信息
+ *
+ * @return
+ */
+ fun GetPersonalInfo(): ByteArray {
+ val value = ByteArray(16)
+ value[0] = DeviceConst.CMD_GET_USERINFO
+ crcValue(value)
+ return value
+ }
+
+ /**
+ * 设置设备时间
+ *
+ * @param
+ * @return
+ */
+ fun SetDeviceTime(time: MyDeviceTime): ByteArray {
+ val value = ByteArray(16)
+ val timeZone = ResolveUtil.getCurrentTimeZone()
+ var zone = timeZone.substring(3)
+ var zoneValue: Byte = 0
+ if (zone.contains("-")) {
+ zone = zone.replace("-", "")
+ zoneValue = java.lang.Byte.valueOf(zone)
+ } else {
+ zoneValue = (java.lang.Byte.valueOf(zone) + 0x80).toByte()
+ }
+ val year = time.year
+ val month = time.month
+ val day = time.day
+ val hour = time.hour
+ val min = time.minute
+ val second = time.second
+ value[0] = DeviceConst.CMD_SET_TIME
+ value[1] = ResolveUtil.getTimeValue(year)
+ value[2] = ResolveUtil.getTimeValue(month)
+ value[3] = ResolveUtil.getTimeValue(day)
+ value[4] = ResolveUtil.getTimeValue(hour)
+ value[5] = ResolveUtil.getTimeValue(min)
+ value[6] = ResolveUtil.getTimeValue(second)
+ value[8] = zoneValue
+ crcValue(value)
+ return value
+ }
+
+ /**
+ * 获取设备时间
+ *
+ * @return
+ */
+ fun GetDeviceTime(): ByteArray {
+ val value = ByteArray(16)
+ value[0] = DeviceConst.CMD_GET_TIME
+ crcValue(value)
+ return value
+ }
+
+ /**
+ * 99: 删除步数详细数据 ,
+ * 0:读最近的步数详细数据。
+ * 1:读指定位置的步数详细数据。
+ * 2:继续上次读的位置下一段数据
+ *
+ * @param
+ * @return * dateOfLastData "yyyy-MM-dd HH:mm:ss or yyyy.MM.dd HH:mm:ss"
+ */
+ private var GetDetailActivityDataWithMode = false
+ fun GetDetailActivityDataWithMode(mode: Byte, dateOfLastData: String): ByteArray {
+ GetDetailActivityDataWithMode = 0x99.toByte() == mode
+ val value = ByteArray(16)
+ value[0] = DeviceConst.CMD_Get_DetailData
+ value[1] = mode
+ insertDateValue(value, dateOfLastData)
+ crcValue(value)
+ return value
+ }
+
+ /**
+ * 99: 删除温度数据 ,
+ * 0:读最近的温度详细数据。
+ * 1:读指定位置的步数详细数据。
+ * 2:继续上次读的位置下一段数据
+ *
+ * @param
+ * @return
+ */
+ internal var GetTemperature_historyDataWithMode = false
+ fun GetTemperature_historyData(mode: Byte, dateOfLastData: String): ByteArray {
+ GetTemperature_historyDataWithMode = 0x99.toByte() == mode
+ val value = ByteArray(16)
+ value[0] = DeviceConst.ReadTempHisrory
+ value[1] = mode
+ insertDateValue(value, dateOfLastData)
+ crcValue(value)
+ return value
+ }
+
+ var StartDeviceMeasurementWithType = false
+ fun SetDeviceMeasurementWithType(dataType: AutoTestMode?, second: Long, open: Boolean): ByteArray {
+ StartDeviceMeasurementWithType = open
+ val value = ByteArray(16)
+ value[0] = DeviceConst.MeasurementWithType
+ when (dataType) {
+ AutoTestMode.AutoHeartRate -> value[1] = 0x02.toByte()
+ AutoTestMode.AutoSpo2 -> value[1] = 0x03.toByte()
+ else -> {}
+ }
+ value[2] = if (open) 0x01.toByte() else 0x00.toByte()
+ value[3] = (second and 0xffL).toByte()
+ value[4] = (second shr 8 and 0xffL).toByte()
+ crcValue(value)
+ return value
+ }
+
+ fun SetAutomaticHRMonitoring(autoHeart: MyAutomaticHRMonitoring, type: AutoMode?): ByteArray {
+ val value = ByteArray(16)
+ val time = autoHeart.time
+ value[0] = DeviceConst.CMD_Set_Auto
+ value[1] = autoHeart.open.toByte()
+ value[2] = ResolveUtil.getTimeValue(autoHeart.startHour)
+ value[3] = ResolveUtil.getTimeValue(autoHeart.startMinute)
+ value[4] = ResolveUtil.getTimeValue(autoHeart.endHour)
+ value[5] = ResolveUtil.getTimeValue(autoHeart.endMinute)
+ value[6] = autoHeart.week.toByte()
+ value[7] = (time and 0xff).toByte()
+ value[8] = (time shr 8 and 0xff).toByte()
+ if (null == type) {
+ value[9] = 0x01.toByte()
+ } else {
+ when (type) {
+ AutoMode.AutoHeartRate -> value[9] = 0x01.toByte()
+ AutoMode.AutoSpo2 -> value[9] = 0x02.toByte()
+ AutoMode.AutoTemp -> value[9] = 0x03.toByte()
+ AutoMode.AutoHrv -> value[9] = 0x04.toByte()
+ }
+ }
+ crcValue(value)
+ return value
+ }
+
+ fun GetAutomatic(type: AutoMode?): ByteArray {
+ val value = ByteArray(16)
+ value[0] = DeviceConst.CMD_Get_Auto
+ if (null == type) {
+ value[1] = 0x01.toByte()
+ } else {
+ when (type) {
+ AutoMode.AutoHeartRate -> value[1] = 0x01.toByte()
+ AutoMode.AutoSpo2 -> value[1] = 0x02.toByte()
+ AutoMode.AutoTemp -> value[1] = 0x03.toByte()
+ AutoMode.AutoHrv -> value[1] = 0x04.toByte()
+ }
+ }
+ crcValue(value)
+ return value
+ }
+
+ /**
+ * 获取某天总数据
+ * 0
+ *
+ * @param mode 0:表⽰是从最新的位置开始读取(最多50组数据) 2:表⽰接着读取(当数据总数⼤于50的时候) 0x99:表⽰删除所有运
+ * 动数据
+ * @return
+ */
+ internal var GetTotalActivityDataWithMode = false
+ fun GetTotalActivityDataWithMode(mode: Byte, dateOfLastData: String): ByteArray {
+ GetTotalActivityDataWithMode = 0x99.toByte() == mode
+ val value = ByteArray(16)
+ value[0] = DeviceConst.CMD_Get_TotalData
+ value[1] = mode
+ insertDateValueNoH(value, dateOfLastData)
+ crcValue(value)
+ return value
+ }
+
+ /**
+ * 获取设备版本号
+ *
+ * @return
+ */
+ fun GetDeviceVersion(): ByteArray {
+ val value = ByteArray(16)
+ value[0] = DeviceConst.CMD_Get_Version
+ crcValue(value)
+ return value
+ }
+
+ /**
+ * 恢复出厂设置
+ *
+ * @return
+ */
+ fun Reset(): ByteArray {
+ val value = ByteArray(16)
+ value[0] = DeviceConst.CMD_Reset
+ crcValue(value)
+ return value
+ }
+
+ /**
+ * 获取设备mac地址
+ *
+ * @return
+ */
+ fun GetDeviceMacAddress(): ByteArray {
+ val value = ByteArray(16)
+ value[0] = DeviceConst.CMD_Get_Address
+ crcValue(value)
+ return value
+ }
+
+ /**
+ * 获取设备电量
+ *
+ * @return
+ */
+ fun GetDeviceBatteryLevel(): ByteArray {
+ val value = ByteArray(16)
+ value[0] = DeviceConst.CMD_Get_BatteryLevel
+ crcValue(value)
+ return value
+ }
+
+ /**
+ * 重启设备
+ *
+ * @return
+ */
+ fun MCUReset(): ByteArray {
+ val value = ByteArray(16)
+ value[0] = DeviceConst.CMD_Mcu_Reset
+ crcValue(value)
+ return value
+ }
+
+ private var readhrv = false
+ fun GetHRVDataWithMode(mode: Byte, dateOfLastData: String): ByteArray {
+ readhrv = 0x99.toByte() != mode
+ val value = ByteArray(16)
+ value[0] = DeviceConst.CMD_Get_HrvTestData
+ value[1] = mode
+ insertDateValue(value, dateOfLastData)
+ crcValue(value)
+ return value
+ }
+
+ internal var GetStaticHRWithMode = false
+ fun GetStaticHRWithMode(mode: Byte, dateOfLastData: String): ByteArray {
+ GetStaticHRWithMode = 0x99.toByte() == mode
+ val value = ByteArray(16)
+ value[0] = DeviceConst.CMD_Get_OnceHeartData
+ value[1] = mode
+ insertDateValue(value, dateOfLastData)
+ crcValue(value)
+ return value
+ }
+
+ internal var GetDynamicHRWithMode = false
+ fun GetDynamicHRWithMode(Number: Byte, dateOfLastData: String): ByteArray {
+ GetDynamicHRWithMode = 0x99.toByte() == Number
+ val value = ByteArray(16)
+ value[0] = DeviceConst.CMD_Get_HeartData
+ value[1] = Number
+ insertDateValue(value, dateOfLastData)
+ crcValue(value)
+ return value
+ }
+
+ fun SetDeviceID(deviceId: String): ByteArray {
+ val value = ByteArray(16)
+ value[0] = DeviceConst.CMD_Set_DeviceID
+ for (i in 0..5) {
+ value[i + 1] = deviceId[i].code.toByte()
+ }
+ crcValue(value)
+ return value
+ }
+
+ fun intTobyte(num: Int): ByteArray {
+ return byteArrayOf((num shr 8 and 0xff).toByte(), (num and 0xff).toByte())
+ }
+
+ @JvmStatic
+ fun byteArrayToInt(arr: ByteArray): Int {
+ val targets = (arr[1].toInt() and 0xff or (arr[0].toInt() shl 8 and 0xff00)).toShort()
+ return targets.toInt()
+ }
+
+ fun getInfoValue(info: String, maxLength: Int): ByteArray? {
+ var nameBytes: ByteArray? = null
+ try {
+ nameBytes = info.toByteArray(charset("UTF-8"))
+ if (nameBytes.size >= maxLength) { //两条命令总共32个字节,内容只占24个字节(32-2*(1cmd+1消息类型+1长度+1校验))
+ val real = ByteArray(maxLength)
+ val chars = info.toCharArray()
+ var length = 0
+ for (i in chars.indices) {
+ val s = chars[i].toString()
+ val nameB = s.toByteArray(charset("UTF-8"))
+ if (length + nameB.size == maxLength) {
+ System.arraycopy(nameBytes, 0, real, 0, real.size)
+ return real
+ } else if (length + nameB.size > maxLength) { //大于24会导致有个字节发不到下位机导致乱码
+ System.arraycopy(nameBytes, 0, real, 0, length)
+ return real
+ }
+ length += nameB.size
+ }
+ }
+ } catch (e: UnsupportedEncodingException) {
+ // TODO Auto-generated catch block
+ e.printStackTrace()
+ }
+ return nameBytes
+ }
+
+ fun crcValue(value: ByteArray) {
+ var crc: Byte = 0
+ for (i in 0 until value.size - 1) {
+ crc = (crc + value[i]).toByte()
+ }
+ value[value.size - 1] = (crc and 0xff.toByte())
+ }
+
+
+ fun insertDateValue(value: ByteArray, time: String) {
+ if (!TextUtils.isEmpty(time) && time.length > 8) {
+ val timeArray = time.split(" ".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
+ val INDEX: String
+ INDEX = if (time.contains("-")) {
+ "-"
+ } else {
+ "\\."
+ }
+ val year = timeArray[0].split(INDEX.toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()[0].toInt()
+ val month = timeArray[0].split(INDEX.toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()[1].toInt()
+ val day = timeArray[0].split(INDEX.toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()[2].toInt()
+ val hour = timeArray[1].split(":".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()[0].toInt()
+ val min = timeArray[1].split(":".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()[1].toInt()
+ val second = timeArray[1].split(":".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()[2].toInt()
+ value[4] = ResolveUtil.getTimeValue(year)
+ value[5] = ResolveUtil.getTimeValue(month)
+ value[6] = ResolveUtil.getTimeValue(day)
+ value[7] = ResolveUtil.getTimeValue(hour)
+ value[8] = ResolveUtil.getTimeValue(min)
+ value[9] = ResolveUtil.getTimeValue(second)
+ }
+ }
+
+ fun insertDateValueNoH(value: ByteArray, time: String) {
+ if (!TextUtils.isEmpty(time) && time.contains("-")) {
+ val timeArray = time.split(" ".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
+ val INDEX: String
+ INDEX = if (time.contains("-")) {
+ "-"
+ } else {
+ "\\."
+ }
+ val year = timeArray[0].split(INDEX.toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()[0].toInt()
+ val month = timeArray[0].split(INDEX.toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()[1].toInt()
+ val day = timeArray[0].split(INDEX.toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()[2].toInt()
+ value[4] = ResolveUtil.getTimeValue(year)
+ value[5] = ResolveUtil.getTimeValue(month)
+ value[6] = ResolveUtil.getTimeValue(day)
+ }
+ }
+
+ /**
+ * 转十六进制字符串
+ *
+ * @param data
+ * @return
+ */
+ fun byte2Hex(data: ByteArray?): String {
+ if (data != null && data.size > 0) {
+ val sb = StringBuilder(data.size)
+ for (tmp in data) {
+ sb.append(String.format("%02X ", tmp))
+ }
+ return sb.toString()
+ }
+ return "no data"
+ }
+
+ /**
+ * OObtain automated test oximetry data
+ * 获得自动测试血氧数据
+ */
+ internal var Obtain_The_data_of_manual_blood_oxygen_test = false
+ fun Oxygen_data(Number: Byte): ByteArray {
+ Obtain_The_data_of_manual_blood_oxygen_test = 0x99.toByte() == Number
+ val value = ByteArray(16)
+ value[0] = DeviceConst.Oxygen_data
+ value[1] = Number
+ crcValue(value)
+ return value
+ }
+
+ fun EnterActivityMode(time: Int, activityMode: Int, WorkMode: Int): ByteArray {
+ FindActivityMode = false
+ val value = ByteArray(16)
+ value[0] = DeviceConst.CMD_Start_EXERCISE
+ value[1] = WorkMode.toByte()
+ value[2] = activityMode.toByte()
+ value[4] = time.toByte()
+ crcValue(value)
+ return value
+ }
+
+ private var FindActivityMode = false
+ fun FindActivityMode(): ByteArray {
+ FindActivityMode = true
+ val value = ByteArray(16)
+ value[0] = DeviceConst.CMD_Start_EXERCISE
+ value[1] = 0x05.toByte()
+ crcValue(value)
+ return value
+ }
+
+ /**
+ * @param status 级别 0,1,2
+ * @param time 分钟
+ * @param WorkMode START=1;PAUSE=2;CONTUINE=3;FINISH=4;
+ * @return
+ */
+ fun setExerciseModeByBreath(status: Int, time: Int, WorkMode: Int): ByteArray {
+ FindActivityMode = false
+ val value = ByteArray(16)
+ value[0] = DeviceConst.CMD_Start_EXERCISE
+ value[1] = WorkMode.toByte()
+ value[2] = 0x06.toByte()
+ value[3] = status.toByte()
+ value[4] = time.toByte()
+ crcValue(value)
+ return value
+ }
+
+ fun sendHeartPackage(distance: Float, space: Int, rssi: Int): ByteArray {
+ val value = ByteArray(16)
+ val distanceValue = getByteArray(distance)
+ val min = space / 60
+ val second = space % 60
+ value[0] = DeviceConst.CMD_heart_package
+ System.arraycopy(distanceValue, 0, value, 1, distanceValue.size)
+ value[5] = min.toByte()
+ value[6] = second.toByte()
+ value[7] = rssi.toByte()
+ crcValue(value)
+ return value
+ }
+
+ /**
+ * 设置设备名字
+ *
+ * @param
+ * @return
+ */
+ fun SetDeviceName(strDeviceName: String): ByteArray {
+ val value = ByteArray(16)
+ value[0] = DeviceConst.CMD_Set_Name
+ val length = if (strDeviceName.length > 14) 14 else strDeviceName.length
+ for (i in 0 until length) {
+ value[i + 1] = strDeviceName[i].code.toByte()
+ }
+ crcValue(value)
+ return value
+ }
+
+ /**
+ * 获取设备名字
+ *
+ * @return
+ */
+ fun GetDeviceName(): ByteArray {
+ val value = ByteArray(16)
+ value[0] = DeviceConst.CMD_Get_Name
+ crcValue(value)
+ return value
+ }
+
+ /**
+ * 获取多模式运动数据
+ *
+ * @param mode 0:表⽰是从最新的位置开始读取(最多50组数据) 2:表⽰接着读取(当数据总数⼤于50的时候) 0x99:表⽰删除所有
+ * GPS数据
+ * @return
+ */
+ internal var GetActivityModeDataWithMode = false
+ fun GetActivityModeDataWithMode(mode: Byte): ByteArray {
+ GetActivityModeDataWithMode = 0x99.toByte() == mode
+ val value = ByteArray(16)
+ value[0] = DeviceConst.CMD_Get_SPORTData
+ value[1] = mode
+ crcValue(value)
+ return value
+ }
+
+ /**
+ * 开始血氧测量
+ * Start blood oxygen measurement
+ */
+ var startBloodsugar = false
+ fun startBloodsugar(): ByteArray {
+ startBloodsugar = true
+ val value = ByteArray(16)
+ value[0] = DeviceConst.CMD_Get_Bloodsugar
+ value[1] = 0x01.toByte()
+ crcValue(value)
+ return value
+ }
+
+ /**
+ * 停止血氧测量
+ * Stop blood oxygen measurement
+ */
+ fun BloodsugarStop(): ByteArray {
+ startBloodsugar = false
+ val value = ByteArray(16)
+ value[0] = DeviceConst.CMD_Get_Bloodsugar
+ value[1] = 0x05.toByte()
+ crcValue(value)
+ return value
+ }
+
+ /**
+ * 血氧测量停止信号采集
+ * Blood oxygen measurement stop signal acquisition
+ */
+ fun BloodsugarStopSiss(): ByteArray {
+ startBloodsugar = false
+ val value = ByteArray(16)
+ value[0] = DeviceConst.CMD_Get_Bloodsugar
+ value[1] = 0x03.toByte()
+ crcValue(value)
+ return value
+ }
+
+ /**
+ * 血氧测量进度更新
+ * Blood oxygen measurement progress update
+ */
+ fun BloodsugarProgress(Progress: Int): ByteArray {
+ startBloodsugar = false
+ val value = ByteArray(16)
+ value[0] = DeviceConst.CMD_Get_Bloodsugar
+ value[1] = 0x04.toByte()
+ value[2] = Progress.toByte() //0-100
+ crcValue(value)
+ return value
+ }
+
+ /**
+ * 血氧测量结果
+ * Send blood glucose results to the device
+ * status 1低 2中 3高 0测试失败
+ * Status 1 low 2 3 high 0 test failed
+ */
+ fun SendBloodGlucoseResultsToTheDevice(status: Int): ByteArray {
+ startBloodsugar = false
+ val value = ByteArray(16)
+ value[0] = DeviceConst.CMD_Get_Bloodsugar
+ value[1] = 0x02.toByte()
+ value[2] = status.toByte()
+ crcValue(value)
+ return value
+ }
+
+ /**
+ * 获取指定格式的日期字符串
+ */
+ @Synchronized
+ fun getFormatTimeString(time: Long, formatString: String?): String {
+ val format = SimpleDateFormat(formatString)
+ var data = ""
+ try {
+ data = format.format(Date(time))
+ } catch (E: Exception) {
+ E.printStackTrace()
+ }
+ return data
+ }
+}
\ No newline at end of file
diff --git a/android/blesdk_2301/src/main/java/com/jstyle/blesdk2301/Util/PDFCreate.kt b/android/blesdk_2301/src/main/java/com/jstyle/blesdk2301/Util/PDFCreate.kt
new file mode 100644
index 00000000..bd9e147f
--- /dev/null
+++ b/android/blesdk_2301/src/main/java/com/jstyle/blesdk2301/Util/PDFCreate.kt
@@ -0,0 +1,230 @@
+package com.jstyle.blesdk2301.Util
+
+import android.annotation.SuppressLint
+import android.content.Context
+import android.graphics.Canvas
+import android.graphics.Color
+import android.graphics.Paint
+import android.graphics.Path
+import android.graphics.Rect
+import android.graphics.pdf.PdfDocument
+import android.os.Build
+import android.text.TextUtils
+import com.jstyle.blesdk2301.model.UserInfo
+import java.io.File
+import java.io.FileNotFoundException
+import java.io.FileOutputStream
+import java.io.IOException
+
+object PDFCreate {
+ private const val TAG = "PDFCreate"
+ const val maxXSize = 5120
+ var length = 20f
+ var totalWidth = 1070f
+ var startX = 35
+ var endX = (startX + 1000).toFloat()
+ var startY = 0f
+ private var endY = 0f
+ private var height = 0f
+ private var width = 0f
+ private var strokeWidthTime = 0
+ private var strokeWidthLine = 0
+ private var totalHeight = 0f
+ fun createPdf(path: String?, context: Context, data: List, userInfo: UserInfo?) {
+ val size = data.size
+ val col = (if (size % maxXSize == 0) size / maxXSize else size / maxXSize + 1).toFloat() //一次画5120个点,分多少次画
+ length = dip2px(context, 10f).toFloat()
+ height = length
+ width = length
+ startX = dip2px(context, 20f)
+ endX = startX + 50 * length
+ totalWidth = endX + startX
+ totalHeight = col * height * 5 + dip2px(context, 130f) //一次需要5个高度
+ val pdfDocument = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
+ PdfDocument()
+ } else {
+ TODO("VERSION.SDK_INT < KITKAT")
+ }
+ val pageInfo = PdfDocument.PageInfo.Builder(totalWidth.toInt(), totalHeight.toInt(), 1) // .setContentRect(new Rect(0,0,(int)totalWidth,(int)totalHeight))
+ .create()
+ val page = pdfDocument.startPage(pageInfo)
+ val canvas = page.canvas
+ val paint = Paint()
+ strokeWidthTime = dip2px(context, 1.5f)
+ strokeWidthLine = dip2px(context, 0.5f)
+ drawReportInfo(context, canvas, paint, userInfo)
+ val pathCanvas = Path()
+ endY = startY + col * height * 5
+ paint.textSize = dip2px(context, 15f).toFloat()
+ drawAxes(pathCanvas, canvas, paint, col)
+ drawTimeLine(pathCanvas, canvas, paint)
+ drawDataLine(context, pathCanvas, canvas, paint, col, data)
+ pdfDocument.finishPage(page)
+ val file = File(path)
+ var outputStream: FileOutputStream? = null
+ try {
+ outputStream = FileOutputStream(file)
+ } catch (e: FileNotFoundException) {
+ e.printStackTrace()
+ }
+ try {
+ pdfDocument.writeTo(outputStream)
+ } catch (e: IOException) {
+ e.printStackTrace()
+ }
+ }
+
+ /**用户信息
+ * @param context
+ * @param canvas
+ * @param paint
+ * @param
+ */
+ private fun drawReportInfo(context: Context, canvas: Canvas, paint: Paint, userInfo: UserInfo?) {
+ paint.textSize = dip2px(context, 15f).toFloat()
+ paint.color = Color.BLACK
+ paint.style = Paint.Style.FILL
+ if (null != userInfo) {
+ if (!TextUtils.isEmpty(userInfo.ecgTitle)) {
+ val title = userInfo.ecgTitle
+ val rect = Rect()
+ paint.getTextBounds(title, 0, title.length, rect)
+ val heightTitle = rect.height()
+ val startTextY = (heightTitle + dip2px(context, 8f)).toFloat()
+ val marginTop = dip2px(context, 4f).toFloat()
+ val marginRight = dip2px(context, 8f).toFloat()
+ //标题
+ canvas.drawText(title, totalWidth / 2 - 150, startTextY, paint)
+ //提醒
+ paint.textSize = dip2px(context, 12f).toFloat()
+ val widthTips = rect.width()
+ val heightTips = rect.height()
+ if (!TextUtils.isEmpty(userInfo.ecgReportTips)) {
+ val tips = userInfo.ecgReportTips
+ val tipsTextY = startTextY + heightTitle + marginTop
+ paint.getTextBounds(tips, 0, tips.length, rect)
+ canvas.drawText(tips, totalWidth - widthTips - marginRight, tipsTextY, paint)
+ }
+ val tipsTextY = startTextY + heightTitle + marginTop
+ val name = userInfo.name
+ val genderString = userInfo.gender
+ val age = userInfo.age
+ val userWeight = userInfo.weight
+ val userHeight = userInfo.height
+ //个人信息
+ val infoTextY = tipsTextY + heightTips + marginTop
+ @SuppressLint("StringFormatMatches") val info = "$name $genderString $age $userHeight $userWeight"
+ paint.getTextBounds(info, 0, info.length, rect)
+ val heightInfo = rect.height()
+ val widthInfo = rect.width()
+ canvas.drawText(info, totalWidth / 2 - (widthInfo shr 1), infoTextY, paint)
+ //时间
+ val dateTextY = infoTextY + heightInfo + marginTop
+ val dateString = userInfo.date
+ paint.getTextBounds(dateString, 0, dateString.length, rect)
+ val widthDate = rect.width()
+ val heightDate = rect.height()
+ canvas.drawText(dateString, totalWidth - widthDate - marginRight, dateTextY, paint)
+ startY = dateTextY + heightDate + marginTop
+ }
+ }
+ }
+
+ /**
+ * 画网格
+ *
+ * @param pathCanvas
+ * @param canvas
+ * @param paint
+ * @param col
+ */
+ private fun drawAxes(pathCanvas: Path, canvas: Canvas, paint: Paint, col: Float) {
+ //画网格竖线
+ val colorLine = Color.rgb(243, 119, 99)
+ paint.color = colorLine
+ paint.style = Paint.Style.STROKE
+ paint.strokeWidth = strokeWidthLine.toFloat()
+ paint.alpha = 200
+ for (i in 0..50) {
+ pathCanvas.moveTo(i * width + startX, startY)
+ pathCanvas.lineTo(i * width + startX, endY)
+ }
+ //画网格横线
+ var i = 0
+ while (i <= col * 5) {
+ pathCanvas.moveTo((startX - (strokeWidthTime shr 1)).toFloat(), i * height + startY)
+ pathCanvas.lineTo(endX + (strokeWidthTime shr 1), i * height + startY)
+ i++
+ }
+ canvas.drawPath(pathCanvas, paint)
+ pathCanvas.reset()
+ }
+
+ /**
+ * 加粗时间线
+ *
+ * @param pathCanvas
+ * @param canvas
+ * @param paint
+ */
+ private fun drawTimeLine(pathCanvas: Path, canvas: Canvas, paint: Paint) {
+ paint.color = Color.BLACK
+ paint.strokeWidth = strokeWidthTime.toFloat()
+ paint.style = Paint.Style.FILL
+ val rect = Rect()
+ for (i in 0..10) {
+ pathCanvas.moveTo(i * 5 * width + startX, startY)
+ pathCanvas.lineTo(i * 5 * width + startX, endY + 20)
+ val time = i.toString() + "s"
+ paint.getTextBounds(time, 0, time.length, rect)
+ val widthTime = rect.width()
+ canvas.drawText(time, i * 5 * width + startX - (widthTime shr 1), endY + 50, paint)
+ }
+ val colorS = Color.rgb(255, 119, 99)
+ paint.color = colorS
+ paint.alpha = 255
+ paint.style = Paint.Style.STROKE
+ canvas.drawPath(pathCanvas, paint)
+ pathCanvas.reset()
+ }
+
+ /**
+ * 画数据
+ *
+ * @param pathCanvas
+ * @param canvas
+ * @param paint
+ * @param col
+ * @param data
+ */
+ private fun drawDataLine(context: Context, pathCanvas: Path, canvas: Canvas, paint: Paint, col: Float, data: List) {
+ val size = data.size
+ paint.strokeWidth = strokeWidthLine.toFloat()
+ paint.color = Color.BLACK
+ var i = 0
+ out@ while (i < col) {
+ val startPoint = i * maxXSize
+ `in`@ for (j in 0 until maxXSize) {
+ if (startPoint + j >= size) break@out
+ val y = data[startPoint + j]
+ if (j == 0) pathCanvas.moveTo(startX.toFloat(), i * height * 5 + getCanvasY(y.toDouble(), context))
+ pathCanvas.lineTo(startX + j * (endX - startX) / maxXSize, i * height * 5 + getCanvasY(y.toDouble(), context))
+ }
+ i++
+ }
+ canvas.drawPath(pathCanvas, paint)
+ }
+
+ private fun getCanvasY(value: Double, context: Context): Float {
+ val height = (5 * 10 / 16000f).toDouble()
+ return dip2px(context, (height * (8000 - value)).toFloat()) + startY
+ }
+
+ /**
+ * 根据手机的分辨率从 dp 的单位 转成为 px(像素)
+ */
+ fun dip2px(context: Context, dpValue: Float): Int {
+ val scale = context.resources.displayMetrics.density
+ return (dpValue * scale + 0.5f).toInt()
+ }
+}
\ No newline at end of file
diff --git a/android/blesdk_2301/src/main/java/com/jstyle/blesdk2301/callback/BleConnectionListener.kt b/android/blesdk_2301/src/main/java/com/jstyle/blesdk2301/callback/BleConnectionListener.kt
new file mode 100644
index 00000000..3038490e
--- /dev/null
+++ b/android/blesdk_2301/src/main/java/com/jstyle/blesdk2301/callback/BleConnectionListener.kt
@@ -0,0 +1,10 @@
+package com.jstyle.blesdk2301.callback
+
+interface BleConnectionListener {
+ fun BleStatus(status: Int, newState: Int) //蓝牙4.0连接状态 Bluetooth 4.0 connection status
+ fun ConnectionSucceeded() //连接设备成功 Successfully connected the device
+ fun Connecting() //设备连接中 Device is connected
+ fun ConnectionFailed() //设备连接失败 Device connection failed
+ fun OnReconnect() //重新连接中 Reconnecting
+ fun BluetoothSwitchIsTurnedOff() //蓝牙开关被关闭 Bluetooth switch is turned off
+}
\ No newline at end of file
diff --git a/android/blesdk_2301/src/main/java/com/jstyle/blesdk2301/callback/DataListener2301.kt b/android/blesdk_2301/src/main/java/com/jstyle/blesdk2301/callback/DataListener2301.kt
new file mode 100644
index 00000000..6e410b23
--- /dev/null
+++ b/android/blesdk_2301/src/main/java/com/jstyle/blesdk2301/callback/DataListener2301.kt
@@ -0,0 +1,9 @@
+package com.jstyle.blesdk2301.callback
+
+/**
+ * Created by Administrator on 2018/4/10.
+ */
+interface DataListener2301 {
+ fun dataCallback(maps: Map?)
+ fun dataCallback(value: ByteArray?)
+}
\ No newline at end of file
diff --git a/android/blesdk_2301/src/main/java/com/jstyle/blesdk2301/callback/OnScanResults.kt b/android/blesdk_2301/src/main/java/com/jstyle/blesdk2301/callback/OnScanResults.kt
new file mode 100644
index 00000000..9c0feee3
--- /dev/null
+++ b/android/blesdk_2301/src/main/java/com/jstyle/blesdk2301/callback/OnScanResults.kt
@@ -0,0 +1,8 @@
+package com.jstyle.blesdk2301.callback
+
+import com.jstyle.blesdk2301.model.Device
+
+interface OnScanResults {
+ fun Success(date: Device?)
+ fun Fail(code: Int)
+}
\ No newline at end of file
diff --git a/android/blesdk_2301/src/main/java/com/jstyle/blesdk2301/cmdenum/YhDeviceData.kt b/android/blesdk_2301/src/main/java/com/jstyle/blesdk2301/cmdenum/YhDeviceData.kt
new file mode 100644
index 00000000..8ab6c74a
--- /dev/null
+++ b/android/blesdk_2301/src/main/java/com/jstyle/blesdk2301/cmdenum/YhDeviceData.kt
@@ -0,0 +1,24 @@
+package com.jstyle.blesdk2301.cmdenum
+
+import java.io.Serializable
+
+class YhDeviceData : Serializable {
+ var dataType = 0 // is index
+ var data: String? = "" //data object
+ var isDataEnd = false //Is it over?
+ fun srgetDeviceData(): String {
+ return if (data == null) "" else data!!
+ }
+
+ fun srsetData(data: String?) {
+ this.data = data
+ }
+
+ override fun toString(): String {
+ return "YhDeviceData{" +
+ "dataType=" + dataType +
+ ", data=" + data +
+ ", dataEnd=" + isDataEnd +
+ '}'
+ }
+}
\ No newline at end of file
diff --git a/android/blesdk_2301/src/main/java/com/jstyle/blesdk2301/constant/BleConst.kt b/android/blesdk_2301/src/main/java/com/jstyle/blesdk2301/constant/BleConst.kt
new file mode 100644
index 00000000..d8a5b241
--- /dev/null
+++ b/android/blesdk_2301/src/main/java/com/jstyle/blesdk2301/constant/BleConst.kt
@@ -0,0 +1,128 @@
+package com.jstyle.blesdk2301.constant
+
+/**
+ * Created by Administrator on 2020/4/9.
+ */
+object BleConst {
+ const val GetDeviceTime = "0"
+ const val SetDeviceTime = "1"
+ const val GetPersonalInfo = "2"
+ const val SetPersonalInfo = "3"
+ const val GetDeviceInfo = "4"
+ const val SetDeviceInfo = "5" //设置手环基本参数
+ const val CMD_Set_Mac = "6"
+ const val GetStepGoal = "7"
+ const val SetStepGoal = "8"
+ const val GetDeviceBatteryLevel = "9"
+ const val GetDeviceMacAddress = "10"
+ const val GetDeviceVersion = "11"
+ const val CMD_Reset = "12"
+ const val CMD_MCUReset = "13"
+ const val SetMotorVibrationWithTimes = "14"
+ const val GetDeviceName = "15"
+ const val GetAutomatic = "16"
+ const val SetAutomatic = "17"
+ const val GetAlarmClock = "18"
+ const val SetAlarmClockWithAllClock = "19"
+ const val Notify = "20"
+ const val GetSedentaryReminder = "21"
+ const val SetSedentaryReminder = "22"
+ const val RealTimeStep = "23"
+ const val GetTotalActivityData = "24"
+ const val GetDetailActivityData = "25"
+ const val GetDetailSleepData = "26"
+ const val GetDynamicHR = "27"
+ const val GetStaticHR = "28"
+ const val GetActivityModeData = "29"
+ const val EnterActivityMode = "30"
+ const val DeviceSendDataToAPP = "31"
+ const val BackHomeView = "35"
+ const val GetTempHistoryData = "36"
+ const val ECGDATA = "37"
+ const val EcgppGstatus = "38"
+ const val EcgppG = "39"
+ const val GPSControlCommand = "40"
+ const val Gps = "41" //手环发送给app
+ const val GetHRVData = "42"
+ const val CMD_Set_TemperatureCorrection = "43"
+ const val Weather = "44"
+ const val Braceletdial = "45"
+ const val Braceletdialok = "46"
+ const val SportMode = "47"
+ const val GetSportMode = "48"
+ const val CMD_Set_WorkOutReminder = "49"
+ const val CMD_Get_WorkOutReminder = "50"
+ const val ReadSerialNumber = "51"
+ const val ECG = "53"
+ const val Clear_Bracelet_data = "54"
+ const val Blood_oxygen = "55"
+ const val SocialdistanceGetting = "56"
+ const val SocialdistanceSetting = "57"
+ const val Sos = "58"
+ const val Temperature_history = "59"
+ const val SetDialinterface = "60"
+ const val HeartBeatpacket = "61"
+ const val GetAxillaryTemperatureDataWithMode = "62"
+ const val GetMAC = "63"
+ const val Getppg = "64"
+ const val CloseECGPPG = "65"
+ const val GetEcgPpgStatus = "66"
+ const val ECGdata = "67"
+ const val GetAutomaticSpo2Monitoring = "68"
+ const val SetSpo2 = "69"
+ const val GEtSpo2 = "70"
+ const val DeleteECGdata = "71"
+ const val DeleteHrv = "72"
+ const val MeasurementHrvCallback = "73"
+ const val MeasurementHeartCallback = "74"
+ const val MeasurementOxygenCallback = "75"
+ const val StopMeasurementHrvCallback = "76"
+ const val StopMeasurementHeartCallback = "77"
+ const val StopMeasurementOxygenCallback = "78"
+ const val ExitQRcode = "79"
+ const val EnterQRcode = "80"
+ const val QRcodebandBack = "81"
+ const val SportData = "82"
+ const val Delete_Obtain_The_data_of_manual_blood_oxygen_test = "83"
+ const val Delete_Blood_oxygen = "84"
+ const val Delete_GetTotalActivityData = "85"
+ const val Delete_AlarmClock = "87"
+ const val Delete_ActivityModeData = "88"
+ const val Delete_GetStaticHR = "89"
+ const val Delete_GetDynamicHR = "90"
+ const val deleteGetTemperature_historyDataWithMode = "91"
+ const val deleteGetAxillaryTemperatureDataWithMode = "92"
+ const val deleteGetDetailActivityDataWithMode = "93"
+ const val Delete_GetDetailSleepData = "94"
+ const val Enter_photo_mode = "95"
+ const val openRRInterval = "96"
+ const val closeRRInterval = "97"
+ const val realtimeRRIntervalData = "98"
+ const val realtimePPGData = "99"
+ const val realtimePPIData = "100"
+ const val ppgWithMode = "101"
+ const val ppgStartSucessed = "102"
+ const val ppgStartFailed = "103"
+ const val ppgStop = "104"
+ const val ppgQuit = "105"
+ const val ppgMeasurementProgress = "106"
+ const val ppgResult = "107"
+ const val SetBloodpressure_calibration = "108"
+ const val GetBloodpressure_calibration = "109"
+ const val SetWomenHealth = "110"
+ const val GetWomenHealth = "111"
+ const val SetPregnancyCycle = "112"
+ const val GetPregnancyCycle = "113"
+ const val Getpeisu = "114"
+ const val GetpeisuER = "115"
+ const val CMD_Set_Name = "116"
+ const val CMD_Get_Name = "117"
+ const val Blood_glucose_status = "118"
+ const val Blood_glucose_data = "119"
+ const val FindActivityMode = "120"
+ const val FindMobilePhoneMode = "FindMobilePhoneMode"
+ const val RejectTelMode = "RejectTelMode"
+ const val TelMode = "TelMode"
+ const val ECGResult = "ECGResult"
+ const val ENTERECG = "ENTERECG"
+}
\ No newline at end of file
diff --git a/android/blesdk_2301/src/main/java/com/jstyle/blesdk2301/constant/DeviceConst.kt b/android/blesdk_2301/src/main/java/com/jstyle/blesdk2301/constant/DeviceConst.kt
new file mode 100644
index 00000000..ed9d8add
--- /dev/null
+++ b/android/blesdk_2301/src/main/java/com/jstyle/blesdk2301/constant/DeviceConst.kt
@@ -0,0 +1,37 @@
+package com.jstyle.blesdk2301.constant
+
+/**
+ * Created by Administrator on 2018/1/17.
+ */
+object DeviceConst {
+ const val CMD_SET_TIME = 0x01.toByte()
+ const val CMD_GET_TIME = 0x41.toByte()
+ const val CMD_Set_UseInfo = 0x02.toByte()
+ const val CMD_GET_USERINFO = 0x42.toByte()
+ const val CMD_Set_DeviceID = 0x05.toByte()
+ const val CMD_Enable_Activity = 0x09.toByte() //
+ const val CMD_Get_BatteryLevel = 0x13.toByte() //
+ const val CMD_Get_Address = 0x22.toByte()
+ const val CMD_Get_Version = 0x27.toByte()
+ const val CMD_Reset = 0x12.toByte()
+ const val CMD_Mcu_Reset = 0x2e.toByte()
+ const val CMD_Set_Auto = 0x2a.toByte()
+ const val CMD_Get_Auto = 0x2b.toByte()
+ const val CMD_Get_TotalData = 0x51.toByte() //
+ const val CMD_Get_DetailData = 0x52.toByte()
+ const val CMD_Get_SleepData = 0x53.toByte()
+ const val CMD_Get_HeartData = 0x54.toByte()
+ const val CMD_Get_OnceHeartData = 0x55.toByte()
+ const val CMD_Get_HrvTestData = 0x56.toByte() //
+ const val ReadTempHisrory = 0x62.toByte()
+ const val MeasurementWithType = 0x28.toByte()
+ const val Oxygen_data = 0x66.toByte()
+ const val CMD_Start_EXERCISE = 0x19.toByte()
+ const val CMD_heart_package = 0x17.toByte()
+ const val CMD_HeartPackageFromDevice = 0x18.toByte() //
+ const val CMD_Set_Name = 0x3d.toByte()
+ const val CMD_Get_Name = 0x3e.toByte()
+ const val CMD_Get_SPORTData = 0x5C.toByte()
+ const val CMD_Get_Bloodsugar = 0x78.toByte()
+ const val Bloodsugar_data = 0x3a.toByte() //血糖数据
+}
\ No newline at end of file
diff --git a/android/blesdk_2301/src/main/java/com/jstyle/blesdk2301/constant/DeviceKey.kt b/android/blesdk_2301/src/main/java/com/jstyle/blesdk2301/constant/DeviceKey.kt
new file mode 100644
index 00000000..62659f4a
--- /dev/null
+++ b/android/blesdk_2301/src/main/java/com/jstyle/blesdk2301/constant/DeviceKey.kt
@@ -0,0 +1,373 @@
+package com.jstyle.blesdk2301.constant
+
+/**
+ * Created by Administrator on 2018/4/10.
+ */
+object DeviceKey {
+ const val heartValue = "heartValue"
+ const val hrvValue = "hrvValue"
+ const val Quality = "Quality"
+ const val HangUp = "HangUp"
+ const val Telephone = "Telephone"
+ const val Photograph = "Photograph"
+ const val CanclePhotograph = "CanclePhotograph"
+ const val type = "type"
+ const val Play = "Play"
+ const val Suspend = "Suspend"
+ const val LastSong = "LastSong"
+ const val NextSong = "NextSong"
+ const val VolumeReduction = "VolumeReduction"
+ const val VolumeUp = "VolumeUp"
+ const val FindYourPhone = "FindYourPhone"
+ const val Cancle_FindPhone = "Cancle_FindPhone"
+ const val SOS = "SOS"
+ const val DataType = "dataType"
+ const val enterActivityModeSuccess = "enterActivityModeSuccess"
+ const val Type = "Type"
+ const val Status = "Status"
+ const val RRIntervalData = "RRIntervalData"
+ const val Manual = "Manual"
+ const val automatic = "automatic"
+ const val Data = "dicData"
+ const val End = "dataEnd"
+ const val index = "index"
+ const val scanInterval = "scanInterval"
+ const val scanTime = "scanTime"
+ const val signalStrength = "signalStrength"
+ const val arrayX = "arrayX"
+ const val arrayY = "arrayY"
+ const val arrayZ = "arrayZ"
+ const val arrayPpgRawData = "arrayPpgRawData"
+ const val KGpsResCheck0 = "KGpsResCheck0" // 设备时间 GET_DEVICE_Time
+ const val KGpsResCheck1 = "KGpsResCheck1" // 设备时间
+ const val Band = "Band"
+ const val KFinishFlag = "finish"
+ const val DeviceTime = "strDeviceTime" // 设备时间 GET_DEVICE_Time
+ const val GPSTime = "gpsExpirationTime" // 设备时间 GET_DEVICE_Time
+ const val TimeZone = "TimeZone" // 设备时间 GET_DEVICE_Time
+ const val KHrvBloodHighPressure = "KHrvBloodHighPressure"
+ const val KHrvBloodLowPressure = "KHrvBloodLowPressure"
+
+ /*
+ * GET_PERSONAL_INFO
+ * sex 性别
+ * Age 年龄
+ * Height 身高
+ * Weight 体重
+ * stepLength 步长
+ * deviceId 设备ID
+ */
+ const val Gender = "MyGender"
+ const val Age = "MyAge"
+ const val Height = "MyHeight"
+ const val Weight = "MyWeight"
+ const val Stride = "MyStride"
+ const val KUserDeviceId = "deviceId"
+
+ /*
+ * GET_DEVICE_INFO
+ * distanceUnit 距离单位
+ * hourState 12小时24小时显示
+ * handleEnable 抬手检查使能标志
+ * handleSign 抬手检测左右手标志
+ * screenState 横竖屏显示
+ * anceEnable ANCS使能开关
+ */
+ const val DistanceUnit = "distancUnit"
+ const val TimeUnit = "timeUnit"
+ const val TempUnit = "temperatureUnit"
+ const val WristOn = "wristOn"
+ const val TemperatureUnit = "TemperatureUnit"
+ const val NightMode = "NightMode"
+ const val LeftOrRight = "handleSign"
+
+ //public static final String ScreenShow = "screenState";
+ const val Dialinterface = "dialinterface"
+ const val SocialDistancedwitch = "SocialDistancedwitch"
+ const val ChineseOrEnglish = "ChineseOrEnglish"
+ const val Lauage = "Lauage"
+ const val ScreenBrightness = "dcreenBrightness"
+ const val KBaseHeart = "baseHeartRate"
+ const val isHorizontalScreen = "isHorizontalScreen"
+ const val Year = "Year"
+ const val Month = "Month"
+ const val Day = "Day"
+ const val MenstrualPeriod_Lenth = "MenstrualPeriod_Lenth"
+ const val MenstrualPeriod_Period = "MenstrualPeriod_Period"
+
+ /*
+ * SET_STEP_MODEL
+ *totalSteps 总步数
+ *calories 卡路里
+ *distance 距离
+ *time 时间
+ *heartValue 心率值
+ */
+ const val Step = "step"
+ const val Calories = "calories"
+ const val Distance = "distance"
+ const val ExerciseMinutes = "exerciseMinutes"
+ const val HeartRate = "heartRate"
+ const val ActiveMinutes = "ExerciseTime"
+ const val TempData = "TempData"
+ const val StepGoal = "stepGoal" // 目标步数值 GET_GOAL
+ const val BatteryLevel = "batteryLevel" // 电量级别 READ_DEVICE_BATTERY
+ const val Chargingstate = "Chargingstate" // 充电状态
+ const val Voltage_value = "VoltageValue" // 电压值
+ const val MacAddress = "macAddress" // MAC地址 READ_MAC_ADDRESS
+ const val DeviceVersion = "deviceVersion" // 版本号 READ_VERSION
+ const val DeviceName = "deviceName" // 设备名称 GET_DEVICE_NAME
+ const val TemperatureCorrectionValue = "TemperatureCorrectionValue" // 设备名称 GET_DEVICE_NAME
+
+ /*
+ * GET_AUTOMIC_HEART
+ *workModel 工作模式
+ *heartStartHour 开始运动时间的小时
+ *heartStartMinter 开始运动时间的分钟
+ *heartEndHour 结束运动时间的小时
+ *heartEndMinter 结束运动时间的分钟
+ *heartWeek 星期使能
+ *workTime 工作模式时间
+ */
+ const val WorkMode = "workModel"
+ const val StartTime = "heartStartHour"
+ const val KHeartStartMinter = "heartStartMinter"
+ const val EndTime = "heartEndHour"
+ const val KHeartEndMinter = "heartEndMinter"
+ const val Weeks = "weekValue"
+ const val IntervalTime = "intervalTime"
+ const val Time = "Time"
+ const val PPG = "PPG"
+
+ /*
+ * READ_SPORT_PERIOD
+ *StartTimeHour 开始运动时间的小时
+ *StartTimeMin 开始运动时间的分钟
+ *EndTimeHour 结束运动时间的小时
+ *EndTimeMin 结束运动时间的分钟
+ *Week 星期使能
+ *KSportNotifierTime 运动提醒周期
+ */
+ const val StartTimeHour = "sportStartHour"
+ const val StartTimeMin = "sportStartMinter"
+ const val EndTimeHour = "sportEndHour"
+ const val EndTimeMin = "sportEndMinter"
+ const val LeastSteps = "leastSteps"
+
+ /*
+ * GET_STEP_DATA
+ *historyDate 日期:年月日
+ *historySteps 步数
+ *historyTime 运动时间
+ *historyDistance 距离
+ *Calories 卡路里
+ *historyGoal 目标
+ */
+ const val Date = "date"
+ const val Size = "size"
+ const val Goal = "goal"
+
+ /*
+ * GET_STEP_DETAIL
+ *Date 日期:年月日时分秒
+ *ArraySteps 步数
+ *Calories 卡路里
+ *Distance 距离
+ *KDetailMinterStep 10分钟内每一分钟的步数
+ */
+ const val ArraySteps = "arraySteps"
+ const val KDetailMinterStep = "detailMinterStep"
+ const val temperature = "temperature"
+ const val axillaryTemperature = "axillaryTemperature"
+
+ /*
+ * GET_SLEEP_DETAIL
+ *Date 日期:年月日时分秒
+ *KSleepLength 睡眠数据的长度
+ *ArraySleep 5分钟的睡眠质量 (总共24个数据,每一个数据代表五分钟)
+ */
+ const val KSleepLength = "sleepLength"
+ const val ArraySleep = "arraySleepQuality"
+ const val sleepUnitLength = "sleepUnitLength" //是不是一分钟的睡眠数据 1为1分钟数据 0为5分钟数据
+
+ /*
+ * GET_HEART_DATA
+ *Date 日期:年月日时分秒
+ *ArrayDynamicHR 10秒一个心率值,总共12个心率值
+ */
+ const val ArrayDynamicHR = "arrayDynamicHR"
+ const val Blood_oxygen = "Blood_oxygen"
+
+ /*
+ * GET_ONCE_HEARTDATA
+ *Date 日期:年月日时分秒
+ *StaticHR 心率值
+ */
+ const val StaticHR = "onceHeartValue"
+
+ /*
+ * GET_HRV_DATA
+ *Date 日期:年月日时分秒
+ *HRV HRV值
+ *VascularAging 血管老化度值
+ *HeartRate 心率值
+ *Stress 疲劳度
+ */
+ const val HRV = "hrv"
+ const val VascularAging = "vascularAging"
+ const val Fatiguedegree = "fatigueDegree"
+ const val Stress = "stress"
+ const val HighPressure = "highPressure"
+ const val LowPressure = "lowPressure"
+ const val highBP = "highBP"
+ const val lowBP = "lowBP"
+
+ /*
+ *GET_ALARM
+ *KAlarmId 0到4闹钟编号
+ *ClockType 闹钟类型
+ *ClockTime 闹钟时间的小时
+ *KAlarmMinter 闹钟时间的分钟
+ *Week 星期使能
+ *KAlarmLength 长度
+ *KAlarmContent 文本的内容
+ */
+ const val KAlarmId = "alarmId"
+ const val OpenOrClose = "clockOpenOrClose"
+ const val ClockType = "clockType"
+ const val ClockTime = "alarmHour"
+ const val KAlarmMinter = "alarmMinter"
+ const val Week = "weekValue"
+ const val KAlarmLength = "alarmLength"
+ const val KAlarmContent = "dicClock"
+
+ /***********************GET_HRV_TESTDATA */ /*
+ *KBloodTestLength 数据长度
+ *KBloodTestProgress 进度
+ *KBloodTestValue 本次PPG获得的值
+ *KBolldTestCurve 本次波型的高度
+ */
+ const val KBloodTestLength = "bloodTestLength"
+ const val KBloodTestProgress = "bloodTestProgress"
+ const val KBloodTestValue = "bloodTestValue"
+ const val KBloodTestCurve = "bloodTestCurve"
+
+ /*
+ *KBloodResultPercent 反弹的百分比
+ *KBloodResultRebound 平均反弹高度
+ *KBloodResultMax 最大高度
+ *KBloodResultRank 结果级别(1到6)
+ */
+ const val KBloodResultPercent = "bloodPercent"
+ const val KBloodResultRebound = "bloodRebound"
+ const val KBloodResultMax = "bloodResultMax"
+ const val KBloodResultRank = "bloodResultRank"
+
+ /*
+ *KHrvTestProgress 进度
+ *KHrvTestWidth 本次心跳的宽度
+ *KHrvTestValue 心率值
+ */
+ const val KHrvTestProgress = "hrvTestProgress"
+ const val KHrvTestWidth = "hrvTestWidth"
+ const val KHrvTestValue = "hrvTestValue"
+
+ /*
+ *KHrvResultState SDNN结果 如果是0,说明检测失败
+ *KHrvResultAvg SDNN平均值
+ *KHrvResultTotal 总SDNN结果
+ *KHrvResultCount 有效数据个数
+ *KHrvResultTired 疲劳指数据
+ *KHrvResultValue 心率值
+ */
+ const val KHrvResultState = "hrvResultState"
+ const val KHrvResultAvg = "hrvResultAvg"
+ const val KHrvResultTotal = "hrvResultTotal"
+ const val KHrvResultCount = "hrvResultCount"
+ const val KHrvResultTired = "hrvResultTired"
+ const val KHrvResultValue = "hrvResultValue"
+
+ /*
+ *KDisturbState 1:开始运动 0:停止运动
+ *KSlipHand 1: 带在手上 0;脱手。
+ *KPPGData PPG的波型值
+ */
+ const val KDisturbState = "disturbState"
+ const val KSlipHand = "slipHand"
+ const val KPPGData = "ppgData"
+ const val KPPIData = "ppiData"
+ const val ppgResult = "ppgResult"
+ const val ppgStartSucessed = "ppgStartSucessed"
+ const val ppgStartFailed = "ppgStartFailed"
+ const val ppgStop = "ppgStop"
+ const val ppgQuit = "ppgQuit"
+ const val ppgMeasurementProgress = "ppgMeasurementProgress"
+
+ /*
+ *@param Date 时间:年月日时分秒
+ *@param Latitude 纬度数据
+ *@param Longitude 经度数据
+ */
+ const val Latitude = "locationLatitude"
+ const val Longitude = "locationLongitude"
+ const val KActivityLocationTime = "ActivityLocationTIme"
+ const val KActivityLocationLatitude = "ActivityLocationLatitude"
+ const val KActivityLocationLongitude = "ActivityLocationLongitude"
+ const val KActivityLocationCount = "KActivityLocationCount"
+
+ /*
+ * GET_SPORTMODEL_DATA
+ *@param Date 时间:年月日时分秒
+ *@param ActivityMode 运动类型
+ 0=Run,
+ 1=Cycling,
+ 2=Swimming,
+ 3=Badminton,
+ 4=Football,
+ 5=Tennis,
+ 6=Yoga,
+ 7=Medication,
+ 8=Dance
+
+ *@param HeartRate 心率
+ *@param ActiveMinutes 运动时间
+ *@param Step 运动步数
+ *@param Pace 运动速度
+ *@param Calories 卡路里
+ *@param Distance 距离
+ */
+ const val ActivityMode = "sportModel"
+ const val Pace = "sportModelSpeed"
+ const val KDataID = "KDataID"
+ const val KPhoneDataLength = "KPhoneDataLength"
+ const val KClockLast = "KClockLast"
+ const val TakePhotoMode = "TakePhotoMode"
+ const val KFunction_tel = "TelMode"
+ const val KFunction_reject_tel = "RejectTelMode"
+ const val FindMobilePhoneMode = "FindMobilePhoneMode"
+ const val KEnable_exercise = "KEnable_exercise"
+ const val ECGQualityValue = "ECGQualityValue"
+ const val ECGResultValue = "ECGResultVALUE"
+ const val ECGHrvValue = "ECGHrvValue"
+ const val ECGAvBlockValue = "ECGAvBlockValue"
+ const val ECGHrValue = "ECGHrValue"
+ const val PPGHrValue = "PPGHrValue"
+ const val ECGStreesValue = "ECGStreesValue"
+ const val ECGhighBpValue = "ECGhighBpValue"
+ const val ECGLowBpValue = "ECGLowBpValue"
+ const val ECGMoodValue = "ECGMoodValue"
+ const val ECGBreathValue = "ECGBreathValue"
+ const val KEcgDataString = "KEcgDataString" // ecg
+ const val ECGValue = "ECGValue"
+ const val PPGValue = "PPGValue"
+ const val EcgStatus = "EcgStatus"
+ const val EcgSBP = "PPGSBP"
+ const val EcgDBP = "PPGDBP"
+ const val EcgHR = "PPGHR"
+ const val WaveformDownTime = "WaveformDownTime"
+ const val WaveformRiseTime = "WaveformRiseTime"
+ const val EcgGender = "Gender"
+ const val EcgAge = "Age"
+ const val EcgHeight = "Height"
+ const val EcgWeight = "Weight"
+}
\ No newline at end of file
diff --git a/android/blesdk_2301/src/main/java/com/jstyle/blesdk2301/model/AutoMode.kt b/android/blesdk_2301/src/main/java/com/jstyle/blesdk2301/model/AutoMode.kt
new file mode 100644
index 00000000..103c1a8b
--- /dev/null
+++ b/android/blesdk_2301/src/main/java/com/jstyle/blesdk2301/model/AutoMode.kt
@@ -0,0 +1,5 @@
+package com.jstyle.blesdk2301.model
+
+enum class AutoMode {
+ AutoHeartRate, AutoSpo2, AutoTemp, AutoHrv
+}
\ No newline at end of file
diff --git a/android/blesdk_2301/src/main/java/com/jstyle/blesdk2301/model/AutoTestMode.kt b/android/blesdk_2301/src/main/java/com/jstyle/blesdk2301/model/AutoTestMode.kt
new file mode 100644
index 00000000..8a3c44e1
--- /dev/null
+++ b/android/blesdk_2301/src/main/java/com/jstyle/blesdk2301/model/AutoTestMode.kt
@@ -0,0 +1,5 @@
+package com.jstyle.blesdk2301.model
+
+enum class AutoTestMode {
+ AutoHeartRate, AutoSpo2
+}
\ No newline at end of file
diff --git a/android/blesdk_2301/src/main/java/com/jstyle/blesdk2301/model/Clock.kt b/android/blesdk_2301/src/main/java/com/jstyle/blesdk2301/model/Clock.kt
new file mode 100644
index 00000000..06d89337
--- /dev/null
+++ b/android/blesdk_2301/src/main/java/com/jstyle/blesdk2301/model/Clock.kt
@@ -0,0 +1,35 @@
+package com.jstyle.blesdk2301.model
+
+import java.io.Serializable
+
+/**
+ * Created by Administrator on 2018/1/16.
+ */
+class Clock : SendData(), Serializable {
+ var number = 0
+ var type = 0
+ var hour = 0
+ var minute = 0
+ var week: Byte = 0
+ var content: String? = null
+ var isEnable = false
+ fun srgetWeek(): Int {
+ return week.toInt()
+ }
+
+ fun srsetWeek(week: Byte) {
+ this.week = week
+ }
+
+ override fun toString(): String {
+ return "Clock{" +
+ "number=" + number +
+ ", type=" + type +
+ ", hour=" + hour +
+ ", minute=" + minute +
+ ", week=" + week +
+ ", content='" + content + '\'' +
+ ", enable=" + isEnable +
+ '}'
+ }
+}
\ No newline at end of file
diff --git a/android/blesdk_2301/src/main/java/com/jstyle/blesdk2301/model/Device.kt b/android/blesdk_2301/src/main/java/com/jstyle/blesdk2301/model/Device.kt
new file mode 100644
index 00000000..c5be38cc
--- /dev/null
+++ b/android/blesdk_2301/src/main/java/com/jstyle/blesdk2301/model/Device.kt
@@ -0,0 +1,50 @@
+package com.jstyle.blesdk2301.model
+
+import android.bluetooth.BluetoothDevice
+import java.io.Serializable
+import java.util.concurrent.ScheduledThreadPoolExecutor
+
+class Device : Serializable {
+ var name: String? = null //设备名字
+ var mac: String? = null //设备的mac
+ var productID = 0 //产品id,预留
+ var bluetoothDevice: BluetoothDevice? = null //蓝牙
+ var isIsconted = false //蓝牙是否连接
+ var isPaired = false //是否是配对设备
+ var isIsdfu = false //是否直接升级
+ var riss = 100
+ var time: ScheduledThreadPoolExecutor? = null //每一个设备轮询器
+ fun srsetIsdfu(isdfu: Boolean) {
+ isIsdfu = isdfu
+ }
+
+ fun srsetIsconted(isconted: Boolean) {
+ isIsconted = isconted
+ }
+
+ fun srgetName(): String {
+ return if (name == null) "" else name!!
+ }
+
+ fun srsetName(name: String?) {
+ this.name = name
+ }
+
+ fun srgetMac(): String {
+ return if (mac == null) "" else mac!!
+ }
+
+ fun srsetMac(mac: String?) {
+ this.mac = mac
+ }
+
+ override fun toString(): String {
+ return "Device{" +
+ "name='" + name + '\'' +
+ ", mac='" + mac + '\'' +
+ ", productID=" + productID +
+ ", getBluetoothDevice=" + bluetoothDevice +
+ ", isconted=" + isIsconted +
+ '}'
+ }
+}
\ No newline at end of file
diff --git a/android/blesdk_2301/src/main/java/com/jstyle/blesdk2301/model/DeviceBean.kt b/android/blesdk_2301/src/main/java/com/jstyle/blesdk2301/model/DeviceBean.kt
new file mode 100644
index 00000000..af1c3894
--- /dev/null
+++ b/android/blesdk_2301/src/main/java/com/jstyle/blesdk2301/model/DeviceBean.kt
@@ -0,0 +1,9 @@
+package com.jstyle.blesdk2301.model
+
+/**
+ * Created by Administrator on 2018/4/28.
+ */
+class DeviceBean {
+ var dataList: List