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>? = null + var isFinish = false +} \ No newline at end of file diff --git a/android/blesdk_2301/src/main/java/com/jstyle/blesdk2301/model/DeviceName.kt b/android/blesdk_2301/src/main/java/com/jstyle/blesdk2301/model/DeviceName.kt new file mode 100644 index 00000000..187a17a6 --- /dev/null +++ b/android/blesdk_2301/src/main/java/com/jstyle/blesdk2301/model/DeviceName.kt @@ -0,0 +1,8 @@ +package com.jstyle.blesdk2301.model + +/** + * Created by Administrator on 2018/4/9. + */ +class DeviceName : SendData() { + var name: String? = null +} \ No newline at end of file diff --git a/android/blesdk_2301/src/main/java/com/jstyle/blesdk2301/model/ExerciseMode.kt b/android/blesdk_2301/src/main/java/com/jstyle/blesdk2301/model/ExerciseMode.kt new file mode 100644 index 00000000..5017fa2d --- /dev/null +++ b/android/blesdk_2301/src/main/java/com/jstyle/blesdk2301/model/ExerciseMode.kt @@ -0,0 +1,41 @@ +package com.jstyle.blesdk2301.model + +/** + * Created by Administrator on 2018/8/2. + */ +class ExerciseMode : SendData() { + var exerciseMode = 0 + var enableStatus = 0 + fun getExerciseMode(position: Int): Int { + return modes[position] + } + + companion object { + const val Status_START = 1 + const val Status_PAUSE = 2 + const val Status_CONTUINE = 3 + const val Status_FINISH = 4 + const val Mode_RUN = 0 + const val Mode_CYCLING = 1 + const val Mode_BADMINTON = 2 + const val Mode_FOOTBALL = 3 + const val Mode_TENNIS = 4 + const val Mode_YOGA = 5 + const val Mode_BREATH = 6 + const val Mode_DANCE = 7 + const val Mode_BASKETBALL = 8 + const val Mode_Walk = 9 + const val Mode_workout = 10 + const val Mode_Cricket = 11 + const val Mode_Hiking = 12 + const val Mode_Aerobics = 13 + const val Mode_PingPong = 14 + const val Mode_RopeJump = 15 + const val Mode_Situps = 16 + const val Volleyball = 17 + var modes = intArrayOf( + Mode_RUN, Mode_CYCLING, Mode_BADMINTON, Mode_FOOTBALL, + Mode_TENNIS, Mode_YOGA, Mode_BREATH, Mode_DANCE, Mode_BASKETBALL, Mode_Walk, Mode_workout, Mode_Cricket, Mode_Hiking, Mode_Aerobics, Mode_PingPong, Mode_RopeJump, Mode_Situps, Volleyball + ) + } +} \ No newline at end of file diff --git a/android/blesdk_2301/src/main/java/com/jstyle/blesdk2301/model/ExtendedBluetoothDevice.kt b/android/blesdk_2301/src/main/java/com/jstyle/blesdk2301/model/ExtendedBluetoothDevice.kt new file mode 100644 index 00000000..382a47ec --- /dev/null +++ b/android/blesdk_2301/src/main/java/com/jstyle/blesdk2301/model/ExtendedBluetoothDevice.kt @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2015, Nordic Semiconductor + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.jstyle.blesdk2301.model + +import android.bluetooth.BluetoothDevice + +class ExtendedBluetoothDevice { + val device: BluetoothDevice + + /** The name is not parsed by some Android devices, f.e. Sony Xperia Z1 with Android 4.3 (C6903). It needs to be parsed manually. */ + var name: String + var rssi: Int + + constructor(device: BluetoothDevice, name: String, rssi: Int) { + this.device = device + this.name = name + this.rssi = rssi + } + + constructor(device: BluetoothDevice) { + this.device = device + name = device.name + rssi = NO_RSSI + } + + fun matches(mdevice: BluetoothDevice): Boolean { + return device.address == mdevice.address + } + + companion object { + /* package */ + const val NO_RSSI = -1000 + } +} \ No newline at end of file diff --git a/android/blesdk_2301/src/main/java/com/jstyle/blesdk2301/model/GetData.kt b/android/blesdk_2301/src/main/java/com/jstyle/blesdk2301/model/GetData.kt new file mode 100644 index 00000000..26fefd9a --- /dev/null +++ b/android/blesdk_2301/src/main/java/com/jstyle/blesdk2301/model/GetData.kt @@ -0,0 +1,16 @@ +package com.jstyle.blesdk2301.model + +/** + * Created by Administrator on 2018/4/9. + */ +class GetData : SendData() { + /** + * 99删除数据,0获取数据 + */ + var dataNum = DataNum_Last + + companion object { + const val DataNum_Delete = 0x99.toByte() + const val DataNum_Last = 0 + } +} \ No newline at end of file diff --git a/android/blesdk_2301/src/main/java/com/jstyle/blesdk2301/model/MyAutomaticHRMonitoring.kt b/android/blesdk_2301/src/main/java/com/jstyle/blesdk2301/model/MyAutomaticHRMonitoring.kt new file mode 100644 index 00000000..3d0f9c4a --- /dev/null +++ b/android/blesdk_2301/src/main/java/com/jstyle/blesdk2301/model/MyAutomaticHRMonitoring.kt @@ -0,0 +1,27 @@ +package com.jstyle.blesdk2301.model + +/** + * Created by Administrator on 2018/1/16. + */ +class MyAutomaticHRMonitoring : SendData() { + var open = 0 //1 开启整个时间段都测量 2时间段内间隔测量 0关闭 + + // 1. Enable measurement throughout the entire time period 2. Measure intervals within the time period 0. Close + var startHour = 0 //开始小时 Starting hours + var startMinute = 0 //开始分钟 Start minute + var endHour = 0 //结束小时 End Hour + var endMinute = 0 //结束分钟 End minute + var week = 0 //星期一到星期日 开启选择 Monday to Sunday open selection + var time = 0 //多少时间测试一次,单位是分钟。 How many times does it take to test, in minutes. + override fun toString(): String { + return "MyAutomaticHRMonitoring{" + + "open=" + open + + ", startHour=" + startHour + + ", startMinute=" + startMinute + + ", endHour=" + endHour + + ", endMinute=" + endMinute + + ", week=" + week + + ", time=" + time + + '}' + } +} \ No newline at end of file diff --git a/android/blesdk_2301/src/main/java/com/jstyle/blesdk2301/model/MyDeviceInfo.kt b/android/blesdk_2301/src/main/java/com/jstyle/blesdk2301/model/MyDeviceInfo.kt new file mode 100644 index 00000000..ea558eab --- /dev/null +++ b/android/blesdk_2301/src/main/java/com/jstyle/blesdk2301/model/MyDeviceInfo.kt @@ -0,0 +1,22 @@ +package com.jstyle.blesdk2301.model + +/** + * Created by Administrator on 2018/4/9. + */ +class MyDeviceInfo : SendData() { + var isDistanceUnit = false //1 mile(true),0km(false) + var isIs12Hour = false //1(12小时)(true),0(24小时制)(false) + var isBright_screen = false //1 turn on the bright screen, 0 turn off the bright screen + var isFahrenheit_or_centigrade = false //Fahrenheit or centigrade + var isHorizontalScreen = false //1 Horizontal screen, 0 Vertical screen + var isNight_mode = false //夜间模式 + var isTemperature_unit = false //夜间模式 + var isSocial_distance_switch = false //社交距离开关 Social distance switch + var lauageNumber = -1 //中英文切换 Chinese_English_switch + var baseheart = 60 //Basic heart rate + var screenBrightness = -1 //Screen brightness 屏幕亮度0-5 + var dialinterface = -1 //表盘更换 Dial replacement 0-10 + fun srsetIs12Hour(is12Hour: Boolean) { + isIs12Hour = is12Hour + } +} \ No newline at end of file diff --git a/android/blesdk_2301/src/main/java/com/jstyle/blesdk2301/model/MyDeviceTime.kt b/android/blesdk_2301/src/main/java/com/jstyle/blesdk2301/model/MyDeviceTime.kt new file mode 100644 index 00000000..8024f624 --- /dev/null +++ b/android/blesdk_2301/src/main/java/com/jstyle/blesdk2301/model/MyDeviceTime.kt @@ -0,0 +1,36 @@ +package com.jstyle.blesdk2301.model + +import java.util.Calendar + +/** + * Created by Administrator on 2018/1/16. + */ +class MyDeviceTime : SendData() { + var year: Int + var month: Int + var day: Int + var hour: Int + var minute: Int + var second: Int + + init { + val calendar = Calendar.getInstance() + year = calendar[Calendar.YEAR] + month = calendar[Calendar.MONTH] + 1 + day = calendar[Calendar.DAY_OF_MONTH] + hour = calendar[Calendar.HOUR_OF_DAY] + minute = calendar[Calendar.MINUTE] + second = calendar[Calendar.SECOND] + } + + override fun toString(): String { + return "MyDeviceTime{" + + "year=" + year + + ", month=" + month + + ", day=" + day + + ", hour=" + hour + + ", minute=" + minute + + ", second=" + second + + '}' + } +} \ No newline at end of file diff --git a/android/blesdk_2301/src/main/java/com/jstyle/blesdk2301/model/MyPersonalInfo.kt b/android/blesdk_2301/src/main/java/com/jstyle/blesdk2301/model/MyPersonalInfo.kt new file mode 100644 index 00000000..4ea4ba2f --- /dev/null +++ b/android/blesdk_2301/src/main/java/com/jstyle/blesdk2301/model/MyPersonalInfo.kt @@ -0,0 +1,21 @@ +package com.jstyle.blesdk2301.model + +/** + * Created by Administrator on 2018/1/16. + */ +class MyPersonalInfo : SendData() { + var sex = 0 //1 male,0female + var age = 0 + var height = 0 + var weight = 0 + var stepLength = 70 + override fun toString(): String { + return "MyPersonalInfo{" + + "sex=" + sex + + ", age=" + age + + ", height=" + height + + ", weight=" + weight + + ", stepLength=" + stepLength + + '}' + } +} \ No newline at end of file diff --git a/android/blesdk_2301/src/main/java/com/jstyle/blesdk2301/model/MySedentaryReminder.kt b/android/blesdk_2301/src/main/java/com/jstyle/blesdk2301/model/MySedentaryReminder.kt new file mode 100644 index 00000000..0024735f --- /dev/null +++ b/android/blesdk_2301/src/main/java/com/jstyle/blesdk2301/model/MySedentaryReminder.kt @@ -0,0 +1,27 @@ +package com.jstyle.blesdk2301.model + +/** + * Created by Administrator on 2018/1/16. + */ +class MySedentaryReminder : SendData() { + var startHour = 0 + var startMinute = 0 + var endHour = 0 + var endMinute = 0 + var week = 0 + var intervalTime = 0 + var leastStep = 0 + var isEnable = false + override fun toString(): String { + return "MySedentaryReminder{" + + "startHour=" + startHour + + ", startMinute=" + startMinute + + ", endHour=" + endHour + + ", endMinute=" + endMinute + + ", week=" + week + + ", intervalTime=" + intervalTime + + ", leastStep=" + leastStep + + ", enable=" + isEnable + + '}' + } +} \ No newline at end of file diff --git a/android/blesdk_2301/src/main/java/com/jstyle/blesdk2301/model/Notifier.kt b/android/blesdk_2301/src/main/java/com/jstyle/blesdk2301/model/Notifier.kt new file mode 100644 index 00000000..02d86037 --- /dev/null +++ b/android/blesdk_2301/src/main/java/com/jstyle/blesdk2301/model/Notifier.kt @@ -0,0 +1,33 @@ +package com.jstyle.blesdk2301.model + +/** + * Created by Administrator on 2018/1/16. + */ +class Notifier : SendData() { + var type = 0 + var info = "" + var title = "" + override fun toString(): String { + return "Notifier{" + + "type=" + type + + ", info='" + info + '\'' + + ", title='" + title + '\'' + + '}' + } + + companion object { + const val Data_Tel = 0 + const val Data_Sms = 1 + const val Data_WeChat = 2 + const val Data_Facebook = 3 + const val Data_Instagram = 4 + const val Data_Skype = 5 + const val Data_Telegram = 6 + const val Data_Twitter = 7 + const val Data_Vkclient = 8 + const val Data_WhatApp = 9 + const val Data_QQ = 10 + const val Data_IN = 11 + const val Data_Stop_Tel = 0xff + } +} \ No newline at end of file diff --git a/android/blesdk_2301/src/main/java/com/jstyle/blesdk2301/model/PregnancyCycle.kt b/android/blesdk_2301/src/main/java/com/jstyle/blesdk2301/model/PregnancyCycle.kt new file mode 100644 index 00000000..e2b3e016 --- /dev/null +++ b/android/blesdk_2301/src/main/java/com/jstyle/blesdk2301/model/PregnancyCycle.kt @@ -0,0 +1,36 @@ +package com.jstyle.blesdk2301.model + +/** + * 怀孕周期 + * 算法 开始到生产 280天 每7天算一周 + */ +class PregnancyCycle { + var address = "" + var userId = "" + var menstrualPeriod_StartTime = "1990-01-01" //经期开始时间 yyyy-MM-dd + var dueDateTime = "" //预产期 + var menstrualPeriod_Period: Long = 30 //经期周期,默认30天 21-35 + + constructor() + constructor( + address: String, userId: String, + MenstrualPeriod_StartTime: String, DueDateTime: String, + MenstrualPeriod_Period: Long + ) { + this.address = address + this.userId = userId + menstrualPeriod_StartTime = MenstrualPeriod_StartTime + dueDateTime = DueDateTime + menstrualPeriod_Period = MenstrualPeriod_Period + } + + override fun toString(): String { + return "PregnancyCycle{" + + "address='" + address + '\'' + + ", userId='" + userId + '\'' + + ", MenstrualPeriod_StartTime='" + menstrualPeriod_StartTime + '\'' + + ", DueDateTime='" + dueDateTime + '\'' + + ", MenstrualPeriod_Period=" + menstrualPeriod_Period + + '}' + } +} \ No newline at end of file diff --git a/android/blesdk_2301/src/main/java/com/jstyle/blesdk2301/model/SendData.kt b/android/blesdk_2301/src/main/java/com/jstyle/blesdk2301/model/SendData.kt new file mode 100644 index 00000000..ada386ee --- /dev/null +++ b/android/blesdk_2301/src/main/java/com/jstyle/blesdk2301/model/SendData.kt @@ -0,0 +1,6 @@ +package com.jstyle.blesdk2301.model + +/** + * Created by Administrator on 2018/4/8. + */ +open class SendData \ No newline at end of file diff --git a/android/blesdk_2301/src/main/java/com/jstyle/blesdk2301/model/SportPeriod.kt b/android/blesdk_2301/src/main/java/com/jstyle/blesdk2301/model/SportPeriod.kt new file mode 100644 index 00000000..ae258081 --- /dev/null +++ b/android/blesdk_2301/src/main/java/com/jstyle/blesdk2301/model/SportPeriod.kt @@ -0,0 +1,16 @@ +package com.jstyle.blesdk2301.model + +/** + * Created by Administrator on 2018/1/16. + */ +class SportPeriod : SendData() { + var startHour = 0 + var startMinute = 0 + var endHour = 0 + var endMinute = 0 + var week = 0 + var days = 0 + var intervalTime = 0 + var leastStep = 0 + var isEnable = false +} \ No newline at end of file diff --git a/android/blesdk_2301/src/main/java/com/jstyle/blesdk2301/model/StepModel.kt b/android/blesdk_2301/src/main/java/com/jstyle/blesdk2301/model/StepModel.kt new file mode 100644 index 00000000..505fee5d --- /dev/null +++ b/android/blesdk_2301/src/main/java/com/jstyle/blesdk2301/model/StepModel.kt @@ -0,0 +1,13 @@ +package com.jstyle.blesdk2301.model + +/** + * Created by Administrator on 2018/4/9. + */ +class StepModel : SendData() { + var isStepState = false //true开启实时计步,false停止实时计步 + override fun toString(): String { + return "StepModel{" + + "stepState=" + isStepState + + '}' + } +} \ No newline at end of file diff --git a/android/blesdk_2301/src/main/java/com/jstyle/blesdk2301/model/UserInfo.kt b/android/blesdk_2301/src/main/java/com/jstyle/blesdk2301/model/UserInfo.kt new file mode 100644 index 00000000..bba146d6 --- /dev/null +++ b/android/blesdk_2301/src/main/java/com/jstyle/blesdk2301/model/UserInfo.kt @@ -0,0 +1,29 @@ +package com.jstyle.blesdk2301.model + +/** + * Created by Administrator on 2022/2/18. + */ +class UserInfo { + var phone = "" + var name = "" + var gender: String? = null //0男,1女 + var age = "" //18 + var height = "" //175 + var weight = "" //75 + var ecgTitle = "" + var ecgReportTips = "" + var date = "" + override fun toString(): String { + return "UserInfo{" + + "phone='" + phone + '\'' + + ", name='" + name + '\'' + + ", gender='" + gender + '\'' + + ", age='" + age + '\'' + + ", height='" + height + '\'' + + ", weight='" + weight + '\'' + + ", ecgTitle='" + ecgTitle + '\'' + + ", ecgReportTips='" + ecgReportTips + '\'' + + ", date='" + date + '\'' + + '}' + } +} \ No newline at end of file diff --git a/android/blesdk_2301/src/main/java/com/jstyle/blesdk2301/model/WeatherData.kt b/android/blesdk_2301/src/main/java/com/jstyle/blesdk2301/model/WeatherData.kt new file mode 100644 index 00000000..5d24c23d --- /dev/null +++ b/android/blesdk_2301/src/main/java/com/jstyle/blesdk2301/model/WeatherData.kt @@ -0,0 +1,9 @@ +package com.jstyle.blesdk2301.model + +class WeatherData { + var tempNow = 0 + var tempHigh = 0 + var tempLow = 0 + var cityName = "" + var weatherId = 0 +} \ No newline at end of file diff --git a/android/blesdk_2301/src/main/java/com/jstyle/blesdk2301/model/WomenHealth.kt b/android/blesdk_2301/src/main/java/com/jstyle/blesdk2301/model/WomenHealth.kt new file mode 100644 index 00000000..f6ff4aae --- /dev/null +++ b/android/blesdk_2301/src/main/java/com/jstyle/blesdk2301/model/WomenHealth.kt @@ -0,0 +1,93 @@ +package com.jstyle.blesdk2301.model + +/** + * 女性健康 + */ +class WomenHealth { + var userId = "" + var address = "" + var menstrualPeriod_StartTime = "1990-01-01" //经期开始时间 yyyy-MM-dd + var menstrualPeriod_Lenth: Long = 7 //经期长度,默认7天,2-8 + var menstrualPeriod_Period: Long = 30 //经期周期,默认30天 21-35 + var flowRate: Long = -1 //流量 -1没有数据 0-4等级 + var dysmenorrhea: Long = -1 //痛经 -1没有数据 0-4等级 + var isLove = false //爱爱 + var mood: Long = -1 //心情 -1没有数据 0-4等级 + var isStart = false //是否是第一个日期 + var isYuejinqi = false //是否是月经期 经期七天 + var isYuceyuejinqi = false //预测月经期 经期前3天 + var isYiyunqi = false //是否是易孕期 经期七天后10天的日期 + var isYuceyiyunqi = false //预测易孕期 易孕期前3天 + var isPailuanqi = false //是否是排卵期 开始15后的一天 + var isUp = false //上升期还是下降器 + var isEdidt = false //是否被编辑过 + var day = 0 //每次循环的第几天 + var menstrual_symptoms = IntArray(32) //经期症状 + + constructor() + constructor( + userId: String, address: String, + MenstrualPeriod_StartTime: String, MenstrualPeriod_Lenth: Long, + MenstrualPeriod_Period: Long, FlowRate: Long, Dysmenorrhea: Long, + Love: Boolean, mood: Long, start: Boolean, yuejinqi: Boolean, + yuceyuejinqi: Boolean, yiyunqi: Boolean, yuceyiyunqi: Boolean, + pailuanqi: Boolean, up: Boolean, edidt: Boolean, day: Int, + Menstrual_symptoms: IntArray + ) { + this.userId = userId + this.address = address + menstrualPeriod_StartTime = MenstrualPeriod_StartTime + menstrualPeriod_Lenth = MenstrualPeriod_Lenth + menstrualPeriod_Period = MenstrualPeriod_Period + flowRate = FlowRate + dysmenorrhea = Dysmenorrhea + isLove = Love + this.mood = mood + isStart = start + isYuejinqi = yuejinqi + isYuceyuejinqi = yuceyuejinqi + isYiyunqi = yiyunqi + isYuceyiyunqi = yuceyiyunqi + isPailuanqi = pailuanqi + isUp = up + isEdidt = edidt + this.day = day + menstrual_symptoms = Menstrual_symptoms + } + + fun getLove(): Boolean { + return isLove + } + + fun getStart(): Boolean { + return isStart + } + + fun getYuejinqi(): Boolean { + return isYuejinqi + } + + fun getYiyunqi(): Boolean { + return isYiyunqi + } + + fun getPailuanqi(): Boolean { + return isPailuanqi + } + + fun getEdidt(): Boolean { + return isEdidt + } + + fun getYuceyuejinqi(): Boolean { + return isYuceyuejinqi + } + + fun getYuceyiyunqi(): Boolean { + return isYuceyiyunqi + } + + fun getUp(): Boolean { + return isUp + } +} \ No newline at end of file diff --git a/android/blesdk_2301/src/main/java/com/jstyle/blesdk2301/other/ResolveUtil.java b/android/blesdk_2301/src/main/java/com/jstyle/blesdk2301/other/ResolveUtil.java new file mode 100644 index 00000000..d1e46066 --- /dev/null +++ b/android/blesdk_2301/src/main/java/com/jstyle/blesdk2301/other/ResolveUtil.java @@ -0,0 +1,1704 @@ +package com.jstyle.blesdk2301.other; + + +import android.util.Log; + +import com.jstyle.blesdk2301.Util.BleSDK; +import com.jstyle.blesdk2301.constant.BleConst; +import com.jstyle.blesdk2301.constant.DeviceConst; +import com.jstyle.blesdk2301.constant.DeviceKey; +import com.jstyle.blesdk2301.model.DeviceBean; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.text.NumberFormat; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.TimeZone; + +/** + * Created by Administrator on 2018/1/17. + */ + +public class ResolveUtil { + + public static int getBcd(String value) { + return Integer.parseInt(value, 16); + } + /** + * crc校验 + * + * @param value + */ + public static void crcValue(byte[] value) { + byte crc = 0; + for (int i = 0; i < value.length - 1; i++) { + crc += value[i]; + } + value[value.length - 1] = (byte) (crc & 0xff); + } + + public static byte getTimeValue(int value) { + String data = value + ""; + Integer m = Integer.parseInt(data, 16); + return (byte) m.intValue(); + } + + + public static String bcd2String(byte bytes) { + StringBuffer temp = new StringBuffer(); + temp.append((byte) ((bytes & 0xf0) >>> 4)); + temp.append((byte) (bytes & 0x0f)); + return temp.toString(); + } + + /** + * 设备时间 + * + * @param value + * @return + */ + public static Map getDeviceTime(byte[] value) { + Map maps=new HashMap<>(); + maps.put(DeviceKey.DataType, BleConst.GetDeviceTime); + maps.put(DeviceKey.End,true); + + MapmapData=new HashMap<>(); + String date = "20"+ByteToHexString(value[1]) + "-" + + ByteToHexString(value[2]) + "-" + ByteToHexString(value[3]) + " " + + ByteToHexString(value[4]) + ":" + ByteToHexString(value[5]) + ":" + ByteToHexString(value[6]); + String gpsDate = ByteToHexString(value[9]) + "." + + ByteToHexString(value[10]) + "." + ByteToHexString(value[11]) ; + mapData.put(DeviceKey.DeviceTime, date); + mapData.put(DeviceKey.GPSTime, gpsDate); + maps.put(DeviceKey.Data, mapData); + return maps; + } + public static String getGpsTime(byte[] value) { + + String gpsDate = ByteToHexString(value[9]) + "." + + ByteToHexString(value[10]) + "." + ByteToHexString(value[11]) ; + + return gpsDate; + } + + /** + * 用户信息 + * + * @param value + * @return + */ + public static Map getUserInfo(byte[] value) { + Map maps=new HashMap<>(); + maps.put(DeviceKey.DataType, BleConst.GetPersonalInfo); + maps.put(DeviceKey.End,true); + MapmapData=new HashMap<>(); + maps.put(DeviceKey.Data, mapData); + String[] userInfo = new String[6]; + for (int i = 0; i < 5; i++) { + userInfo[i] = String.valueOf(getValue(value[i + 1], 0)); + } + String deviceId = ""; + for (int i = 6; i < 12; i++) { + if (value[i] == 0) + continue; + deviceId += (char) getValue(value[i], 0); + } + userInfo[5] = deviceId; + mapData.put(DeviceKey.Gender, userInfo[0]); + mapData.put(DeviceKey.Age, userInfo[1]); + mapData.put(DeviceKey.Height, userInfo[2]); + mapData.put(DeviceKey.Weight, userInfo[3]); + mapData.put(DeviceKey.Stride, userInfo[4]); + mapData.put(DeviceKey.KUserDeviceId, userInfo[5]); + return maps; + } + + public static Map GetAutomaticSpo2Monitoring(byte[] value) { + Map maps=new HashMap<>(); + maps.put(DeviceKey.DataType, BleConst.GetAutomaticSpo2Monitoring); + maps.put(DeviceKey.End,false); + List> list = new ArrayList<>(); + maps.put(DeviceKey.Data, list); + int count = 10; + int length = value.length; + int size = length / count; + if (size == 0) { + maps.put(DeviceKey.End,true); + return maps; + } + for (int i = 0; i < size; i++) { + int flag = 1 + (i + 1) * count; + if (value[length-1] == (byte) 0xff) { + maps.put(DeviceKey.End,true); + } + Map hashMap = new HashMap<>(); + String date = "20"+ByteToHexString(value[3 + i * count]) + "." + + ByteToHexString(value[4 + i * count]) + "." + ByteToHexString(value[5 + i * count]) + " " + + ByteToHexString(value[6 + i * count]) + ":" + ByteToHexString(value[7 + i * count]) + ":" + ByteToHexString(value[8 + i * count]); + hashMap.put(DeviceKey.Date, date); + hashMap.put(DeviceKey.Blood_oxygen, String.valueOf(getValue(value[9 + i * count], 0))); + list.add(hashMap); + } + + return maps; + } + /** + * 设备信息 + * + * @param value + * @return + */ + public static Map getDeviceInfo(byte[] value) { + Map maps=new HashMap<>(); + maps.put(DeviceKey.DataType, BleConst.GetDeviceInfo); + maps.put(DeviceKey.End,true); + MapmapData=new HashMap<>(); + maps.put(DeviceKey.Data, mapData); + mapData.put(DeviceKey.DistanceUnit, String.valueOf(getValue(value[ 1], 0))); + mapData.put(DeviceKey.TimeUnit, String.valueOf(getValue(value[ 2], 0))); + mapData.put(DeviceKey.WristOn, String.valueOf(getValue(value[ 3], 0))); + mapData.put(DeviceKey.TempUnit, String.valueOf(getValue(value[ 4], 0))); + mapData.put(DeviceKey.NightMode, String.valueOf(getValue(value[ 5], 0))); + mapData.put(DeviceKey.KBaseHeart, String.valueOf(getValue(value[ 9], 0))); + mapData.put(DeviceKey.ScreenBrightness, String.valueOf(getValue(value[ 11], 0))); + mapData.put(DeviceKey.Dialinterface, String.valueOf(getValue(value[12], 0))); + mapData.put(DeviceKey.SocialDistancedwitch, String.valueOf(getValue(value[13], 0))); + mapData.put(DeviceKey.Lauage, String.valueOf(getValue(value[14], 0))); + return maps; + } + + /** + * 实时计步信息 + * + * @param value + * @return + */ + public static Map getActivityData(byte[] value) { + Map maps=new HashMap<>(); + maps.put(DeviceKey.DataType, BleConst.RealTimeStep); + maps.put(DeviceKey.End,true); + MapmapData=new HashMap<>(); + maps.put(DeviceKey.Data, mapData); + String[] activityData = new String[6]; + int step = 0; + float cal = 0; + float distance = 0; + int time = 0; + int heart = 0; + int exerciseTime = 0; + for (int i = 1; i < 5; i++) { + step += getValue(value[i], i - 1); + } + for (int i = 5; i < 9; i++) { + cal += getValue(value[i], i - 5); + } + for (int i = 9; i < 13; i++) { + distance += getValue(value[i], i - 9); + } + for (int i = 13; i < 17; i++) { + time += getValue(value[i], i - 13); + } + for (int i = 17; i < 21; i++) { + exerciseTime += getValue(value[i], i - 17); + } + heart = getValue(value[21], 0); + int temp = getValue(value[22], 0)+getValue(value[23],1); + NumberFormat numberFormat = getNumberFormat(1); + BigDecimal bigDecimal=new BigDecimal(String.valueOf(cal/100)); + BigDecimal bigDecimaCal= bigDecimal.setScale(1, RoundingMode.HALF_DOWN); + activityData[0] = String.valueOf(step); + activityData[1] = bigDecimaCal.floatValue()+""; + numberFormat.setMinimumFractionDigits(2); + activityData[2] = numberFormat.format(distance / 100); + activityData[3] = String.valueOf(time/60); + activityData[4] = String.valueOf(heart); + activityData[5] = String.valueOf(exerciseTime); + mapData.put(DeviceKey.Step, activityData[0]); + mapData.put(DeviceKey.Calories, activityData[1]); + mapData.put(DeviceKey.Distance, activityData[2]); + mapData.put(DeviceKey.ExerciseMinutes, activityData[3]); + mapData.put(DeviceKey.HeartRate, activityData[4]); + mapData.put(DeviceKey.ActiveMinutes, activityData[5]); + numberFormat.setMinimumFractionDigits(1); + mapData.put(DeviceKey.TempData,numberFormat.format(temp*0.1f)); + mapData.put(DeviceKey.Blood_oxygen, String.valueOf(getValue(value[24], 0))); + return maps; + } + + public static Map get3d(byte[] value) { + Map maps=new HashMap<>(); + maps.put(DeviceKey.DataType, BleConst.GetMAC); + maps.put(DeviceKey.End,true); + List> list = new ArrayList<>(); + maps.put(DeviceKey.Data, list); + for(int i=0;i hashMap = new HashMap<>(); + int valueddd = i * 6; + int data = getValue(value[1 + valueddd], 0) + getValue(value[2 + valueddd], 1); + int data2 = getValue(value[3 + valueddd], 0) + getValue(value[4 + valueddd], 1); + int data3 = getValue(value[5 + valueddd], 0) + getValue(value[6 + valueddd], 1); + hashMap.put(DeviceKey.arrayX, data); + hashMap.put(DeviceKey.arrayY, data2); + hashMap.put(DeviceKey.arrayZ, data3); + list.add(hashMap); + } + return maps; + } + + + public static Map getPPG(byte[] value) { + Map maps=new HashMap<>(); + maps.put(DeviceKey.DataType, BleConst.Getppg); + maps.put(DeviceKey.End,true); + List> list = new ArrayList<>(); + maps.put(DeviceKey.Data, list); + byte[] bs = new byte[value.length-3]; + System.arraycopy(value, 3, bs, 0, bs.length); + for(int i=0;i hashMap = new HashMap<>(); + int valueddd = i * 2; + int data = getValue(value[valueddd], 0) + getValue(value[valueddd+1], 1); + hashMap.put(DeviceKey.DataType, value[1]); + hashMap.put(DeviceKey.arrayPpgRawData, data); + list.add(hashMap); + } + return maps; + } + + + + /** + * 步数目标 + * + * @param value + * @return + */ + public static Map getGoal(byte[] value) { + Map maps=new HashMap<>(); + maps.put(DeviceKey.DataType, BleConst.GetStepGoal); + maps.put(DeviceKey.End,true); + MapmapData=new HashMap<>(); + maps.put(DeviceKey.Data, mapData); + int goal = 0; + for (int i = 0; i < 4; i++) { + goal += getValue(value[i + 1], i); + } + mapData.put(DeviceKey.StepGoal, String.valueOf(goal)); + return maps; + } + + /** + * 设备电量 + * + * @param value + * @return + */ + public static Map getDeviceBattery(byte[] value) { + Map maps=new HashMap<>(); + maps.put(DeviceKey.DataType, BleConst.GetDeviceBatteryLevel); + maps.put(DeviceKey.End,true); + MapmapData=new HashMap<>(); + maps.put(DeviceKey.Data, mapData); + int battery = getValue(value[1], 0); + /*int Chargingstate = getValue(value[2], 0); + int Voltage_value = getValue(value[3 ], 0) + getValue(value[4 ], 1);*/ + mapData.put(DeviceKey.BatteryLevel, String.valueOf(battery)); + /* mapData.put(DeviceKey.Chargingstate, String.valueOf(Chargingstate)); + mapData.put(DeviceKey.Voltage_value, String.valueOf(Voltage_value));*/ + return maps; + } + + /** + * 设备mac地址 + * + * @param value + * @return + */ + public static Map getDeviceAddress(byte[] value) { + Map maps=new HashMap<>(); + maps.put(DeviceKey.DataType, BleConst.GetDeviceMacAddress); + maps.put(DeviceKey.End,true); + MapmapData=new HashMap<>(); + maps.put(DeviceKey.Data, mapData); + StringBuilder sb = new StringBuilder(); + for (int i = 1; i < 7; i++) { + sb.append(String.format("%02X", value[i])).append(":"); + } + String address = sb.toString(); + mapData.put(DeviceKey.MacAddress, address.substring(0, address.lastIndexOf(":"))); + return maps; + } + + /** + * 设备版本号 + * + * @param value + * @return + */ + public static Map getDeviceVersion(byte[] value) { + Map maps=new HashMap<>(); + maps.put(DeviceKey.DataType, BleConst.GetDeviceVersion); + maps.put(DeviceKey.End,true); + MapmapData=new HashMap<>(); + maps.put(DeviceKey.Data, mapData); + StringBuffer stringBuffer = new StringBuffer(); + for (int i = 1; i < 5; i++) { + stringBuffer.append(String.format("%X", value[i])).append(i == 4 ? "" : "."); + } + mapData.put(DeviceKey.DeviceVersion, stringBuffer.toString()); + return maps; + } + + + + public static Map Reset() { + Map maps=new HashMap<>(); + maps.put(DeviceKey.DataType, BleConst.CMD_Reset); + maps.put(DeviceKey.Data, new HashMap<>()); + maps.put(DeviceKey.End,true); + return maps; + } + public static Map MCUReset() { + Map maps=new HashMap<>(); + maps.put(DeviceKey.DataType, BleConst.CMD_MCUReset); + maps.put(DeviceKey.Data, new HashMap<>()); + maps.put(DeviceKey.End,true); + return maps; + } + + public static Map Notify() { + Map maps=new HashMap<>(); + maps.put(DeviceKey.DataType, BleConst.Notify); + maps.put(DeviceKey.Data, new HashMap<>()); + maps.put(DeviceKey.End,true); + return maps; + } + + /** + * 设备名字 + * + * @param value + * @return + */ + public static Map getDeviceName(byte[] value) { + Map maps=new HashMap<>(); + maps.put(DeviceKey.DataType, BleConst.CMD_Get_Name); + maps.put(DeviceKey.End,true); + MapmapData=new HashMap<>(); + maps.put(DeviceKey.Data, mapData); + String name = ""; + for (int i = 1; i < 15; i++) { + int charValue=getValue(value[i],0); + if(charValue==0||charValue>127)continue; + name += (char) charValue; + } + mapData.put(DeviceKey.DeviceName, name); + return maps; + } + + public static String getByteString(byte b) { + byte[] array = new byte[8]; + StringBuffer stringBuffer = new StringBuffer(); + for (int i = 0; i <= 6; i++) { + array[i] = (byte) (b & 1); + b = (byte) (b >> 1); + stringBuffer.append(String.valueOf(array[i])).append(i == 6 ? "" : "-"); + } + return stringBuffer.toString(); + } + + public static String getByteArray(byte b) { + byte[] array = new byte[8]; + StringBuffer stringBuffer = new StringBuffer(); + for (int i = 0; i <= 7; i++) { + array[i] = (byte) (b & 1); + b = (byte) (b >> 1); + stringBuffer.append(String.valueOf(array[i])); + } + return stringBuffer.toString(); + } + + /** + * 自动测量心率时间段 + * + * @param value + * @return + */ + public static Map getAutoHeart(byte[] value) { + Map maps=new HashMap<>(); + maps.put(DeviceKey.DataType, BleConst.GetAutomatic); + maps.put(DeviceKey.End,true); + MapmapData=new HashMap<>(); + maps.put(DeviceKey.Data, mapData); + String[] autoHeart = new String[7]; + int enable = getValue(value[1], 0); + String startHour = ByteToHexString(value[2]); + String startMin = ByteToHexString(value[3]); + String stopHour = ByteToHexString(value[4]); + String stopMin = ByteToHexString(value[5]); + String week = getByteString(value[6]); + int time = getValue(value[7], 0) + getValue(value[8], 1); + autoHeart[0] = String.valueOf(enable); + autoHeart[1] = startHour; + autoHeart[2] = startMin; + autoHeart[3] = stopHour; + autoHeart[4] = stopMin; + autoHeart[5] = week; + autoHeart[6] = String.valueOf(time); + mapData.put(DeviceKey.WorkMode, autoHeart[0]); + mapData.put(DeviceKey.StartTime, autoHeart[1]); + mapData.put(DeviceKey.KHeartStartMinter, autoHeart[2]); + mapData.put(DeviceKey.EndTime, autoHeart[3]); + mapData.put(DeviceKey.KHeartEndMinter, autoHeart[4]); + mapData.put(DeviceKey.Weeks, autoHeart[5]); + mapData.put(DeviceKey.IntervalTime, autoHeart[6]); + return maps; + } + + /** + * 读取血压校准 + * + */ + public static Map ReadOxy(byte[] value) { + Map hashMap = new HashMap<>(); + hashMap.put(DeviceKey.DataType, BleConst.GetBloodpressure_calibration); + hashMap.put(DeviceKey.End,true); + MapmapData=new HashMap<>(); + hashMap.put(DeviceKey.Data, mapData); + mapData.put(DeviceKey.KHrvBloodLowPressure, getValue(value[1],0)+10+""); + mapData.put(DeviceKey. KHrvBloodHighPressure, getValue(value[3],0)+10+""); + return hashMap; + } + + public static Map getSpo2(byte[] value) { + Map maps=new HashMap<>(); + maps.put(DeviceKey.DataType, BleConst.GEtSpo2); + maps.put(DeviceKey.End,true); + MapmapData=new HashMap<>(); + maps.put(DeviceKey.Data, mapData); //29 01 01 39 01 30 01 30 7F 00 00 00 00 00 00 45 + String time = ByteToHexString(value[3]); + String startHour = ByteToHexString(value[4]); + String startMin = ByteToHexString(value[5]); + String stopHour = ByteToHexString(value[6]); + String stopMin = ByteToHexString(value[7]); + String week = getByteString(value[8]); + mapData.put(DeviceKey.StartTime, startHour); + mapData.put(DeviceKey.KHeartStartMinter, startMin); + mapData.put(DeviceKey.EndTime, stopHour); + mapData.put(DeviceKey.KHeartEndMinter, stopMin); + mapData.put(DeviceKey.Weeks, week); + mapData.put(DeviceKey.IntervalTime, time); + return maps; + } + + /** + * 运动提醒 + * + * @param value + * @return + */ + public static Map getActivityAlarm(byte[] value) { + Map maps=new HashMap<>(); + maps.put(DeviceKey.DataType, BleConst.GetSedentaryReminder); + maps.put(DeviceKey.End,true); + MapmapData=new HashMap<>(); + maps.put(DeviceKey.Data, mapData); + String[] activityAlarm = new String[7]; + String startHour = ByteToHexString(value[1]); + String startMin = ByteToHexString(value[2]); + String stopHour = ByteToHexString(value[3]); + String stopMin = ByteToHexString(value[4]); + String week = getByteString(value[5]); + int time = getValue(value[6], 0); + int step = getValue(value[7], 0); + activityAlarm[0] = startHour; + activityAlarm[1] = startMin; + activityAlarm[2] = stopHour; + activityAlarm[3] = stopMin; + activityAlarm[4] = week; + activityAlarm[5] = String.valueOf(time); + activityAlarm[6] = String.valueOf(step); + mapData.put(DeviceKey.StartTimeHour, activityAlarm[0]); + mapData.put(DeviceKey.StartTimeMin, activityAlarm[1]); + mapData.put(DeviceKey.EndTimeHour, activityAlarm[2]); + mapData.put(DeviceKey.EndTimeMin, activityAlarm[3]); + mapData.put(DeviceKey.Week, activityAlarm[4]); + mapData.put(DeviceKey.IntervalTime, activityAlarm[5]); + mapData.put(DeviceKey.LeastSteps, activityAlarm[6]); + return maps; + } + + /** + * 总运动数据 + * + * @param value + * @return + */ + public static Map getTotalStepData(byte[] value) { + Map maps=new HashMap<>(); + maps.put(DeviceKey.DataType, BleConst.GetTotalActivityData); + maps.put(DeviceKey.End,false); + List> list = new ArrayList<>(); + maps.put(DeviceKey.Data, list); + int count = getStepCount(value); + int length = value.length; + int size = length / count; + if (size == 0) { + maps.put(DeviceKey.End,true); + return maps; + } + NumberFormat numberFormat =NumberFormat.getNumberInstance(Locale.ENGLISH); + numberFormat.setMinimumFractionDigits(2); + numberFormat.setGroupingUsed(false); + for (int i = 0; i < size; i++) { + int flag = 1 + (i + 1) * count; + if (flag < length && value[flag] == (byte) 0xff) { + maps.put(DeviceKey.End,true); + } + Map hashMap = new HashMap<>(); + String date ="20"+ByteToHexString(value[2 + i * count]) + "." + + ByteToHexString(value[3 + i * count]) + "." + ByteToHexString(value[4 + i * count]); + int step = 0; + int time = 0; + float cal = 0; + float distance = 0; + + for (int j = 0; j < 4; j++) { + step += getValue(value[5 + j + i * count], j); + } + for (int j = 0; j < 4; j++) { + time += getValue(value[9 + j + i * count], j); + } + for (int j = 0; j < 4; j++) { + distance += getValue(value[13 + j + i * count], j); + } + for (int j = 0; j < 4; j++) { + cal += getValue(value[17 + j + i * count], j); + } + int exerciseTime = 0; + int goal = count==26?getValue(value[21 + i * count], 0):(getValue(value[21 + i * count], 0)+getValue(value[22 + i * count], 1)); + + for (int j = 0; j < 4; j++) { + exerciseTime += getValue(value[count-4 + j + i * count], j); + } + hashMap.put(DeviceKey.Date, date); + hashMap.put(DeviceKey.Step, String.valueOf(step)); + hashMap.put(DeviceKey.ExerciseMinutes, String.valueOf(time)); + hashMap.put(DeviceKey.Calories, numberFormat.format(cal / 100)); + hashMap.put(DeviceKey.Distance, numberFormat.format(distance / 100)); + hashMap.put(DeviceKey.Goal, String.valueOf(goal)); + hashMap.put(DeviceKey.ActiveMinutes, String.valueOf(exerciseTime)); + list.add(hashMap); + } + + return maps; + } + + private static int getStepCount(byte[]value){ + int goal=27; + int length=value.length; + if(length!=2){ + if(length%26==0){ + goal=26; + }else if(length%27==0){ + goal=27; + }else{ + if((length-2)%26==0){ + goal=26; + }else if((length-2)%27==0){ + goal=27; + } + } + } + return goal; + } + /** + * 详细运动数据 + * + * @param value + * @return + */ + public static Map getDetailData(byte[] value) { + Map maps=new HashMap<>(); + maps.put(DeviceKey.DataType, BleConst.GetDetailActivityData); + maps.put(DeviceKey.End,false); + List> list = new ArrayList<>(); + maps.put(DeviceKey.Data, list); + int count = 25; + int length = value.length; + int size = length / count; + DeviceBean deviceBean = new DeviceBean(); + if (size == 0) { + maps.put(DeviceKey.End,true); + return maps; + } + NumberFormat numberFormat = NumberFormat.getNumberInstance(Locale.ENGLISH); + numberFormat.setMaximumFractionDigits(2); + numberFormat.setGroupingUsed(false); + for (int i = 0; i < size; i++) { + Map hashMap = new HashMap<>(); + int flag = 1 + (i + 1) * count; + if ( value[length-1] == (byte) 0xff) { + maps.put(DeviceKey.End,true); + } + String date ="20"+ByteToHexString(value[3 + i * 25]) + "." + + ByteToHexString(value[4 + i * 25]) + "." + ByteToHexString(value[5 + i * 25]) + " " + + ByteToHexString(value[6 + i * 25]) + ":" + ByteToHexString(value[7 + i * 25]) + ":" + ByteToHexString(value[8 + i * 25]); + int step = 0; + float cal = 0; + float distance = 0; + StringBuffer stringBuffer = new StringBuffer(); + for (int j = 0; j < 2; j++) { + step += getValue(value[9 + j + i * 25], j); + } + for (int j = 0; j < 2; j++) { + cal += getValue(value[11 + j + i * 25], j); + } + for (int j = 0; j < 2; j++) { + distance += getValue(value[13 + j + i * 25], j); + } + for (int j = 0; j < 10; j++) { + stringBuffer.append(String.valueOf(getValue(value[15 + j + i * 25], 0))).append(j == 9 ? "" : " "); + } + hashMap.put(DeviceKey.Date, date); + hashMap.put(DeviceKey.KDetailMinterStep, String.valueOf(step)); + hashMap.put(DeviceKey.Calories, numberFormat.format(cal / 100)); + hashMap.put(DeviceKey.Distance, numberFormat.format(distance / 100)); + hashMap.put(DeviceKey.ArraySteps, stringBuffer.toString()); + list.add(hashMap); + } + + return maps; + } + + /** + * 睡眠数据 + * + * @param value + * @return + */ + public static Map getSleepData(byte[] value) { + int length = value.length; + Map maps=new HashMap<>(); + maps.put(DeviceKey.DataType, BleConst.GetDetailSleepData); + maps.put(DeviceKey.End,false); + List> list = new ArrayList<>(); + maps.put(DeviceKey.Data, list); + boolean end=value[value.length - 1] == (byte) 0xff&& value[value.length - 2] ==DeviceConst.CMD_Get_SleepData; + if (end) { maps.put(DeviceKey.End,true);} + if(130==length||(end&&132 == length)){//一分钟睡眠数据 + Map hashMap = new HashMap<>(); + String date = "20" + bcd2String(value[3]) + "-" + bcd2String(value[4]) + "-" + bcd2String(value[5]) + " " + + bcd2String(value[6]) + ":" + bcd2String(value[7]) + ":" + bcd2String(value[8]); + hashMap.put(DeviceKey.Date, date); + int sleepLength = getValue(value[9], 0); + StringBuilder stringBuffer = new StringBuilder(); + for (int j = 0; j < sleepLength; j++) { + stringBuffer.append(getValue(value[10 + j], 0)).append(j == sleepLength ? "" : " "); + } + hashMap.put(DeviceKey.ArraySleep, stringBuffer.toString()); + hashMap.put(DeviceKey.sleepUnitLength,"1"); + list.add(hashMap); + }else{ + int count = 34; + int size = length / count; + if (size == 0) { + maps.put(DeviceKey.Data, list); + maps.put(DeviceKey.End,true); + return maps; + } + for (int i = 0; i < size; i++) { + Map hashMap = new HashMap<>(); + String date = "20" + bcd2String(value[3 + i * 34]) + "-" + + bcd2String(value[4 + i * 34]) + "-" + bcd2String(value[5 + i * 34]) + " " + + bcd2String(value[6 + i * 34]) + ":" + bcd2String(value[7 + i * 34]) + ":" + bcd2String(value[8 + i * 34]); + hashMap.put(DeviceKey.Date, date); + int sleepLength = getValue(value[9 + i * 34], 0); + StringBuilder stringBuffer = new StringBuilder(); + try{ + for (int j = 0; j < sleepLength; j++) { + stringBuffer.append(getValue(value[10 + j + i * 34], 0)).append(j == sleepLength ?"":" "); + } + }catch (ArrayIndexOutOfBoundsException E){E.fillInStackTrace(); } + hashMap.put(DeviceKey.ArraySleep, stringBuffer.toString()); + hashMap.put(DeviceKey.sleepUnitLength,"5"); + list.add(hashMap); + } + } + return maps; + } + + + /** + * 历史心率数据 + * + * @param value + * @return + */ + public static Map getBloodoxygen(byte[] value) { + Map maps=new HashMap<>(); + maps.put(DeviceKey.DataType, BleConst.Blood_oxygen); + maps.put(DeviceKey.End,false); + List> list = new ArrayList<>(); + maps.put(DeviceKey.Data, list); + int count = 10; + int length = value.length; + int size = length / count; + if (size == 0) { + maps.put(DeviceKey.End,true); + return maps; + } + for (int i = 0; i < size; i++) { + int flag = 1 + (i + 1) * count; + if (value[length-1] == (byte) 0xff) { + maps.put(DeviceKey.End,true); + } + Map hashMap = new HashMap<>(); + String date = "20"+ByteToHexString(value[3 + i * count]) + "." + + ByteToHexString(value[4 + i * count]) + "." + ByteToHexString(value[5 + i * count]) + " " + + ByteToHexString(value[6 + i * count]) + ":" + ByteToHexString(value[7 + i * count]) + ":" + ByteToHexString(value[8 + i * count]); + hashMap.put(DeviceKey.Date, date); + hashMap.put(DeviceKey.Blood_oxygen, String.valueOf(getValue(value[9 + i * 10], 0))); + list.add(hashMap); + } + + return maps; + } + + + + + + /** + * 历史心率数据 + * + * @param value + * @return + */ + public static Map getHeartData(byte[] value) { + Map maps=new HashMap<>(); + maps.put(DeviceKey.DataType, BleConst.GetDynamicHR); + maps.put(DeviceKey.End,false); + List> list = new ArrayList<>(); + maps.put(DeviceKey.Data, list); + int count = 24; + int length = value.length; + int size = length / count; + if (size == 0) { + maps.put(DeviceKey.End,true); + return maps; + } + for (int i = 0; i < size; i++) { + if (value[length-1] == (byte) 0xff) { + maps.put(DeviceKey.End,true); + } + Map hashMap = new HashMap<>(); + String date = "20"+ByteToHexString(value[3 + i * count]) + "." + + ByteToHexString(value[4 + i * count]) + "." + ByteToHexString(value[5 + i * count]) + " " + + ByteToHexString(value[6 + i * count]) + ":" + ByteToHexString(value[7 + i * count]) + ":" + ByteToHexString(value[8 + i * count]); + + + StringBuffer stringBuffer = new StringBuffer(); + for (int j = 0; j < 15; j++) { + stringBuffer.append(String.valueOf(getValue(value[9 + j + i * count], 0))).append(j == 14 ? "" : " "); + } + hashMap.put(DeviceKey.Date, date); + hashMap.put(DeviceKey.ArrayDynamicHR, stringBuffer.toString()); + list.add(hashMap); + } + + return maps; + } + + + /** + * 单次历史心率数据 + * + * @param value + * @return + */ + public static Map getOnceHeartData(byte[] value) { + Map maps=new HashMap<>(); + maps.put(DeviceKey.DataType, BleConst.GetStaticHR); + maps.put(DeviceKey.End,false); + List> list = new ArrayList<>(); + maps.put(DeviceKey.Data, list); + int count = 10; + int length = value.length; + int size = length / count; + if (size == 0) { + maps.put(DeviceKey.End,true); + return maps; + } + for (int i = 0; i < size; i++) { + int flag = 1 + (i + 1) * count; + if ( value[length-1] == (byte) 0xff) { + maps.put(DeviceKey.End,true); + } + Map hashMap = new HashMap<>(); + String date ="20"+ByteToHexString(value[3 + i * 10]) + "." + + ByteToHexString(value[4 + i * 10]) + "." + ByteToHexString(value[5 + i * 10]) + " " + + ByteToHexString(value[6 + i * 10]) + ":" + ByteToHexString(value[7 + i * 10]) + ":" + ByteToHexString(value[8 + i * 10]); + String heart = String.valueOf(getValue(value[9 + i * 10], 0)); + hashMap.put(DeviceKey.Date, date); + hashMap.put(DeviceKey.StaticHR, heart); + list.add(hashMap); + } + return maps; + } + /** + * htv测试数据 + * + * @param + * @return + */ + public static Map DeleteHrv() { + Map maps=new HashMap<>(); + maps.put(DeviceKey.DataType, BleConst.DeleteHrv); + maps.put(DeviceKey.End,true); + List> list = new ArrayList<>(); + maps.put(DeviceKey.Data, list); + return maps; + } + /** + * htv测试数据 + * + * @param value + * @return + */ + public static Map getHrvTestData(byte[] value) { + Map maps=new HashMap<>(); + maps.put(DeviceKey.DataType, BleConst.GetHRVData); + maps.put(DeviceKey.End,false); + List> list = new ArrayList<>(); + maps.put(DeviceKey.Data, list); + int count = 15; + int length = value.length; + int size = length / count; + + if (size == 0) { + maps.put(DeviceKey.End,true); + return maps; + } + if(value[value.length-1]== (byte) 0xff) maps.put(DeviceKey.End,true);; + for (int i = 0; i < size; i++) { + + Map hashMap = new HashMap<>(); + String date ="20"+ByteToHexString(value[3 + i * count]) + "." + + ByteToHexString(value[4 + i * count]) + "." + ByteToHexString(value[5 + i * count]) + " " + + ByteToHexString(value[6 + i * count]) + ":" + ByteToHexString(value[7 + i * count]) + ":" + ByteToHexString(value[8 + i * count]); + String hrv = String.valueOf(getValue(value[9 + i * count], 0)); + String blood = String.valueOf(getValue(value[10 + i * count], 0)); + String heart = String.valueOf(getValue(value[11 + i * count], 0)); + String Tired = String.valueOf(getValue(value[12 + i * count], 0)); + + String moodValue = String.valueOf(getValue(value[13 + i * count], 0)); + String breathRate = String.valueOf(getValue(value[14 + i * count], 0)); + hashMap.put(DeviceKey.Date, date); + hashMap.put(DeviceKey.HRV, hrv); + hashMap.put(DeviceKey.VascularAging, blood); + hashMap.put(DeviceKey.Stress, Tired); + hashMap.put(DeviceKey.highBP, moodValue); + hashMap.put(DeviceKey.lowBP, breathRate); + hashMap.put(DeviceKey.HeartRate, heart); + list.add(hashMap); + } + + return maps; + } + + /** + * 闹钟数据 + * + * @param value + * @return + */ + public static Map getClockData(byte[] value) { + Map maps=new HashMap<>(); + maps.put(DeviceKey.DataType, BleConst.GetAlarmClock); + maps.put(DeviceKey.End,false); + List> list = new ArrayList<>(); + maps.put(DeviceKey.Data, list); + int count = 41; + int length = value.length; + int size = length / count; + + DeviceBean deviceBean = new DeviceBean(); + if (size == 0) { + maps.put(DeviceKey.End,true); + return maps; + } + for (int i = 0; i < size; i++) { + int flag = 1 + (i + 1) * count; + if (flag < length && value[flag] == (byte) 0xff) { + maps.put(DeviceKey.End,true); + } + Map hashMap = new HashMap<>(); + String id = String.valueOf(getValue(value[4 + i * count], 0)); + String enable = String.valueOf(getValue(value[5 + i * count], 0)); + String type = String.valueOf(getValue(value[6 + i * count], 0)); + String hour = ByteToHexString(value[7 + i * count]); + String min = ByteToHexString(value[8 + i * count]); + String week = getByteString(value[9 + i * count]); + int lengthS = getValue(value[10 + i * count], 0); + String content = ""; + for (int J = 0; J < lengthS; J++) { + if (value[11 + i * count] == 0) + continue; + content += (char) getValue(value[11 + J + i * count], 0); + } + hashMap.put(DeviceKey.KAlarmId, id); + hashMap.put(DeviceKey.OpenOrClose, enable); + hashMap.put(DeviceKey.ClockType, type); + hashMap.put(DeviceKey.ClockTime, hour); + hashMap.put(DeviceKey.KAlarmMinter, min); + hashMap.put(DeviceKey.Week, week); + hashMap.put(DeviceKey.KAlarmContent, content); + hashMap.put(DeviceKey.KAlarmLength, String.valueOf(lengthS)); + list.add(hashMap); + } + + return maps; + } + + private static final String TAG = "ResolveUtil"; + +/* public static Map getHistoryGpsData(byte[] value) { + Map maps=new HashMap<>(); + maps.put(DeviceKey.DataType, BleConst.GetGPSData); + maps.put(DeviceKey.End,false); + List> list = new ArrayList<>(); + maps.put(DeviceKey.Data, list); + int count = 59; + int length = value.length; + int size = length / count; + + + if (size == 0) { + maps.put(DeviceKey.End,true); + return maps; + } + for (int i = 0; i < size; i++) { + int flag = 1 + (i + 1) * count; + if (flag < length && i==size-1&&value[flag] == (byte) 0xff) { + maps.put(DeviceKey.End,true); + } + Map hashMap = new HashMap<>(); + int id=getValue(value[1+i*count],0)+getValue(value[2+i*count],1); + String date = ByteToHexString(value[3 + i * count]) + "." + + ByteToHexString(value[4 + i * count]) + "." + ByteToHexString(value[5 + i * count]) + " " + + ByteToHexString(value[6 + i * count]) + ":" + ByteToHexString(value[7 + i * count]) + ":" + ByteToHexString(value[8 + i * count]); + byte[] valueLatitude = new byte[4]; + byte[] valueLongitude = new byte[4]; + StringBuffer stringBufferLatitude = new StringBuffer(); + StringBuffer stringBufferLongitude = new StringBuffer(); + for (int k = 0; k < 6; k++) { + for (int j = 0; j < 4; j++) { + valueLatitude[3 - j] = value[9 + j + i * count + k * 8]; + valueLongitude[3 - j] = value[13 + j + i * count + k * 8]; + } + String Latitude = String.valueOf(getFloat(valueLatitude, 0)); + String Longitude = String.valueOf(getFloat(valueLongitude, 0)); + stringBufferLatitude.append(Latitude).append(k == 5 ? "" : ","); + stringBufferLongitude.append(Longitude).append(k == 5 ? "" : ","); + } + hashMap.put(DeviceKey.Date, date); + hashMap.put(DeviceKey.KDataID,String.valueOf(id)); + hashMap.put(DeviceKey.Latitude, stringBufferLatitude.toString()); + hashMap.put(DeviceKey.Longitude, stringBufferLongitude.toString()); + list.add(hashMap); + } + + return maps; + }*/ + +/* public static Map getActivityGpsData(byte[] value) { + Map maps=new HashMap<>(); + maps.put(DeviceKey.DataType, BleConst.DeviceSendToApp); + maps.put(DeviceKey.End,true); + Map hashMap = new HashMap<>(); + maps.put(DeviceKey.Data, hashMap); + String date = ByteToHexString(value[1]) + "-" + + ByteToHexString(value[2]) + "-" + ByteToHexString(value[3]) + " " + + ByteToHexString(value[4]) + ":" + ByteToHexString(value[5]) + ":" + ByteToHexString(value[6]); + byte[] valueLatitude = new byte[4]; + byte[] valueLongitude = new byte[4]; + for (int j = 0; j < 4; j++) { + valueLatitude[3 - j] = value[9+j ]; + valueLongitude[3 - j] = value[14+j]; + } + String Latitude = String.valueOf(getFloat(valueLatitude, 0)); + String Longitude = String.valueOf(getFloat(valueLongitude, 0)); + int count=getValue(value[18],0); + + hashMap.put(DeviceKey.KActivityLocationTime, date); + hashMap.put(DeviceKey.KActivityLocationLatitude, Latitude); + hashMap.put(DeviceKey.KActivityLocationLongitude, Longitude); + hashMap.put(DeviceKey.KActivityLocationCount, String.valueOf(count)); + + return maps; + }*/ +public static Map getTempDataer(byte[] value) { + Map maps=new HashMap<>(); + maps.put(DeviceKey.DataType, BleConst.GetAxillaryTemperatureDataWithMode); + maps.put(DeviceKey.End,false); + List> list = new ArrayList<>(); + maps.put(DeviceKey.Data, list); + + int count = 11; + int length = value.length; + int size = length / count; + if (size == 0||value[length - 1] == (byte) 0xff) { + maps.put(DeviceKey.End,true); + } + NumberFormat numberFormat=getNumberFormat(1); + for (int i = 0; i < size; i++) { + Map hashMap = new HashMap<>(); + String date = "20"+bcd2String(value[3 + i * count]) + "." + + bcd2String(value[4 + i * count]) + "." + bcd2String(value[5 + i * count]) + " " + + bcd2String(value[6 + i * count]) + ":" + bcd2String(value[7 + i * count]) + ":" + bcd2String(value[8 + i * count]); + int tempValue = getValue(value[9 + i * count], 0)+getValue(value[10 + i * count], 1); + hashMap.put(DeviceKey.Date, date); + hashMap.put(DeviceKey.axillaryTemperature, numberFormat.format(tempValue*0.1f)); + list.add(hashMap); + } + + return maps; +} + + public static Map getTempData(byte[] value) { + Map maps=new HashMap<>(); + List> list = new ArrayList<>(); + maps.put(DeviceKey.DataType, BleConst.Temperature_history); + maps.put(DeviceKey.End,false); + maps.put(DeviceKey.Data, list); + int count = 11; + int length = value.length; + int size = length / count; + if (size == 0||value[length - 1] == (byte) 0xff) { + maps.put(DeviceKey.End,true); + } + NumberFormat numberFormat=getNumberFormat(1); + for (int i = 0; i < size; i++) { + if (value[length-1] == (byte) 0xff) { + maps.put(DeviceKey.End,true); + } + Map hashMap = new HashMap<>(); + String date = "20"+bcd2String(value[3 + i * count]) + "." + + bcd2String(value[4 + i * count]) + "." + bcd2String(value[5 + i * count]) + " " + + bcd2String(value[6 + i * count]) + ":" + bcd2String(value[7 + i * count]) + ":" + bcd2String(value[8 + i * count]); + int tempValue = getValue(value[9 + i * count], 0)+getValue(value[10 + i * count], 1); + hashMap.put(DeviceKey.Date, date); + hashMap.put(DeviceKey.temperature, numberFormat.format(tempValue*0.1f)); + list.add(hashMap); + } + return maps; + } + + //私有属性 + private Databack databack = null; + + public interface Databack { + void Endback(Map map); + } + + //setter方法 + public void setDataback(Databack databack1) { + this.databack = databack1; + } + + protected static List ecgdata=new ArrayList<>();; + protected static int size=0; + protected static int count=0; + protected static Map maps=new HashMap<>(); + protected static List> list = new ArrayList<>(); +/* public static void Getecgdata(byte[] value,Databack databack) { + if (value[value.length - 1] == (byte) 0x71) { + maps.put(DeviceKey.DataType, BleConst.DeleteECGdata); + maps.put(DeviceKey.End,true); + if(null!=databack){databack.Endback(maps);} + }else if(3==value.length&&value[value.length - 1] == (byte) 0xff){ + if(read){ + Map hashMap = new HashMap<>(); + hashMap.put(DeviceKey.ECGValue, ecgdata.toString()+""); + list.add(hashMap); + maps.put(DeviceKey.End,true); + if(null!=databack){databack.Endback(maps);} + }else{ + maps.put(DeviceKey.DataType, BleConst. ECGdata); + maps.put(DeviceKey.End,true); + if(null!=databack){databack.Endback(maps);} + + + } + }else{ + + int index =getValue(value[1 ], 0)+getValue(value[2], 1) ; + if(0==index){ + ecgdata=new ArrayList<>();; + maps=new HashMap<>(); + list = new ArrayList<>(); + maps.put(DeviceKey.DataType, BleConst. ECGdata); + maps.put(DeviceKey.Data, list); + count=0; + size =getValue(value[9 ], 0)+getValue(value[10], 1) ; + String date = "20"+bcd2String(value[3]) + "." + + bcd2String(value[4 ]) + "." + bcd2String(value[5]) + " " + + bcd2String(value[6 ]) + ":" + bcd2String(value[7 ]) + ":" + bcd2String(value[8 ]); + Map hashMap = new HashMap<>(); + hashMap.put(DeviceKey.Date, date); + hashMap.put(DeviceKey.HRV, getValue(value[11], 0)+""); + hashMap.put(DeviceKey.HeartRate, getValue(value[12], 0)+""); + hashMap.put(DeviceKey.ECGMoodValue, getValue(value[12], 0)+""); + byte[] bs = new byte[value.length-14]; + System.arraycopy(value, 14, bs, 0, bs.length); + for (int i=0;i hashMap = new HashMap<>(); + hashMap.put(DeviceKey.ECGValue, ecgdata.toString()+""); + list.add(hashMap); + maps.put(DeviceKey.End,true); + if(null!=databack){databack.Endback(maps);} + }*//* + + } + } + + }*/ + + + + + public static Map getEcgHistoryData(byte[] value) { + Map maps=new HashMap<>(); + Map list = new HashMap<>(); + maps.put(DeviceKey.DataType, BleConst.ECGdata); + maps.put(DeviceKey.End,false); + maps.put(DeviceKey.Data, list); + + int length = value.length; + if (length== 3||(value[length - 3] == (byte) 0x71 && value[length - 2] == (byte) 0xff && value[length - 1] == (byte) 0xff)) { + maps.put(DeviceKey.End,true); + return maps; + } + int id = getValue(value[1], 0) + getValue(value[2], 1); + int offset = 3; + Map hashMap = new HashMap<>(); + if (id == 0) {//第一条 + String date = "20"+bcd2String(value[3]) + "-" + + bcd2String(value[4]) + "-" + bcd2String(value[5]) + " " + + bcd2String(value[6]) + ":" + bcd2String(value[7]) + ":" + bcd2String(value[8]); + String hrv = String.valueOf(getValue(value[11], 0)); + String heart = String.valueOf(getValue(value[12], 0)); + String moodValue = String.valueOf(getValue(value[13], 0)); + list.put(DeviceKey.Date, date); + list.put(DeviceKey.HRV,hrv); + list.put(DeviceKey.HeartRate,heart); + list.put(DeviceKey.ECGMoodValue, moodValue); + /* list.add(hashMap);*/ + offset = 27; + + } + byte[] tempValue = new byte[length - offset]; + System.arraycopy(value, offset, tempValue, 0, tempValue.length); + String ecgData = getEcgDataString(tempValue); + list.put(DeviceKey.ECGValue, ecgData); + /*list.add(hashMap);*/ + return maps; + } + + + protected static String getEcgDataString(byte[] value) { + StringBuffer stringBuffer = new StringBuffer(); + int length = value.length / 2 - 1; + for (int i = 0; i < length; i++) { + int ecgValue = getValue(value[i * 2 + 1], 1) + ResolveUtil.getValue(value[i * 2 + 2], 0); + if (ecgValue >= 32768) ecgValue = ecgValue - 65536; + stringBuffer.append(ecgValue).append(","); + } + return stringBuffer.toString(); + } + protected static String getPPGData(byte[] value) { + StringBuffer stringBuffer = new StringBuffer(); + for (int i = 0; i < value.length; i++) { + int ecgValue = getValue(value[i], 0); + stringBuffer.append(ecgValue).append(","); + } + return stringBuffer.toString(); + } + + protected static String getDoublePPGData(byte[] value) { + StringBuffer stringBuffer = new StringBuffer(); + int length = value.length / 2 ; + for (int i = 0; i < length; i++) { + int ecgValue = ResolveUtil.getValue(value[2*i], 1) + ResolveUtil.getValue(value[2*i+1], 0); + /* if (ecgValue >= 32768) ecgValue = ecgValue - 65536;*/ + stringBuffer.append(ecgValue).append(","); + } + return stringBuffer.toString(); + } + + public static Map getExerciseData(byte[] value) { + Map maps=new HashMap<>(); + maps.put(DeviceKey.DataType, BleConst.GetActivityModeData); + maps.put(DeviceKey.End,false); + List> list = new ArrayList<>(); + maps.put(DeviceKey.Data, list); + int count = 25; + int length = value.length; + int size = length / count; + if (size == 0) { + maps.put(DeviceKey.End,true); + return maps; + } + NumberFormat numberFormatCal =getNumberFormat(1); + NumberFormat numberFormat =getNumberFormat(2); + for (int i = 0; i < size; i++) { + int flag = 1 + (i + 1) * count; + if (flag < length && i==size-1&&value[flag] == (byte) 0xff) { + maps.put(DeviceKey.End,true); + } + Map hashMap = new HashMap<>(); + String date ="20"+ByteToHexString(value[3 + i * count]) + "." + + ByteToHexString(value[4 + i * count]) + "." + ByteToHexString(value[5 + i * count]) + " " + + ByteToHexString(value[6 + i * count]) + ":" + ByteToHexString(value[7 + i * count]) + ":" + ByteToHexString(value[8 + i * count]); + String mode = String.valueOf(getValue(value[9 + i * count], 0)); + String heartRate = String.valueOf(getValue(value[10 + i * count], 0)); + int periodTime = getData(2, 11 + i * count, value); + int steps = getData(2, 13 + i * count, value); + int speedMin = getValue(value[15 + i * count],0); + int speedS = getValue(value[16 + i * count],0); + byte[] valueCal = new byte[4]; + for (int j = 0; j < 4; j++) { + valueCal[3 - j] = value[17 +j+ i * count]; + } + byte[] valueDistance = new byte[4]; + for (int j = 0; j < 4; j++) { + valueDistance[3 - j] = value[21 +j+ i * count]; + } + float cal = getFloat(valueCal,0); + float distance = getFloat(valueDistance,0); + + hashMap.put(DeviceKey.Date, date); + hashMap.put(DeviceKey.ActivityMode, mode); + hashMap.put(DeviceKey.HeartRate, heartRate); + hashMap.put(DeviceKey.ActiveMinutes, String.valueOf(periodTime)); + hashMap.put(DeviceKey.Step, String.valueOf(steps)); + hashMap.put(DeviceKey.Pace, String.format("%02d",speedMin)+"'"+String.format("%02d",speedS)+"\""); + hashMap.put(DeviceKey.Distance, numberFormat.format(distance)); + hashMap.put(DeviceKey.Calories, numberFormatCal.format(cal)); + list.add(hashMap); + } + + return maps; + } + + public static Map getActivityExerciseData(byte[] value) { + Map maps=new HashMap<>(); + maps.put(DeviceKey.DataType, BleConst.SportData); + maps.put(DeviceKey.End,true); + + Map map = new HashMap<>(); + maps.put(DeviceKey.Data, map); + int heartRate = getValue(value[1], 0); + int steps = 0; + float kcal = 0; + for (int i = 0; i < 4; i++) { + steps += getValue(value[i + 2], i); + } + + byte[] valueCal = new byte[4]; + for (int i = 0; i < 4; i++) { + valueCal[3 - i] = value[i + 6]; + // kcal+=getFloat(value[i+6],i); + } + long time = 0; + for (int i = 0; i < 4; i++) { + time += getValue(value[i + 10], i); + } + kcal = getFloat(valueCal, 0); + map.put(DeviceKey.HeartRate, String.valueOf(heartRate)); + map.put(DeviceKey.Step, String.valueOf(steps)); + map.put(DeviceKey.Calories, String.valueOf(kcal)); + map.put(DeviceKey.ActiveMinutes, String.valueOf(time)); + return maps; + } + public static Map setTimeSuccessful(byte[] value) {//设置时间成功后返回手机一个数据包的最大长度 + Map maps=new HashMap<>(); + maps.put(DeviceKey.DataType,BleConst.SetDeviceTime); + maps.put(DeviceKey.End,true); + Map map = new HashMap<>(); + maps.put(DeviceKey.Data, map); + + map.put(DeviceKey.KPhoneDataLength, String.valueOf(getValue(value[1],0))); + return maps; + } + + + public static Map setMacSuccessful() {//设置时间成功后返回手机一个数据包的最大长度 + Map maps=new HashMap<>(); + maps.put(DeviceKey.DataType,BleConst.CMD_Set_Mac); + maps.put(DeviceKey.End,true); + Map map = new HashMap<>(); + maps.put(DeviceKey.Data, map); + return maps; + } + + + + + public static Map ECGData(byte[] value) { + Map maps=new HashMap<>(); + Map mapData = new HashMap<>(); + maps.put(DeviceKey.End,true); + maps.put(DeviceKey.DataType, BleConst.EcgppG); + maps.put(DeviceKey.Data, mapData); + mapData.put(DeviceKey.ECGValue,value); + return maps; + } + public static Map ecgData(byte[] value) { + Map maps=new HashMap<>(); + maps.put(DeviceKey.End,true); + maps.put(DeviceKey.DataType, BleConst.EcgppGstatus); + Map map = new HashMap<>(); + int index = ResolveUtil.getValue(value[1], 0); + map.put(DeviceKey.EcgStatus, index+""); + maps.put(DeviceKey.Data, map); + return maps; + } + + public static Map GetEcgPpgStatus(byte[] value) { + Map maps=new HashMap<>(); + maps.put(DeviceKey.End,true); + maps.put(DeviceKey.DataType, BleConst.GetEcgPpgStatus); + int index = ResolveUtil.getValue(value[1], 0); + Map map = new HashMap<>(); + map.put(DeviceKey.EcgStatus, index); + maps.put(DeviceKey.Data, map); + if(3==index){//有数据 + int HR = ResolveUtil.getValue(value[2], 0); + int VascularAging = ResolveUtil.getValue(value[3], 0); + int HeartRate = ResolveUtil.getValue(value[4], 0); + int Fatiguedegree= ResolveUtil.getValue(value[5], 0); + int HighPressure = ResolveUtil.getValue(value[6], 0); + int LowPressure = ResolveUtil.getValue(value[7], 0); + int ECGMoodValue = ResolveUtil.getValue(value[8], 0); + int ECGBreathValue = ResolveUtil.getValue(value[9], 0); + map.put(DeviceKey.HeartRate, HeartRate); + map.put(DeviceKey.VascularAging, VascularAging); + map.put(DeviceKey.HRV, HR); + map.put(DeviceKey.Fatiguedegree, Fatiguedegree); + map.put(DeviceKey.HighPressure, HighPressure); + map.put(DeviceKey.LowPressure, LowPressure); + map.put(DeviceKey.ECGMoodValue, ECGMoodValue); + map.put(DeviceKey.ECGBreathValue, ECGBreathValue); + String date ="20"+ByteToHexString(value[10]) + "."+ + ByteToHexString(value[11]) + "." + ByteToHexString(value[12]) + " "+ + ByteToHexString(value[13]) + ":" + ByteToHexString(value[14]) + ":" + ByteToHexString(value[15]); + maps.put(DeviceKey.Date,date); + } + return maps; + } + public static Map ECGResult(byte[] value) { + Map maps=new HashMap<>(); + Map mapData = new HashMap<>(); + maps.put(DeviceKey.End,true); + maps.put(DeviceKey.DataType, BleConst.ECGResult); + maps.put(DeviceKey.Data, mapData); + int resultValue= getValue(value[1],0); + String date ="20"+ByteToHexString(value[10]) + "."+ + ByteToHexString(value[11]) + "." + ByteToHexString(value[12]) + " "+ + ByteToHexString(value[13]) + ":" + ByteToHexString(value[14]) + ":" + ByteToHexString(value[15]); + int hrv = ResolveUtil.getValue(value[2], 0); + int avBlock = ResolveUtil.getValue(value[3], 0); + int hr = ResolveUtil.getValue(value[4], 0); + int strees = ResolveUtil.getValue(value[5], 0); + int highBp = ResolveUtil.getValue(value[6], 0); + int lowBp = ResolveUtil.getValue(value[7], 0); + int moodValue = ResolveUtil.getValue(value[8], 0); + int breathValue = ResolveUtil.getValue(value[9], 0); + mapData.put(DeviceKey.ECGResultValue,String.valueOf(resultValue)); + mapData.put(DeviceKey.Date,date); + mapData.put(DeviceKey.ECGHrvValue,String.valueOf(hrv)); + mapData.put(DeviceKey.ECGAvBlockValue,String.valueOf(avBlock)); + mapData.put(DeviceKey.ECGHrValue,String.valueOf(hr)); + mapData.put(DeviceKey.ECGStreesValue,String.valueOf(strees)); + mapData.put(DeviceKey.ECGhighBpValue,String.valueOf(highBp)); + mapData.put(DeviceKey.ECGLowBpValue,String.valueOf(lowBp)); + mapData.put(DeviceKey.ECGMoodValue,String.valueOf(moodValue)); + mapData.put(DeviceKey.ECGBreathValue,String.valueOf(breathValue)); + return maps; + } + public static Map enterEcg(byte[] value) { + Map maps=new HashMap<>(); + Map mapData = new HashMap<>(); + maps.put(DeviceKey.End,true); + maps.put(DeviceKey.DataType, BleConst.ENTERECG); + maps.put(DeviceKey.Data, mapData); + + return maps; + } + public static Map updateClockSuccessful(byte[] value) { + Map maps=new HashMap<>(); + maps.put(DeviceKey.DataType, BleConst.SetAlarmClockWithAllClock); + maps.put(DeviceKey.End,true); + Map map = new HashMap<>(); + maps.put(DeviceKey.Data, map); + map.put(DeviceKey.KClockLast, String.valueOf(getValue(value[value.length-1],0))); + return maps; + } + + public static int getData(int length, int start, byte[] value) { + int data = 0; + for (int j = 0; j < length; j++) { + data += getValue(value[j + start], j); + } + return data; + } + + public static int getValue(byte b, int count) { + return (int) ((b & 0xff) * Math.pow(256, count)); + } + + public static String ByteToHexString(byte a) { + String s = ""; + s = Integer.toHexString(new Byte(a).intValue()); + if (s.length() == 1) { + s = "0" + s; + } + return s; + } + + public static float getFloat(byte[] arr, int index) {//字节转浮点 + return Float.intBitsToFloat(getInt(arr, index)); + } + + public static int getInt(byte[] arr, int index) { + return (0xff000000 & (arr[index + 0] << 24)) | + (0x00ff0000 & (arr[index + 1] << 16)) | + (0x0000ff00 & (arr[index + 2] << 8)) | + (0x000000ff & arr[index + 3]); + } + public static NumberFormat getNumberFormat(int max){ + NumberFormat numberFormat = NumberFormat.getNumberInstance(Locale.ENGLISH); + numberFormat.setMaximumFractionDigits(max); + numberFormat.setGroupingUsed(false); + return numberFormat; + } + public static String byte2Hex(byte data) { + return String.format("%02X ", data); + } + public static String getCurrentTimeZone() { + TimeZone tz = TimeZone.getDefault(); + String strTz = tz.getDisplayName(false, TimeZone.SHORT); + Log.i(TAG, "getCurrentTimeZone: "+strTz); + int _t = TimeZone.getDefault().getOffset(System.currentTimeMillis()) / (3600 * 1000); + strTz = String.valueOf(_t); + strTz = "GMT" + strTz; + Log.i(TAG, "getCurrentTimeZone: "+strTz); + return strTz; + + } + public static Map setMethodSuccessful(String dataType) {//设置时间成功后返回手机一个数据包的最大长度 + Map maps = new HashMap<>(); + maps.put(DeviceKey.DataType, dataType); + maps.put(DeviceKey.End, true); + maps.put(DeviceKey.Data, new HashMap<>()); + return maps; + } + + public static Map GetTemperatureCorrectionValue(byte[] value) { + Map maps=new HashMap<>(); + maps.put(DeviceKey.DataType, BleConst.CMD_Set_TemperatureCorrection); + maps.put(DeviceKey.End,true); + MapmapData=new HashMap<>(); + maps.put(DeviceKey.Data, mapData); + byte[]tempValue=new byte[2]; + tempValue[0]=value[3]; + tempValue[1]=value[2]; + int TemperatureCorrectionValue = BleSDK.byteArrayToInt(tempValue); + + mapData.put(DeviceKey.TemperatureCorrectionValue, String.valueOf(TemperatureCorrectionValue)); + return maps; + } + + + public static String[] getWorkOutReminder(byte[] value) { + String[] activityAlarm = new String[6]; + String startHour = bcd2String(value[1]); + String startMin = bcd2String(value[2]); + String days= getValue(value[3], 0)+""; + String week = getByteString(value[4]); + int enable=getValue(value[5], 0); + int time = getValue(value[6], 0)+getValue(value[7],1); + + activityAlarm[0] = startHour; + activityAlarm[1] = startMin; + activityAlarm[2] = days; + activityAlarm[3] = week; + activityAlarm[4] = String.valueOf(enable); + activityAlarm[5] = String.valueOf(time); + + return activityAlarm; + } + + /** + * + * + */ + public static Map GetWomenHealth(byte[] value) { + Map hashMap = new HashMap<>(); + hashMap.put(DeviceKey.DataType, BleConst.GetWomenHealth); + hashMap.put(DeviceKey.End,true); + MapmapData=new HashMap<>(); + mapData.put(DeviceKey.Month, bcd2String(value[1])); + mapData.put(DeviceKey.Day, bcd2String(value[2])); + mapData.put(DeviceKey.MenstrualPeriod_Lenth, bcd2String(value[3])); + mapData.put(DeviceKey.MenstrualPeriod_Period, bcd2String(value[4])); + hashMap.put(DeviceKey.Data, mapData); + return hashMap; + } + + public static Map Getpeisu(int nungb) { + Map hashMap = new HashMap<>(); + hashMap.put(DeviceKey.DataType, BleConst.Getpeisu); + hashMap.put(DeviceKey.End,true); + MapmapData=new HashMap<>(); + mapData.put(DeviceKey.Day, nungb+""); + hashMap.put(DeviceKey.Data, mapData); + return hashMap; + } + + public static Map GetpeisuER() { + Map hashMap = new HashMap<>(); + hashMap.put(DeviceKey.DataType, BleConst.GetpeisuER); + hashMap.put(DeviceKey.End,true); + MapmapData=new HashMap<>(); + hashMap.put(DeviceKey.Data, mapData); + return hashMap; + } + /** + * + * + */ + public static Map GetPregnancyCycle(byte[] value) { + Map hashMap = new HashMap<>(); + hashMap.put(DeviceKey.DataType, BleConst.GetPregnancyCycle); + hashMap.put(DeviceKey.End,true); + MapmapData=new HashMap<>(); + mapData.put(DeviceKey.Year, bcd2String(value[3])); + mapData.put(DeviceKey.Month, bcd2String(value[4])); + mapData.put(DeviceKey.Day, bcd2String(value[5])); + hashMap.put(DeviceKey.Data, mapData); + return hashMap; + } + + + + public static Map getHistoryGpsData(byte[] value) { + + List> list = new ArrayList<>(); + Map mmp=new HashMap<>(); + mmp.put(DeviceKey.DataType, BleConst.Gps); + mmp.put(DeviceKey.End, false); + int count = 59; + int length = value.length; + int size = length / count; + if (size == 0) { + return mmp; + } + int index=size*50; + if ((value[value.length - 1] == (byte) 0xff && value[value.length - 2] == (byte) 0x5a)){ + mmp.put(DeviceKey.End, true); + } + for (int i = 0; i < size; i++) { + int flag = 1 + (i + 1) * count; + Map hashMap = new HashMap<>(); + int id = getValue(value[1 + i * count], 0) + getValue(value[2 + i * count], 1)%index; + String date ="20"+bcd2String(value[3 + i * count]) + "." + + bcd2String(value[4 + i * count]) + "." + bcd2String(value[5 + i * count]) + " " + + bcd2String(value[6 + i * count]) + ":" + bcd2String(value[7 + i * count]) + ":" + bcd2String(value[8 + i * count]); + byte[] valueLatitude = new byte[4]; + byte[] valueLongitude = new byte[4]; + StringBuffer stringBufferLatitude = new StringBuffer(); + StringBuffer stringBufferLongitude = new StringBuffer(); + for (int k = 0; k < 6; k++) { + for (int j = 0; j < 4; j++) { + valueLatitude[3 - j] = value[9 + j + i * count + k * 8]; + valueLongitude[3 - j] = value[13 + j + i * count + k * 8]; + } + String Latitude = String.valueOf(getFloat(valueLatitude, 0)); + String Longitude = String.valueOf(getFloat(valueLongitude, 0)); + stringBufferLatitude.append(Latitude).append(k == 5 ? "" : ","); + stringBufferLongitude.append(Longitude).append(k == 5 ? "" : ","); + } + hashMap.put(DeviceKey.Date, date.length() == 17 ? date : "2019.01.01 00:00:00"); + hashMap.put(DeviceKey.Latitude, stringBufferLatitude.toString()); + hashMap.put(DeviceKey.Longitude, stringBufferLongitude.toString()); + list.add(hashMap); + } + mmp.put(DeviceKey.Data, list.toString()); + + return mmp; + } + + + static double[] B_HR = new double[]{ + 0.012493658738073, + 0, + -0.024987317476146, + 0, + 0.012493658738073 + }; + static double[] A_HR = new double[]{ + 1, + -3.658469528008591, + 5.026987876570873, + -3.078346646055655, + 0.709828779797188 + }; + static double[] inPut = new double[]{0, 0, 0, 0, 0}; + static double[] outPut = new double[]{0, 0, 0, 0, 0}; + + public static double filterEcgData(double data) { + inPut[4] = data * 18.3 / 128 + 0.06; + outPut[4] = B_HR[0] * inPut[4] + + B_HR[1] * inPut[3] + + B_HR[2] * inPut[2] + + B_HR[3] * inPut[1] + + B_HR[4] * inPut[0] - + A_HR[1] * outPut[3] - + A_HR[2] * outPut[2] - + A_HR[3] * outPut[1] - + A_HR[4] * outPut[0]; + for (int i = 0; i < 4; i++) { + inPut[i] = inPut[i + 1]; + outPut[i] = outPut[i + 1]; + } + return outPut[4]; + } +} diff --git a/android/blesdk_2301/src/test/java/com/jstyle/blesdk2301/ExampleUnitTest.java b/android/blesdk_2301/src/test/java/com/jstyle/blesdk2301/ExampleUnitTest.java new file mode 100644 index 00000000..bf42dda1 --- /dev/null +++ b/android/blesdk_2301/src/test/java/com/jstyle/blesdk2301/ExampleUnitTest.java @@ -0,0 +1,17 @@ +package com.jstyle.blesdk2301; + +import org.junit.Test; + +import static org.junit.Assert.*; + +/** + * Example local unit test, which will execute on the development machine (host). + * + * @see Testing documentation + */ +public class ExampleUnitTest { + @Test + public void addition_isCorrect() throws Exception { + assertEquals(4, 2 + 2); + } +} \ No newline at end of file diff --git a/android/build.gradle b/android/build.gradle index b5185cca..c036b096 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -9,6 +9,7 @@ buildscript { // ndkVersion = "21.4.7075529" // } + ext.kotlin_version = '1.8.20' repositories { google() jcenter() @@ -21,7 +22,7 @@ buildscript { dependencies { classpath 'com.android.tools.build:gradle:7.1.3' - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:2.0.0" classpath 'com.google.gms:google-services:4.3.8' // classpath 'com.google.firebase:firebase-crashlytics-gradle:2.8.1' classpath 'com.huawei.agconnect:agcp:1.5.2.300' diff --git a/android/settings.gradle b/android/settings.gradle index 5a2f14fb..e1e9493a 100644 --- a/android/settings.gradle +++ b/android/settings.gradle @@ -1,4 +1,4 @@ -include ':app' +include ':app', ':blesdk_2301' def flutterProjectRoot = rootProject.projectDir.parentFile.toPath() diff --git a/assets/images/new-design/passport.svg b/assets/images/new-design/passport.svg index a3a060f0..1e45f593 100644 --- a/assets/images/new-design/passport.svg +++ b/assets/images/new-design/passport.svg @@ -1,134 +1,134 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ios/Podfile b/ios/Podfile index 5495f0ca..52d8322a 100644 --- a/ios/Podfile +++ b/ios/Podfile @@ -33,6 +33,7 @@ target 'Runner' do pod 'OpenTok', '~> 2.22.0' pod 'VTO2Lib' + pod 'VTMProductLib' flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) end @@ -84,4 +85,4 @@ post_integrate do |installer| end end project.save() -end \ No newline at end of file +end diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index 130bc8cc..f519b6f5 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -15,11 +15,28 @@ 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; 4EE411B2E3CB5D4F81AE5078 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3D1513E0724DAFB89C198BDD /* Pods_Runner.framework */; }; 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; + 7606E6682B68D823004B0F1D /* VTProLib.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7606E6672B68D823004B0F1D /* VTProLib.xcframework */; }; 762D738E274E42650063CE73 /* ring_30Sec.caf in Resources */ = {isa = PBXBuildFile; fileRef = 762D738C274E42650063CE73 /* ring_30Sec.caf */; }; 762D738F274E42650063CE73 /* ring_30Sec.mp3 in Resources */ = {isa = PBXBuildFile; fileRef = 762D738D274E42650063CE73 /* ring_30Sec.mp3 */; }; + 7647AD572BFB4D4C006D6FBF /* CheckMeUsersListModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7647AD562BFB4D4C006D6FBF /* CheckMeUsersListModel.swift */; }; + 7647AD592BFB7904006D6FBF /* CheckMeTemperatureModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7647AD582BFB7904006D6FBF /* CheckMeTemperatureModel.swift */; }; + 7647AD5B2BFC92B7006D6FBF /* CheckMeOxymeterModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7647AD5A2BFC92B7006D6FBF /* CheckMeOxymeterModel.swift */; }; + 765948192B4E72A100D96E03 /* TupleToArray.swift in Sources */ = {isa = PBXBuildFile; fileRef = 765948182B4E72A100D96E03 /* TupleToArray.swift */; }; + 7659481B2B4E899200D96E03 /* VTECGParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7659481A2B4E899200D96E03 /* VTECGParser.swift */; }; + 7659481E2B4EBEB600D96E03 /* VTBLEUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = 7659481D2B4EBEB600D96E03 /* VTBLEUtils.m */; }; + 765948202B4EC54400D96E03 /* EKGFileDetail.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7659481F2B4EC54400D96E03 /* EKGFileDetail.swift */; }; + 765948222B4FD1B500D96E03 /* SP20RTParamModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 765948212B4FD1B500D96E03 /* SP20RTParamModel.swift */; }; + 765948242B4FE12B00D96E03 /* DoubleExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 765948232B4FE12B00D96E03 /* DoubleExtension.swift */; }; + 765948262B566F2000D96E03 /* BpRtDataResultModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 765948252B566F2000D96E03 /* BpRtDataResultModel.swift */; }; + 765948282B56783900D96E03 /* BpRtDataMeasuringModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 765948272B56783900D96E03 /* BpRtDataMeasuringModel.swift */; }; + 7659482A2B57FE5D00D96E03 /* BleDeviceModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 765948292B57FE5D00D96E03 /* BleDeviceModel.swift */; }; + 7659482F2B5813F200D96E03 /* ECGRtModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7659482E2B5813F200D96E03 /* ECGRtModel.swift */; }; + 76801ACA2AD7F7DA00126C2D /* CRAP20Lib.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 76801AC92AD7F7D900126C2D /* CRAP20Lib.xcframework */; }; 76815B27275F381C00E66E94 /* HealthKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 76815B26275F381C00E66E94 /* HealthKit.framework */; }; 76962ECE28AE5C10004EAE09 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 76962ECD28AE5C10004EAE09 /* GoogleService-Info.plist */; }; 76F2556127F1FFED0062C1CD /* PassKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 76F2556027F1FFED0062C1CD /* PassKit.framework */; }; + 76F3B9612ACEBE8E00D7A698 /* VTDevice.m in Sources */ = {isa = PBXBuildFile; fileRef = 76F3B95E2ACEBE8D00D7A698 /* VTDevice.m */; }; + 76F789982ACEA25600FC6C38 /* BLEPlatformBridge.swift in Sources */ = {isa = PBXBuildFile; fileRef = 76F789972ACEA25600FC6C38 /* BLEPlatformBridge.swift */; }; 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; @@ -42,19 +59,6 @@ E9F7623B25922BCE00FB5CCF /* FlutterConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9F7623A25922BCE00FB5CCF /* FlutterConstants.swift */; }; /* End PBXBuildFile section */ -/* Begin PBXCopyFilesBuildPhase section */ - 9705A1C41CF9048500538489 /* Embed Frameworks */ = { - isa = PBXCopyFilesBuildPhase; - buildActionMask = 2147483647; - dstPath = ""; - dstSubfolderSpec = 10; - files = ( - ); - name = "Embed Frameworks"; - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXCopyFilesBuildPhase section */ - /* Begin PBXFileReference section */ 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; @@ -67,6 +71,7 @@ 6EE8819867EC2775AB578377 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 7606E6672B68D823004B0F1D /* VTProLib.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = VTProLib.xcframework; path = Runner/BLE_SDKs/VTProLib.xcframework; sourceTree = ""; }; 762D738C274E42650063CE73 /* ring_30Sec.caf */ = {isa = PBXFileReference; lastKnownFileType = file; name = ring_30Sec.caf; path = ../../assets/sounds/ring_30Sec.caf; sourceTree = ""; }; 762D738D274E42650063CE73 /* ring_30Sec.mp3 */ = {isa = PBXFileReference; lastKnownFileType = audio.mp3; name = ring_30Sec.mp3; path = ../../assets/sounds/ring_30Sec.mp3; sourceTree = ""; }; 7643E4042BE0D0B400BD2F25 /* ar */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ar; path = ar.lproj/Main_Custom.strings; sourceTree = ""; }; @@ -75,6 +80,9 @@ 76815B26275F381C00E66E94 /* HealthKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = HealthKit.framework; path = System/Library/Frameworks/HealthKit.framework; sourceTree = SDKROOT; }; 76962ECD28AE5C10004EAE09 /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = ""; }; 76F2556027F1FFED0062C1CD /* PassKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = PassKit.framework; path = System/Library/Frameworks/PassKit.framework; sourceTree = SDKROOT; }; + 76F3B95D2ACEBE8D00D7A698 /* VTDevice.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = VTDevice.h; sourceTree = ""; }; + 76F3B95E2ACEBE8D00D7A698 /* VTDevice.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = VTDevice.m; sourceTree = ""; }; + 76F789972ACEA25600FC6C38 /* BLEPlatformBridge.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BLEPlatformBridge.swift; sourceTree = ""; }; 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; 838788A2BEDC4910F4B029A6 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; @@ -110,10 +118,12 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 76801ACA2AD7F7DA00126C2D /* CRAP20Lib.xcframework in Frameworks */, 76F2556127F1FFED0062C1CD /* PassKit.framework in Frameworks */, 76815B27275F381C00E66E94 /* HealthKit.framework in Frameworks */, E9620805255C2ED100D3A35D /* NetworkExtension.framework in Frameworks */, 4EE411B2E3CB5D4F81AE5078 /* Pods_Runner.framework in Frameworks */, + 7606E6682B68D823004B0F1D /* VTProLib.xcframework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -133,6 +143,8 @@ 555EAAA626EFB641859EF0BE /* Frameworks */ = { isa = PBXGroup; children = ( + 7606E6672B68D823004B0F1D /* VTProLib.xcframework */, + 76801AC92AD7F7D900126C2D /* CRAP20Lib.xcframework */, 76F2556027F1FFED0062C1CD /* PassKit.framework */, 76815B26275F381C00E66E94 /* HealthKit.framework */, E9620804255C2ED100D3A35D /* NetworkExtension.framework */, @@ -151,6 +163,46 @@ path = Pods; sourceTree = ""; }; + 7659482D2B5813E300D96E03 /* ECGRTModels */ = { + isa = PBXGroup; + children = ( + 7659482E2B5813F200D96E03 /* ECGRtModel.swift */, + ); + path = ECGRTModels; + sourceTree = ""; + }; + 76A8B2752ACD5DB80073357D /* Ble */ = { + isa = PBXGroup; + children = ( + 76F3B95C2ACEBE2800D7A698 /* Utils */, + 76F789972ACEA25600FC6C38 /* BLEPlatformBridge.swift */, + ); + path = Ble; + sourceTree = ""; + }; + 76F3B95C2ACEBE2800D7A698 /* Utils */ = { + isa = PBXGroup; + children = ( + 7659482D2B5813E300D96E03 /* ECGRTModels */, + 7659481C2B4EBEB600D96E03 /* VTBLEUtils.h */, + 7659481D2B4EBEB600D96E03 /* VTBLEUtils.m */, + 7659481A2B4E899200D96E03 /* VTECGParser.swift */, + 76F3B95D2ACEBE8D00D7A698 /* VTDevice.h */, + 76F3B95E2ACEBE8D00D7A698 /* VTDevice.m */, + 765948182B4E72A100D96E03 /* TupleToArray.swift */, + 7659481F2B4EC54400D96E03 /* EKGFileDetail.swift */, + 765948212B4FD1B500D96E03 /* SP20RTParamModel.swift */, + 765948232B4FE12B00D96E03 /* DoubleExtension.swift */, + 765948252B566F2000D96E03 /* BpRtDataResultModel.swift */, + 765948272B56783900D96E03 /* BpRtDataMeasuringModel.swift */, + 765948292B57FE5D00D96E03 /* BleDeviceModel.swift */, + 7647AD562BFB4D4C006D6FBF /* CheckMeUsersListModel.swift */, + 7647AD582BFB7904006D6FBF /* CheckMeTemperatureModel.swift */, + 7647AD5A2BFC92B7006D6FBF /* CheckMeOxymeterModel.swift */, + ); + path = Utils; + sourceTree = ""; + }; 9740EEB11CF90186004384FC /* Flutter */ = { isa = PBXGroup; children = ( @@ -211,6 +263,7 @@ E91B538C256AAA6500E96549 /* Helper */ = { isa = PBXGroup; children = ( + 76A8B2752ACD5DB80073357D /* Ble */, E91B538D256AAA6500E96549 /* GlobalHelper.swift */, E91B538E256AAA6500E96549 /* Extensions.swift */, E91B538F256AAA6500E96549 /* API.swift */, @@ -254,7 +307,6 @@ 97C146EA1CF9000F007C117D /* Sources */, 97C146EB1CF9000F007C117D /* Frameworks */, 97C146EC1CF9000F007C117D /* Resources */, - 9705A1C41CF9048500538489 /* Embed Frameworks */, 3B06AD1E1E4923F5004D2608 /* Thin Binary */, 487FDD6493EB9AE7D8E39485 /* [CP] Embed Pods Frameworks */, 4671D3CD126F6635F4B75A6E /* [CP] Copy Pods Resources */, @@ -421,19 +473,34 @@ 306FE6C8271D790C002D6EFC /* OpenTokPlatformBridge.swift in Sources */, E923EFD225863FDF00E3E751 /* GeoZoneModel.swift in Sources */, E91B539A256AAA6500E96549 /* MainFlutterVC.swift in Sources */, + 7647AD5B2BFC92B7006D6FBF /* CheckMeOxymeterModel.swift in Sources */, E91B539C256AAA6500E96549 /* HMG_GUEST_bkp.swift in Sources */, E91B5396256AAA6500E96549 /* GlobalHelper.swift in Sources */, + 765948282B56783900D96E03 /* BpRtDataMeasuringModel.swift in Sources */, + 7647AD572BFB4D4C006D6FBF /* CheckMeUsersListModel.swift in Sources */, + 7647AD592BFB7904006D6FBF /* CheckMeTemperatureModel.swift in Sources */, + 7659481E2B4EBEB600D96E03 /* VTBLEUtils.m in Sources */, E923EFD4258645C100E3E751 /* HMG_Geofence.swift in Sources */, E923EFD62587443800E3E751 /* HMGPlatformBridge.swift in Sources */, + 76F3B9612ACEBE8E00D7A698 /* VTDevice.m in Sources */, 301C79AE27200D9F0016307B /* OpenTokRemoteVideoFactory.swift in Sources */, E9F7623B25922BCE00FB5CCF /* FlutterConstants.swift in Sources */, 306FE6CB271D8B73002D6EFC /* OpenTok.swift in Sources */, 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */, + 76F789982ACEA25600FC6C38 /* BLEPlatformBridge.swift in Sources */, E9E27168256E3A4000F49B69 /* LocalizedFromFlutter.swift in Sources */, 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, 301C79B027200DED0016307B /* OpenTokLocalVideoFactory.swift in Sources */, E9C8C136256BACDA00EFFB62 /* HMG_Guest.swift in Sources */, + 7659481B2B4E899200D96E03 /* VTECGParser.swift in Sources */, + 765948192B4E72A100D96E03 /* TupleToArray.swift in Sources */, + 765948262B566F2000D96E03 /* BpRtDataResultModel.swift in Sources */, + 765948222B4FD1B500D96E03 /* SP20RTParamModel.swift in Sources */, + 7659482F2B5813F200D96E03 /* ECGRtModel.swift in Sources */, E91B539B256AAA6500E96549 /* HMG_Internet.swift in Sources */, + 765948202B4EC54400D96E03 /* EKGFileDetail.swift in Sources */, + 765948242B4FE12B00D96E03 /* DoubleExtension.swift in Sources */, + 7659482A2B57FE5D00D96E03 /* BleDeviceModel.swift in Sources */, E91B5397256AAA6500E96549 /* Extensions.swift in Sources */, E91B5398256AAA6500E96549 /* API.swift in Sources */, ); @@ -539,6 +606,7 @@ "$(inherited)", "$(PROJECT_DIR)/Flutter", ); + GCC_NO_COMMON_BLOCKS = YES; INFOPLIST_FILE = Runner/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 13.0; LD_RUNPATH_SEARCH_PATHS = ( @@ -686,6 +754,7 @@ "$(inherited)", "$(PROJECT_DIR)/Flutter", ); + GCC_NO_COMMON_BLOCKS = YES; INFOPLIST_FILE = Runner/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 13.0; LD_RUNPATH_SEARCH_PATHS = ( @@ -724,6 +793,7 @@ "$(inherited)", "$(PROJECT_DIR)/Flutter", ); + GCC_NO_COMMON_BLOCKS = YES; INFOPLIST_FILE = Runner/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 13.0; LD_RUNPATH_SEARCH_PATHS = ( diff --git a/ios/Runner/AppDelegate.swift b/ios/Runner/AppDelegate.swift index d8e5e0f6..6fe4859d 100644 --- a/ios/Runner/AppDelegate.swift +++ b/ios/Runner/AppDelegate.swift @@ -15,11 +15,16 @@ var userNotificationCenterDelegate:UNUserNotificationCenterDelegate? = nil GeneratedPluginRegistrant.register(with: self) initializePlatformChannels() - + if let _ = launchOptions?[.location] { HMG_Geofence.initGeofencing() } + // // Prepare channel + // FlutterViewController* controller = (FlutterViewController*)self.window.rootViewController; + // self->channel = [FlutterEventChannel eventChannelWithName:@"events" binaryMessenger:controller]; + // [self->channel setStreamHandler:[EventHandler new]]; + UNUserNotificationCenter.current().delegate = self return super.application(application, didFinishLaunchingWithOptions: launchOptions) } @@ -29,11 +34,15 @@ var userNotificationCenterDelegate:UNUserNotificationCenterDelegate? = nil if let mainViewController = window.rootViewController as? MainFlutterVC{ // platform initialization suppose to be in foreground flutterViewController = mainViewController HMGPlatformBridge.initialize(flutterViewController: flutterViewController) + BLEPlatformBridge.initialize(flutterViewController: flutterViewController) +// BLEPlatformBridgeEKG.initialize(flutterViewController: flutterViewController) // platform initialization for EKG OpenTokPlatformBridge.initialize(flutterViewController: flutterViewController, registrar: self.registrar(forPlugin: "open-tok")) }else if let mainViewController = initialViewController(){ // platform initialization suppose to be in background flutterViewController = mainViewController HMGPlatformBridge.initialize(flutterViewController: flutterViewController) + BLEPlatformBridge.initialize(flutterViewController: flutterViewController) +// BLEPlatformBridgeEKG.initialize(flutterViewController: flutterViewController) // platform initialization for EKG OpenTokPlatformBridge.initialize(flutterViewController: flutterViewController, registrar: self.registrar(forPlugin: "open-tok")) } } @@ -57,14 +66,14 @@ extension AppDelegate{ /* let dart = FlutterDartProject(precompiledDartBundle: .main) - let engine = FlutterEngine(name: "com.hmg.cs", project: dart, allowHeadlessExecution: true) - if engine.run(){ - flutterMethodChannel = FlutterMethodChannel(name: "HMG-Platform-Bridge", binaryMessenger: engine.binaryMessenger) - - Timer.scheduledTimer(withTimeInterval: 0.5, repeats: false) { (timer) in - FlutterText.with(key: "alreadyConnectedHmgNetwork"){ localized in - print(localized) - } - } - } + let engine = FlutterEngine(name: "com.hmg.cs", project: dart, allowHeadlessExecution: true) + if engine.run(){ + flutterMethodChannel = FlutterMethodChannel(name: "HMG-Platform-Bridge", binaryMessenger: engine.binaryMessenger) + + Timer.scheduledTimer(withTimeInterval: 0.5, repeats: false) { (timer) in + FlutterText.with(key: "alreadyConnectedHmgNetwork"){ localized in + print(localized) + } + } + } */ diff --git a/ios/Runner/Controllers/MainFlutterVC.swift b/ios/Runner/Controllers/MainFlutterVC.swift index 4f91d052..50ddaaf4 100644 --- a/ios/Runner/Controllers/MainFlutterVC.swift +++ b/ios/Runner/Controllers/MainFlutterVC.swift @@ -105,14 +105,14 @@ class MainFlutterVC: FlutterViewController { // Register Geofence func registerHmgGeofences(result: @escaping FlutterResult){ - flutterMethodChannel?.invokeMethod("getGeofencePreferenceKey", arguments: nil){ geoFencesJsonString in - if let jsonString = geoFencesJsonString as? String{ - let allZones = GeoZoneModel.list(from: jsonString) - HMG_Geofence().register(geoZones: allZones) - - }else{ - } - } +// flutterMethodChannel?.invokeMethod("getGeofencePreferenceKey", arguments: nil){ geoFencesJsonString in +// if let jsonString = geoFencesJsonString as? String{ +// let allZones = GeoZoneModel.list(from: jsonString) +// HMG_Geofence().register(geoZones: allZones) +// +// }else{ +// } +// } } } diff --git a/ios/Runner/Helper/FlutterConstants.swift b/ios/Runner/Helper/FlutterConstants.swift index f1b3f098..122f2c8f 100644 --- a/ios/Runner/Helper/FlutterConstants.swift +++ b/ios/Runner/Helper/FlutterConstants.swift @@ -6,6 +6,7 @@ // import UIKit +import Flutter class FlutterConstants{ static var LOG_GEOFENCE_URL:String? diff --git a/ios/Runner/Helper/HMGPlatformBridge.swift b/ios/Runner/Helper/HMGPlatformBridge.swift index fd9fb401..fb235980 100644 --- a/ios/Runner/Helper/HMGPlatformBridge.swift +++ b/ios/Runner/Helper/HMGPlatformBridge.swift @@ -8,11 +8,13 @@ import UIKit import NetworkExtension import SystemConfiguration.CaptiveNetwork +import Flutter var flutterMethodChannel:FlutterMethodChannel? = nil fileprivate var mainViewController:MainFlutterVC! class HMGPlatformBridge{ + private let channelName = "HMG-Platform-Bridge" private static var shared_:HMGPlatformBridge? @@ -60,7 +62,7 @@ class HMGPlatformBridge{ func connectHMGInternetWifi(methodCall:FlutterMethodCall ,result: @escaping FlutterResult){ guard let pateintId = (methodCall.arguments as? [Any])?.first as? String - else { return assert(true, "Missing or invalid arguments (Must have one argument 'String at 0'") } + else { return assert(true, "Missing or invalid arguments (Must have one argument 'String at 0'") } HMG_Internet.shared.connect(patientId: pateintId) { (status, message) in @@ -136,5 +138,5 @@ class HMGPlatformBridge{ HMG_Geofence.shared().unRegisterAll() result(true) } - + } diff --git a/ios/Runner/Helper/OpenTokPlatformBridge.swift b/ios/Runner/Helper/OpenTokPlatformBridge.swift index 4da39dc4..efc3b291 100644 --- a/ios/Runner/Helper/OpenTokPlatformBridge.swift +++ b/ios/Runner/Helper/OpenTokPlatformBridge.swift @@ -8,6 +8,7 @@ import UIKit import NetworkExtension import SystemConfiguration.CaptiveNetwork +import Flutter fileprivate var openTok:OpenTok? diff --git a/ios/Runner/Runner-Bridging-Header.h b/ios/Runner/Runner-Bridging-Header.h index 308a2a56..ebd7fcdd 100644 --- a/ios/Runner/Runner-Bridging-Header.h +++ b/ios/Runner/Runner-Bridging-Header.h @@ -1 +1,4 @@ #import "GeneratedPluginRegistrant.h" +#import "VTBLEUtils.h" +#import "VTDevice.h" +#import diff --git a/lib/config/localized_values.dart b/lib/config/localized_values.dart index 5a775c5b..db97e6a5 100644 --- a/lib/config/localized_values.dart +++ b/lib/config/localized_values.dart @@ -1949,6 +1949,7 @@ const Map localizedValues = { "admissionReqNo": {"en": "Admission Request No", "ar": "رقم طلب القبول:"}, "dischargeDate": {"en": "Discharge Date", "ar": "تاريخ التفريغ"}, "selectAdmissionText": {"en": "Please select one of the admissions from below to view medical reports:", "ar": "يرجى تحديد أحد حالات القبول من الأسفل لعرض التقارير الطبية:"}, + "spirometer": {"en": "Spirometer", "ar": "مقياس التنفس"}, "invalidEligibility": { "en": "You cannot make online payment because you are not eligible to use the provided service.", "ar": "لا يمكنك إجراء الدفع عبر الإنترنت لأنك غير مؤهل لاستخدام الخدمة المقدمة." diff --git a/lib/main.dart b/lib/main.dart index a0a584a5..39690e70 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -4,6 +4,7 @@ import 'package:diplomaticquarterapp/config/config.dart'; import 'package:diplomaticquarterapp/core/viewModels/PharmacyPagesViewModel.dart'; import 'package:diplomaticquarterapp/core/viewModels/dashboard_view_model.dart'; import 'package:diplomaticquarterapp/models/Appointments/toDoCountProviderModel.dart'; +import 'package:diplomaticquarterapp/pages/medical/my_trackers/my_trackers_view_model/my_trackers_view_model.dart'; import 'package:diplomaticquarterapp/routes.dart'; import 'package:diplomaticquarterapp/services/payfort_services/payfort_view_model.dart'; import 'package:diplomaticquarterapp/services/robo_search/event_provider.dart'; @@ -16,6 +17,8 @@ import 'package:diplomaticquarterapp/uitl/translations_delegate_base.dart'; import 'package:firebase_core/firebase_core.dart'; import 'package:flutter/material.dart'; import 'package:flutter_localizations/flutter_localizations.dart'; +import 'package:in_app_update/in_app_update.dart'; +import 'package:logger/logger.dart'; import 'package:provider/provider.dart'; import 'config/size_config.dart'; @@ -24,6 +27,8 @@ import 'core/viewModels/project_view_model.dart'; import 'locator.dart'; import 'pages/pharmacies/compare-list.dart'; +Logger logger = Logger(); + void main() async { WidgetsFlutterBinding.ensureInitialized(); await Firebase.initializeApp(); @@ -133,6 +138,8 @@ class _MyApp extends State { ChangeNotifierProvider( create: (context) => CompareList(), ), + ChangeNotifierProvider(create: (context) => MyTrackersViewModel()), + ChangeNotifierProvider( create: (context) => OrderPreviewViewModel(), ), diff --git a/lib/pages/ble_screen.dart b/lib/pages/ble_screen.dart new file mode 100644 index 00000000..1fe607b4 --- /dev/null +++ b/lib/pages/ble_screen.dart @@ -0,0 +1,57 @@ +import 'package:diplomaticquarterapp/pages/medical/my_trackers/ble_helpers/ble_connect_helper.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:photo_view/photo_view.dart'; + +class BLEScreen extends StatefulWidget { + @override + State createState() => _BLEScreenState(); +} + +class _BLEScreenState extends State { + EventChannel eventChannel = EventChannel('BLE-Platform-Bridge-Event'); + String receivedData = ''; + + @override + void initState() { + // TODO: implement initState + super.initState(); + eventChannel.receiveBroadcastStream().listen((event) { + // Handle the received event here. + print('Received event---: $event'); + + setState(() { + receivedData = event; + }); + // You can update your UI or perform any other actions based on the event. + }); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + body: SafeArea( + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Center( + child: GestureDetector( + onTap: () async { + // await BleChannel.getScanningResult("oximeter"); + }, + child: Container( + width: 50, + height: 50, + color: Colors.blueAccent, + ), + ), + ), + Center(child: Text(receivedData)) + ], + ), + ), + ); + } +} diff --git a/lib/pages/landing/landing_page.dart b/lib/pages/landing/landing_page.dart index ebe6e67b..a949605d 100644 --- a/lib/pages/landing/landing_page.dart +++ b/lib/pages/landing/landing_page.dart @@ -19,6 +19,13 @@ import 'package:diplomaticquarterapp/pages/ToDoList/ToDo.dart'; import 'package:diplomaticquarterapp/pages/landing/home_page_2.dart'; import 'package:diplomaticquarterapp/pages/medical/medical_profile_page_new.dart'; import 'package:diplomaticquarterapp/pages/rateAppointment/rate_appointment_doctor.dart'; +import 'package:diplomaticquarterapp/pages/medical/my_trackers/ecg_ble.dart'; +import 'package:diplomaticquarterapp/pages/medical/my_trackers/spirometer.dart'; +import 'package:diplomaticquarterapp/pages/medical/my_trackers/viatom_devices/bp_tracker_ble.dart'; +import 'package:diplomaticquarterapp/pages/medical/my_trackers/viatom_devices/ekg_tracker_ble.dart'; +import 'package:diplomaticquarterapp/pages/medical/my_trackers/viatom_devices/oxymeter_ble.dart'; +import 'package:diplomaticquarterapp/pages/medical/my_trackers/viatom_devices/sp20_pulse_tracker.dart'; +import 'package:diplomaticquarterapp/pages/medical/my_trackers/weight_scale_ble.dart'; import 'package:diplomaticquarterapp/pages/videocall-webrtc-rnd/webrtc/start_video_call.dart'; import 'package:diplomaticquarterapp/services/authentication/auth_provider.dart'; import 'package:diplomaticquarterapp/services/clinic_services/get_clinic_service.dart'; @@ -619,6 +626,10 @@ class _LandingPageState extends State with WidgetsBindingObserver { }, onLoginClick: () { login(); + // Navigator.push(context, FadePage(page: BpTrackerBLE())); + // Navigator.push(context, FadePage(page: SP20PulseTracker())); + // Navigator.push(context, FadePage(page: EKG_BLE())); + // Navigator.push(context, FadePage(page: Oxymeter_BLE())); }, onMedicalFileClick: () { changeCurrentTab(1); diff --git a/lib/pages/medical/my_trackers/ble_device_type_screens/andesfit_device_types/andesfit_blood_pressure_connect_screen.dart b/lib/pages/medical/my_trackers/ble_device_type_screens/andesfit_device_types/andesfit_blood_pressure_connect_screen.dart new file mode 100644 index 00000000..a4b2d11b --- /dev/null +++ b/lib/pages/medical/my_trackers/ble_device_type_screens/andesfit_device_types/andesfit_blood_pressure_connect_screen.dart @@ -0,0 +1,175 @@ +import 'package:flutter_blue_plus/flutter_blue_plus.dart'; +import 'package:diplomaticquarterapp/pages/medical/my_trackers/my_trackers_view_model/my_trackers_view_model.dart'; +import 'package:diplomaticquarterapp/uitl/utils_new.dart'; +import 'package:diplomaticquarterapp/widgets/buttons/defaultButton.dart'; +import 'package:diplomaticquarterapp/widgets/others/app_scaffold_widget.dart'; +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; + +class AndesFitBloodPressureConnectScreen extends StatefulWidget { + final BluetoothDevice deviceModel; + final bool isEcgAvailable; + + const AndesFitBloodPressureConnectScreen({this.deviceModel, this.isEcgAvailable = false}); + + @override + State createState() => _AndesFitBloodPressureConnectScreenState(); +} + +class _AndesFitBloodPressureConnectScreenState extends State { + MyTrackersViewModel myTrackersVm; + + @override + void initState() { + myTrackersVm = context.read(); + myTrackersVm.connectAndesfitBloodPressureDevice(widget.deviceModel); + super.initState(); + } + + @override + void dispose() { + myTrackersVm.andesfitBpCurrentStatus = null; + myTrackersVm.andesfitBpRtMeasuringModel = null; + myTrackersVm.andesfitBpRtResultModel = null; + myTrackersVm.selectedAndesFitScanResult = null; + myTrackersVm.isAndesfitDeviceConnected = null; + myTrackersVm.disConnectAndesfitDevice(widget.deviceModel); + super.dispose(); + } + + Widget buildBloodPressureUI(MyTrackersViewModel myTrackersViewModel) { + return Expanded( + child: ListView( + children: [ + if (myTrackersViewModel.andesfitBpCurrentStatus == kRealTimeDataBPMeasuring) ...[ + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + mHeight(24.0), + Column( + children: [ + Text(myTrackersViewModel.andesfitBpRtMeasuringModel.pressure.toString(), style: TextStyle(fontSize: 100, fontWeight: FontWeight.bold)), + Text("Pressure", style: TextStyle(fontSize: 20)), + ], + ), + mHeight(24.0), + ], + ), + ] else if (myTrackersViewModel.andesfitBpCurrentStatus == kRealTimeDataBPResult) ...[ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + mHeight(24.0), + Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + Column( + children: [ + Text("Sys", style: TextStyle(fontSize: 20)), + Text(myTrackersViewModel.andesfitBpRtResultModel.sys.toString(), style: TextStyle(fontSize: 50, fontWeight: FontWeight.bold)), + Text("mmHg", style: TextStyle(fontSize: 10)), + ], + ), + Column( + children: [ + Text("Dia", style: TextStyle(fontSize: 20)), + Text(myTrackersViewModel.andesfitBpRtResultModel.dia.toString(), style: TextStyle(fontSize: 50, fontWeight: FontWeight.bold)), + Text("mmHg", style: TextStyle(fontSize: 10)), + ], + ), + Column( + children: [ + Text("♥︎", style: TextStyle(fontSize: 20)), + Text(myTrackersViewModel.andesfitBpRtResultModel.pr.toString(), style: TextStyle(fontSize: 50, fontWeight: FontWeight.bold)), + Text("/min", style: TextStyle(fontSize: 10)), + ], + ), + ], + ), + mHeight(30.0), + // buildStatusForBP(myTrackersViewModel.bpRtResultModel.result), + // mHeight(24.0), + ], + ), + ] else ...[ + Padding( + padding: const EdgeInsets.all(24.0), + child: Center( + child: Text( + "Some animation with the instruction", + style: TextStyle(fontSize: 9.0), + ), + ), + ) + ], + ], + ), + ); + } + + Widget buildStatusForBP(int result) { + String resultStatus = ""; + + if (result == 0) { + resultStatus = "Normal"; + } else if (result == 1) { + resultStatus = "Unable to analyze(cuff is too loose, inflation is slow, slow air leakage, large air volume)"; + } else if (result == 2) { + resultStatus = "Waveform disorder(arm movement or other interference detected during pumping)"; + } else if (result == 3) { + resultStatus = "Weak signal, unable to detect pulse wave(clothes with interference sleeves)"; + } else { + resultStatus = "Equipment error(valve blocking, over-range blood pressure measurement, serious cuff leakage, software system abnormality, hardware system error, and other abnormalities)"; + } + return Column( + children: [ + Center(child: Text("$resultStatus", style: TextStyle(fontSize: 20))), + if (result != 0) ...[ + mHeight(24.0), + Center(child: Text("Press START/STOP Button on the device to restart.", style: TextStyle(fontSize: 15))), + ], + ], + ); + } + + @override + Widget build(BuildContext context) { + return AppScaffold( + appBarTitle: "${widget.deviceModel.localName}", + showNewAppBar: true, + isShowDecPage: false, + showNewAppBarTitle: true, + backgroundColor: Color(0xffF8F8F8), + body: Padding( + padding: const EdgeInsets.all(24.0), + child: Consumer( + builder: (BuildContext context, MyTrackersViewModel myTrackersViewModel, Widget child) { + return Column( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + buildBloodPressureUI(myTrackersViewModel), + if (myTrackersViewModel.isAndesfitDeviceConnected != null && myTrackersViewModel.isAndesfitDeviceConnected) ...[ + Row( + children: [ + Expanded( + child: DefaultButton( + "Disconnect with ${widget.deviceModel.localName}", + () async { + myTrackersVm.disConnectAndesfitDevice(widget.deviceModel); + Navigator.pop(context); + }, + textColor: Colors.white, + ), + ), + ], + ), + ] + ], + ); + }, + ), + ), + ); + } +} diff --git a/lib/pages/medical/my_trackers/ble_device_type_screens/andesfit_device_types/andesfit_blood_sugar_connect_screen.dart b/lib/pages/medical/my_trackers/ble_device_type_screens/andesfit_device_types/andesfit_blood_sugar_connect_screen.dart new file mode 100644 index 00000000..6051b754 --- /dev/null +++ b/lib/pages/medical/my_trackers/ble_device_type_screens/andesfit_device_types/andesfit_blood_sugar_connect_screen.dart @@ -0,0 +1,131 @@ +import 'package:diplomaticquarterapp/extensions/string_extensions.dart'; +import 'package:flutter_blue_plus/flutter_blue_plus.dart'; +import 'package:diplomaticquarterapp/pages/medical/my_trackers/my_trackers_view_model/my_trackers_view_model.dart'; +import 'package:diplomaticquarterapp/uitl/utils_new.dart'; +import 'package:diplomaticquarterapp/widgets/buttons/defaultButton.dart'; +import 'package:diplomaticquarterapp/widgets/others/app_scaffold_widget.dart'; +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; + +class AndesFitBloodSugarConnectScreen extends StatefulWidget { + final BluetoothDevice deviceModel; + + const AndesFitBloodSugarConnectScreen({this.deviceModel}); + + @override + State createState() => _AndesFitBloodSugarConnectScreenState(); +} + +class _AndesFitBloodSugarConnectScreenState extends State { + MyTrackersViewModel myTrackersVm; + + @override + void initState() { + myTrackersVm = context.read(); + myTrackersVm.connectAndesfitBloodSugarDevice(widget.deviceModel); + super.initState(); + } + + @override + void dispose() { + myTrackersVm.bloodSugarValuesStream.cancel(); + myTrackersVm.currentBloodGlucose = null; + myTrackersVm.selectedAndesFitScanResult = null; + myTrackersVm.isAndesfitDeviceConnected = null; + myTrackersVm.disConnectAndesfitDevice(widget.deviceModel); + super.dispose(); + } + + Widget buildBloodSugarUI(MyTrackersViewModel myTrackersViewModel) { + return Expanded( + child: ListView( + children: [ + if (myTrackersViewModel.currentBloodGlucose == null && myTrackersViewModel.isAndesfitDeviceConnected) ...[ + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + mHeight(24.0), + Column( + children: [ + Text("Please Wait", style: TextStyle(fontSize: 20)), + ], + ), + mHeight(24.0), + ], + ), + ] else if (myTrackersViewModel.currentBloodGlucose != null) ...[ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + mHeight(24.0), + Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + Column( + children: [ + Text("Blood Sugar", style: TextStyle(fontSize: 20)), + Text(myTrackersViewModel.currentBloodGlucose.toString(), style: TextStyle(fontSize: 40, fontWeight: FontWeight.bold)), + Text("mg/dL", style: TextStyle(fontSize: 15)), + ], + ), + ], + ), + ], + ), + ] else ...[ + Padding( + padding: const EdgeInsets.all(24.0), + child: Center( + child: Text( + "Some animation with the instruction", + style: TextStyle(fontSize: 9.0), + ), + ), + ) + ], + ], + ), + ); + } + + @override + Widget build(BuildContext context) { + return AppScaffold( + appBarTitle: "${widget.deviceModel.localName}", + showNewAppBar: true, + isShowDecPage: false, + showNewAppBarTitle: true, + backgroundColor: Color(0xffF8F8F8), + body: Padding( + padding: const EdgeInsets.all(24.0), + child: Consumer( + builder: (BuildContext context, MyTrackersViewModel myTrackersViewModel, Widget child) { + return Column( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + buildBloodSugarUI(myTrackersViewModel), + if (myTrackersViewModel.isAndesfitDeviceConnected != null && myTrackersViewModel.isAndesfitDeviceConnected) ...[ + Row( + children: [ + Expanded( + child: DefaultButton( + "Disconnect with ${widget.deviceModel.localName}", + () async { + myTrackersVm.disConnectAndesfitDevice(widget.deviceModel); + Navigator.pop(context); + }, + textColor: Colors.white, + ), + ), + ], + ), + ] + ], + ); + }, + ), + ), + ); + } +} diff --git a/lib/pages/medical/my_trackers/ble_device_type_screens/andesfit_device_types/andesfit_spirometer_connect_screen.dart b/lib/pages/medical/my_trackers/ble_device_type_screens/andesfit_device_types/andesfit_spirometer_connect_screen.dart new file mode 100644 index 00000000..30277e87 --- /dev/null +++ b/lib/pages/medical/my_trackers/ble_device_type_screens/andesfit_device_types/andesfit_spirometer_connect_screen.dart @@ -0,0 +1,138 @@ +import 'package:diplomaticquarterapp/extensions/string_extensions.dart'; +import 'package:flutter_blue_plus/flutter_blue_plus.dart'; +import 'package:diplomaticquarterapp/pages/medical/my_trackers/my_trackers_view_model/my_trackers_view_model.dart'; +import 'package:diplomaticquarterapp/uitl/utils_new.dart'; +import 'package:diplomaticquarterapp/widgets/buttons/defaultButton.dart'; +import 'package:diplomaticquarterapp/widgets/others/app_scaffold_widget.dart'; +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; + +class AndesFitSpirometerConnectScreen extends StatefulWidget { + final BluetoothDevice deviceModel; + + const AndesFitSpirometerConnectScreen({this.deviceModel}); + + @override + State createState() => _AndesFitSpirometerConnectScreenState(); +} + +class _AndesFitSpirometerConnectScreenState extends State { + MyTrackersViewModel myTrackersVm; + + @override + void initState() { + myTrackersVm = context.read(); + myTrackersVm.connectAndesfitSpirometerDevice(widget.deviceModel); + super.initState(); + } + + @override + void dispose() { + myTrackersVm.valuePEF = null; + myTrackersVm.valueFEV = null; + myTrackersVm.isAndesfitDeviceConnected = null; + myTrackersVm.timerForSpirometer.cancel(); + myTrackersVm.disConnectAndesfitDevice(widget.deviceModel); + super.dispose(); + } + + Widget buildSpirometerUI(MyTrackersViewModel myTrackersViewModel) { + return Expanded( + child: ListView( + children: [ + if ((myTrackersViewModel.valuePEF == null && myTrackersViewModel.valueFEV == null) && myTrackersViewModel.isAndesfitDeviceConnected) ...[ + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + mHeight(24.0), + Column( + children: [ + Text("Please blow gently", style: TextStyle(fontSize: 20)), + ], + ), + mHeight(24.0), + ], + ), + ] else if ((myTrackersViewModel.valuePEF != null && myTrackersViewModel.valueFEV != null)) ...[ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + mHeight(24.0), + Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + Column( + children: [ + Text("PEF", style: TextStyle(fontSize: 20)), + Text(myTrackersViewModel.valuePEF.toString(), style: TextStyle(fontSize: 40, fontWeight: FontWeight.bold)), + Text("L/min", style: TextStyle(fontSize: 15)), + ], + ), + Column( + children: [ + Text("FEV", style: TextStyle(fontSize: 20)), + Text(myTrackersViewModel.valueFEV.toString(), style: TextStyle(fontSize: 40, fontWeight: FontWeight.bold)), + Text("L", style: TextStyle(fontSize: 15)), + ], + ), + ], + ), + ], + ), + ] else ...[ + Padding( + padding: const EdgeInsets.all(24.0), + child: Center( + child: Text( + "Some animation with the instruction", + style: TextStyle(fontSize: 9.0), + ), + ), + ) + ], + ], + ), + ); + } + + @override + Widget build(BuildContext context) { + return AppScaffold( + appBarTitle: "${widget.deviceModel.localName}", + showNewAppBar: true, + isShowDecPage: false, + showNewAppBarTitle: true, + backgroundColor: Color(0xffF8F8F8), + body: Padding( + padding: const EdgeInsets.all(24.0), + child: Consumer( + builder: (BuildContext context, MyTrackersViewModel myTrackersViewModel, Widget child) { + return Column( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + buildSpirometerUI(myTrackersViewModel), + if (myTrackersViewModel.isAndesfitDeviceConnected != null && myTrackersViewModel.isAndesfitDeviceConnected) ...[ + Row( + children: [ + Expanded( + child: DefaultButton( + "Disconnect with ${widget.deviceModel.localName}", + () async { + myTrackersVm.disConnectAndesfitDevice(widget.deviceModel); + Navigator.pop(context); + }, + textColor: Colors.white, + ), + ), + ], + ), + ] + ], + ); + }, + ), + ), + ); + } +} diff --git a/lib/pages/medical/my_trackers/ble_device_type_screens/andesfit_device_types/andesfit_temperature_connect_screen.dart b/lib/pages/medical/my_trackers/ble_device_type_screens/andesfit_device_types/andesfit_temperature_connect_screen.dart new file mode 100644 index 00000000..57552fd0 --- /dev/null +++ b/lib/pages/medical/my_trackers/ble_device_type_screens/andesfit_device_types/andesfit_temperature_connect_screen.dart @@ -0,0 +1,138 @@ +import 'package:diplomaticquarterapp/extensions/string_extensions.dart'; +import 'package:flutter_blue_plus/flutter_blue_plus.dart'; +import 'package:diplomaticquarterapp/pages/medical/my_trackers/my_trackers_view_model/my_trackers_view_model.dart'; +import 'package:diplomaticquarterapp/uitl/utils_new.dart'; +import 'package:diplomaticquarterapp/widgets/buttons/defaultButton.dart'; +import 'package:diplomaticquarterapp/widgets/others/app_scaffold_widget.dart'; +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; + +class AndesFitTemperatureConnectScreen extends StatefulWidget { + final BluetoothDevice deviceModel; + + const AndesFitTemperatureConnectScreen({this.deviceModel}); + + @override + State createState() => _AndesFitTemperatureConnectScreenState(); +} + +class _AndesFitTemperatureConnectScreenState extends State { + MyTrackersViewModel myTrackersVm; + + @override + void initState() { + myTrackersVm = context.read(); + myTrackersVm.connectAndesfitTemperatureDevice(widget.deviceModel); + super.initState(); + } + + @override + void dispose() { + myTrackersVm.currentTempInCelsius = null; + myTrackersVm.selectedAndesFitScanResult = null; + myTrackersVm.isAndesfitDeviceConnected = null; + myTrackersVm.disConnectAndesfitDevice(widget.deviceModel); + super.dispose(); + } + + Widget buildTemperatureScaleUI(MyTrackersViewModel myTrackersViewModel) { + return Expanded( + child: ListView( + children: [ + if (myTrackersViewModel.currentTempInCelsius == null && (myTrackersViewModel.isAndesfitDeviceConnected != null && myTrackersViewModel.isAndesfitDeviceConnected)) ...[ + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + mHeight(24.0), + Column( + children: [ + // Text("Please stay Still.", style: TextStyle(fontSize: 100, fontWeight: FontWeight.bold)), + Text("Please Wait", style: TextStyle(fontSize: 20)), + ], + ), + mHeight(24.0), + ], + ), + ] else if (myTrackersViewModel.currentTempInCelsius != null) ...[ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + mHeight(24.0), + Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + Column( + children: [ + // Text("Temperature", style: TextStyle(fontSize: 20)), + Text((myTrackersViewModel.currentTempInCelsius.split(" / ")[0]).toString(), style: TextStyle(fontSize: 40, fontWeight: FontWeight.bold)), + Text("Celsius", style: TextStyle(fontSize: 15)), + ], + ), + Column( + children: [ + // Text("Temperature", style: TextStyle(fontSize: 20)), + Text((myTrackersViewModel.currentTempInCelsius.split(" / ")[1]).toString(), style: TextStyle(fontSize: 40, fontWeight: FontWeight.bold)), + Text("Fahrenheit", style: TextStyle(fontSize: 15)), + ], + ), + ], + ), + ], + ), + ] else ...[ + Padding( + padding: const EdgeInsets.all(24.0), + child: Center( + child: Text( + "Some animation with the instruction", + style: TextStyle(fontSize: 9.0), + ), + ), + ) + ], + ], + ), + ); + } + + @override + Widget build(BuildContext context) { + return AppScaffold( + appBarTitle: "${widget.deviceModel.localName}", + showNewAppBar: true, + isShowDecPage: false, + showNewAppBarTitle: true, + backgroundColor: Color(0xffF8F8F8), + body: Padding( + padding: const EdgeInsets.all(24.0), + child: Consumer( + builder: (BuildContext context, MyTrackersViewModel myTrackersViewModel, Widget child) { + return Column( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + buildTemperatureScaleUI(myTrackersViewModel), + if (myTrackersViewModel.isAndesfitDeviceConnected != null && myTrackersViewModel.isAndesfitDeviceConnected) ...[ + Row( + children: [ + Expanded( + child: DefaultButton( + "Disconnect with ${widget.deviceModel.localName}", + () async { + myTrackersVm.disConnectAndesfitDevice(widget.deviceModel); + Navigator.pop(context); + }, + textColor: Colors.white, + ), + ), + ], + ), + ] + ], + ); + }, + ), + ), + ); + } +} diff --git a/lib/pages/medical/my_trackers/ble_device_type_screens/andesfit_device_types/andesfit_weigth_scale_connect_screen.dart b/lib/pages/medical/my_trackers/ble_device_type_screens/andesfit_device_types/andesfit_weigth_scale_connect_screen.dart new file mode 100644 index 00000000..8e0f9226 --- /dev/null +++ b/lib/pages/medical/my_trackers/ble_device_type_screens/andesfit_device_types/andesfit_weigth_scale_connect_screen.dart @@ -0,0 +1,174 @@ +import 'package:diplomaticquarterapp/extensions/string_extensions.dart'; +import 'package:flutter_blue_plus/flutter_blue_plus.dart'; +import 'package:diplomaticquarterapp/pages/medical/my_trackers/my_trackers_view_model/my_trackers_view_model.dart'; +import 'package:diplomaticquarterapp/uitl/utils_new.dart'; +import 'package:diplomaticquarterapp/widgets/buttons/defaultButton.dart'; +import 'package:diplomaticquarterapp/widgets/others/app_scaffold_widget.dart'; +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; + +class AndesFitWeightScaleConnectScreen extends StatefulWidget { + final BluetoothDevice deviceModel; + + const AndesFitWeightScaleConnectScreen({this.deviceModel}); + + @override + State createState() => _AndesFitWeightScaleConnectScreenState(); +} + +class _AndesFitWeightScaleConnectScreenState extends State { + MyTrackersViewModel myTrackersVm; + + @override + void initState() { + myTrackersVm = context.read(); + myTrackersVm.connectAndesfitWeightDevice(widget.deviceModel); + super.initState(); + } + + @override + void dispose() { + myTrackersVm.andesfitWeightScaleData = null; + myTrackersVm.selectedAndesFitScanResult = null; + myTrackersVm.isAndesfitDeviceConnected = null; + myTrackersVm.disConnectAndesfitDevice(widget.deviceModel); + super.dispose(); + } + + Widget buildWeightScaleUI(MyTrackersViewModel myTrackersViewModel) { + return Expanded( + child: ListView( + children: [ + if (myTrackersViewModel.andesfitWeightScaleData == null && myTrackersViewModel.isAndesfitDeviceConnected != null && myTrackersViewModel.isAndesfitDeviceConnected) ...[ + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + mHeight(24.0), + Column( + children: [ + // Text("Please stay Still.", style: TextStyle(fontSize: 100, fontWeight: FontWeight.bold)), + Text("Please stay Still.", style: TextStyle(fontSize: 20)), + ], + ), + mHeight(24.0), + ], + ), + ] else if (myTrackersViewModel.andesfitWeightScaleData != null) ...[ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + mHeight(24.0), + Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + Column( + children: [ + Text("Weight", style: TextStyle(fontSize: 20)), + Text(myTrackersViewModel.andesfitWeightScaleData.weight.toString(), style: TextStyle(fontSize: 40, fontWeight: FontWeight.bold)), + Text("KG", style: TextStyle(fontSize: 15)), + ], + ), + Column( + children: [ + Text("BMI", style: TextStyle(fontSize: 20)), + Text(myTrackersViewModel.andesfitWeightScaleData.bmi.toString(), style: TextStyle(fontSize: 40, fontWeight: FontWeight.bold)), + Text("", style: TextStyle(fontSize: 15)), + ], + ), + Column( + children: [ + Text("Muscle", style: TextStyle(fontSize: 20)), + Text(myTrackersViewModel.andesfitWeightScaleData.muscle.toString(), style: TextStyle(fontSize: 40, fontWeight: FontWeight.bold)), + Text("%", style: TextStyle(fontSize: 15)), + ], + ), + ], + ), + mHeight(30.0), + Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + Column( + children: [ + Text("Fat", style: TextStyle(fontSize: 20)), + Text(myTrackersViewModel.andesfitWeightScaleData.fat.toString(), style: TextStyle(fontSize: 40, fontWeight: FontWeight.bold)), + Text("%", style: TextStyle(fontSize: 15)), + ], + ), + Column( + children: [ + Text("BMR", style: TextStyle(fontSize: 20)), + Text(myTrackersViewModel.andesfitWeightScaleData.bmr.toString(), style: TextStyle(fontSize: 40, fontWeight: FontWeight.bold)), + Text("calories/day", style: TextStyle(fontSize: 15)), + ], + ), + Column( + children: [ + Text("Water", style: TextStyle(fontSize: 20)), + Text(myTrackersViewModel.andesfitWeightScaleData.water.toString(), style: TextStyle(fontSize: 40, fontWeight: FontWeight.bold)), + Text("%", style: TextStyle(fontSize: 15)), + ], + ), + ], + ), + // buildStatusForBP(myTrackersViewModel.bpRtResultModel.result), + // mHeight(24.0), + ], + ), + ] else ...[ + Padding( + padding: const EdgeInsets.all(24.0), + child: Center( + child: Text( + "Some animation with the instruction", + style: TextStyle(fontSize: 9.0), + ), + ), + ) + ], + ], + ), + ); + } + + @override + Widget build(BuildContext context) { + return AppScaffold( + appBarTitle: "${widget.deviceModel.localName}", + showNewAppBar: true, + isShowDecPage: false, + showNewAppBarTitle: true, + backgroundColor: Color(0xffF8F8F8), + body: Padding( + padding: const EdgeInsets.all(24.0), + child: Consumer( + builder: (BuildContext context, MyTrackersViewModel myTrackersViewModel, Widget child) { + return Column( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + buildWeightScaleUI(myTrackersViewModel), + if (myTrackersViewModel.isAndesfitDeviceConnected != null && myTrackersViewModel.isAndesfitDeviceConnected) ...[ + Row( + children: [ + Expanded( + child: DefaultButton( + "Disconnect with ${widget.deviceModel.localName}", + () async { + myTrackersVm.disConnectAndesfitDevice(widget.deviceModel); + Navigator.pop(context); + }, + textColor: Colors.white, + ), + ), + ], + ), + ] + ], + ); + }, + ), + ), + ); + } +} diff --git a/lib/pages/medical/my_trackers/ble_device_type_screens/ble_devices_screen.dart b/lib/pages/medical/my_trackers/ble_device_type_screens/ble_devices_screen.dart new file mode 100644 index 00000000..07bd5419 --- /dev/null +++ b/lib/pages/medical/my_trackers/ble_device_type_screens/ble_devices_screen.dart @@ -0,0 +1,196 @@ +import 'dart:developer'; + +import 'package:diplomaticquarterapp/pages/medical/my_trackers/ble_device_type_screens/checkme_all_in-one_connect_screen.dart'; +import 'package:diplomaticquarterapp/pages/medical/my_trackers/ble_device_type_screens/andesfit_device_types/andesfit_blood_pressure_connect_screen.dart'; +import 'package:diplomaticquarterapp/pages/medical/my_trackers/ble_device_type_screens/andesfit_device_types/andesfit_blood_sugar_connect_screen.dart'; +import 'package:diplomaticquarterapp/pages/medical/my_trackers/ble_device_type_screens/andesfit_device_types/andesfit_spirometer_connect_screen.dart'; +import 'package:diplomaticquarterapp/pages/medical/my_trackers/ble_device_type_screens/andesfit_device_types/andesfit_temperature_connect_screen.dart'; +import 'package:diplomaticquarterapp/pages/medical/my_trackers/ble_device_type_screens/andesfit_device_types/andesfit_weigth_scale_connect_screen.dart'; +import 'package:diplomaticquarterapp/pages/medical/my_trackers/ble_device_type_screens/lepu_device_types/bloodpressure_connect_screen.dart'; +import 'package:diplomaticquarterapp/pages/medical/my_trackers/ble_device_type_screens/lepu_device_types/ecg_connect_screen.dart'; +import 'package:diplomaticquarterapp/pages/medical/my_trackers/ble_device_type_screens/lepu_device_types/oxymeter_connect_screen.dart'; +import 'package:diplomaticquarterapp/pages/medical/my_trackers/ble_device_type_screens/smart_ring_connect_screen.dart'; +import 'package:diplomaticquarterapp/pages/medical/my_trackers/ble_models/ble_devices_model.dart'; +import 'package:diplomaticquarterapp/pages/medical/my_trackers/my_trackers_view_model/my_trackers_view_model.dart'; +import 'package:diplomaticquarterapp/widgets/data_display/medical/medical_profile_item.dart'; +import 'package:diplomaticquarterapp/widgets/others/app_scaffold_widget.dart'; +import 'package:diplomaticquarterapp/widgets/transitions/fade_page.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_blue_plus/flutter_blue_plus.dart'; +import 'package:provider/provider.dart'; + +class BleDevicesScreen extends StatefulWidget { + @override + State createState() => _BleDevicesScreenState(); +} + +class _BleDevicesScreenState extends State { + MyTrackersViewModel myTrackersVm; + + @override + void dispose() { + FlutterBluePlus.stopScan(); + myTrackersVm.stopNativeScan(); + myTrackersVm.resetList(); + super.dispose(); + } + + @override + void initState() { + myTrackersVm = context.read(); + if (myTrackersVm.currentSelectedTrackerType == TrackerTypeEnum.AllInOneTracker) { + myTrackersVm.startSearchingForCheckMePro(); + } + if (myTrackersVm.currentSelectedTrackerType == TrackerTypeEnum.SmartRing) { + myTrackersVm.startSearchingForSmartRing(); + } else { + myTrackersVm.startSearchingForTracker(); + myTrackersVm.startTimerForNativeScan(); + } + super.initState(); + } + + bool getIfECGAvailableInBPDevice(String name) { + int index = myTrackersVm.ecgEnabledDevices.indexWhere((deviceName) => deviceName == name); + return index != -1; + } + + void onDeviceTapped(BleDeviceModel device) { + myTrackersVm.isDeviceSelected = true; + log("isDeviceSelected from Screen: ${myTrackersVm.isDeviceSelected}"); + FlutterBluePlus.stopScan(); + switch (device.deviceType) { + case TrackerTypeEnum.OxymeterTracker: + if (myTrackersVm.isDeviceFromAndesFit(device.name)) { + return; + } + myTrackersVm.oxyRtModel = null; + Navigator.pushReplacement(context, FadePage(page: OxymeterConnectScreen(deviceModel: device))); + break; + case TrackerTypeEnum.BloodPressureTracker: + myTrackersVm.andesfitBpCurrentStatus = null; + myTrackersVm.andesfitBpRtMeasuringModel = null; + myTrackersVm.andesfitBpRtResultModel = null; + myTrackersVm.selectedAndesFitScanResult = null; + myTrackersVm.isAndesfitDeviceConnected = null; + myTrackersVm.bleDevicesStream.cancel(); + if (myTrackersVm.isDeviceFromAndesFit(device.name)) { + Navigator.pushReplacement(context, FadePage(page: AndesFitBloodPressureConnectScreen(deviceModel: device.andesfitBluetoothDevice, isEcgAvailable: false))); + return; + } + + Navigator.pushReplacement(context, FadePage(page: BloodPressureConnectScreen(deviceModel: device, isEcgAvailable: getIfECGAvailableInBPDevice(device.name)))); + break; + case TrackerTypeEnum.BloodSugarTracker: + if (myTrackersVm.isDeviceFromAndesFit(device.name)) { + myTrackersVm.currentBloodGlucose = null; + Navigator.pushReplacement(context, FadePage(page: AndesFitBloodSugarConnectScreen(deviceModel: device.andesfitBluetoothDevice))); + return; + } + break; + case TrackerTypeEnum.ECGTracker: + if (myTrackersVm.isDeviceFromAndesFit(device.name)) { + return; + } + myTrackersVm.ecgRtModel = null; + Navigator.pushReplacement(context, FadePage(page: ECGConnectScreen(deviceModel: device))); + break; + + case TrackerTypeEnum.WeightScale: + if (myTrackersVm.isDeviceFromAndesFit(device.name)) { + myTrackersVm.andesfitWeightScaleData = null; + Navigator.pushReplacement(context, FadePage(page: AndesFitWeightScaleConnectScreen(deviceModel: device.andesfitBluetoothDevice))); + return; + } + break; + + case TrackerTypeEnum.Temperature: + if (myTrackersVm.isDeviceFromAndesFit(device.name)) { + myTrackersVm.currentTempInCelsius = null; + Navigator.pushReplacement(context, FadePage(page: AndesFitTemperatureConnectScreen(deviceModel: device.andesfitBluetoothDevice))); + return; + } + break; + + case TrackerTypeEnum.Spirometer: + if (myTrackersVm.isDeviceFromAndesFit(device.name)) { + myTrackersVm.currentTempInCelsius = null; + Navigator.pushReplacement(context, FadePage(page: AndesFitSpirometerConnectScreen(deviceModel: device.andesfitBluetoothDevice))); + return; + } + break; + + case TrackerTypeEnum.SmartRing: + if (myTrackersVm.isDeviceFromAndesFit(device.name)) { + return; + } + Navigator.pushReplacement(context, FadePage(page: SmartRingAllInOneConnectScreen(deviceModel: device))); + break; + + case TrackerTypeEnum.AllInOneTracker: + if (myTrackersVm.isDeviceFromAndesFit(device.name)) { + return; + } + Navigator.pushReplacement(context, FadePage(page: CheckMeAllInOneConnectScreen(deviceModel: device))); + break; + } + } + + @override + Widget build(BuildContext context) { + return AppScaffold( + appBarTitle: "BLE Tracker", + showNewAppBar: true, + isShowDecPage: false, + showNewAppBarTitle: true, + backgroundColor: Color(0xffF8F8F8), + body: Container( + child: SingleChildScrollView( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Consumer( + builder: (BuildContext context, MyTrackersViewModel myTrackerVm, Widget child) { + if (myTrackerVm.devicesList.isEmpty) { + return Padding( + padding: const EdgeInsets.all(24.0), + child: Center( + child: Text( + "Searching for Devices Animation", + style: TextStyle(fontSize: 9.0), + ), + ), + ); + } + return Padding( + padding: EdgeInsets.only(left: 12, right: 12, top: 20), + child: GridView.builder( + shrinkWrap: true, + primary: false, + physics: NeverScrollableScrollPhysics(), + gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 3, childAspectRatio: 2 / 2, crossAxisSpacing: 12, mainAxisSpacing: 12), + padding: EdgeInsets.zero, + itemCount: myTrackerVm.devicesList.length, + itemBuilder: (BuildContext context, int index) { + BleDeviceModel device = myTrackerVm.devicesList[index]; + return InkWell( + onTap: () => onDeviceTapped(device), + child: MedicalProfileItem( + title: "${device.name}", + imagePath: 'tracker.svg', + subTitle: "", + isEnable: true, + ), + ); + }, + ), + ); + }, + ), + ], + ), + ), + ), + ); + } +} diff --git a/lib/pages/medical/my_trackers/ble_device_type_screens/checkme_all_in-one_connect_screen.dart b/lib/pages/medical/my_trackers/ble_device_type_screens/checkme_all_in-one_connect_screen.dart new file mode 100644 index 00000000..56b32ada --- /dev/null +++ b/lib/pages/medical/my_trackers/ble_device_type_screens/checkme_all_in-one_connect_screen.dart @@ -0,0 +1,179 @@ +import 'package:diplomaticquarterapp/pages/medical/my_trackers/ble_device_type_screens/checkme_all_in_one_info_screen.dart'; +import 'package:diplomaticquarterapp/pages/medical/my_trackers/ble_models/ble_devices_model.dart'; +import 'package:diplomaticquarterapp/pages/medical/my_trackers/my_trackers_view_model/my_trackers_view_model.dart'; +import 'package:diplomaticquarterapp/widgets/transitions/fade_page.dart'; +import 'package:diplomaticquarterapp/widgets/buttons/defaultButton.dart'; +import 'package:diplomaticquarterapp/widgets/data_display/medical/medical_profile_item.dart'; +import 'package:diplomaticquarterapp/widgets/others/app_scaffold_widget.dart'; +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; + +class CheckMeAllInOneConnectScreen extends StatefulWidget { + final BleDeviceModel deviceModel; + + const CheckMeAllInOneConnectScreen({this.deviceModel}); + + @override + State createState() => _CheckMeAllInOneConnectScreenState(); +} + +class _CheckMeAllInOneConnectScreenState extends State { + MyTrackersViewModel myTrackersVm; + + @override + void initState() { + myTrackersVm = context.read(); + myTrackersVm.connectDevice(widget.deviceModel); + super.initState(); + } + + @override + void dispose() { + myTrackersVm.disConnectDevice(); + super.dispose(); + } + + String getTrackerNameByEnum(TrackerTypeEnum trackerTypeEnum) { + switch (trackerTypeEnum) { + case TrackerTypeEnum.OxymeterTracker: + return "Oxymeter"; + + case TrackerTypeEnum.BloodPressureTracker: + return "Blood Pressure"; + + case TrackerTypeEnum.BloodSugarTracker: + return "Blood Glucose"; + + case TrackerTypeEnum.ECGTracker: + return "ECG"; + + case TrackerTypeEnum.WeightScale: + return "Weight Scale"; + + case TrackerTypeEnum.Temperature: + return "Temperature"; + + case TrackerTypeEnum.Spirometer: + return "Spirometer"; + + case TrackerTypeEnum.SmartRing: + return "Smart Ring"; + + case TrackerTypeEnum.AllInOneTracker: + return "All in One"; + } + return "All in One"; + } + + Future onTrackerTypePressed(TrackerTypeEnum trackerTypeEnum, MyTrackersViewModel myTrackersViewModel) async { + switch (trackerTypeEnum) { + case TrackerTypeEnum.BloodPressureTracker: + case TrackerTypeEnum.BloodSugarTracker: + myTrackersViewModel.getCheckMeUsersListDialog(trackerTypeEnum, context); + break; + case TrackerTypeEnum.OxymeterTracker: + myTrackersViewModel.getOxiDataFromCheckMePro(); + Navigator.of(context).push(FadePage(page: CheckMeAllInOneInfoScreen(trackerTypeEnum))); + break; + case TrackerTypeEnum.ECGTracker: + myTrackersViewModel.getECGDataFromCheckMePro(); + Navigator.of(context).push(FadePage(page: CheckMeAllInOneInfoScreen(trackerTypeEnum))); + break; + case TrackerTypeEnum.Temperature: + myTrackersViewModel.getTempDataFromCheckMePro(); + Navigator.of(context).push(FadePage(page: CheckMeAllInOneInfoScreen(trackerTypeEnum))); + break; + case TrackerTypeEnum.Spirometer: + case TrackerTypeEnum.WeightScale: + case TrackerTypeEnum.SmartRing: + case TrackerTypeEnum.AllInOneTracker: + break; + } + } + + Widget buildAllInOneUI(MyTrackersViewModel myTrackersViewModel) { + return Expanded( + child: ListView( + children: [ + if (myTrackersViewModel.checkMeProUsersList == null) ...[ + Padding( + padding: const EdgeInsets.all(24.0), + child: Center( + child: Text( + "Some animation with the instruction", + style: TextStyle(fontSize: 9.0), + ), + ), + ) + ] else ...[ + Center( + child: Column( + children: [ + GridView.builder( + shrinkWrap: true, + primary: false, + physics: NeverScrollableScrollPhysics(), + gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 3, childAspectRatio: 2 / 2, crossAxisSpacing: 12, mainAxisSpacing: 12), + padding: EdgeInsets.zero, + itemCount: myTrackersViewModel.checkMeProTrackers.length, + itemBuilder: (BuildContext context, int index) { + return InkWell( + onTap: () => onTrackerTypePressed(myTrackersViewModel.checkMeProTrackers[index], myTrackersViewModel), + child: MedicalProfileItem( + title: getTrackerNameByEnum(myTrackersViewModel.checkMeProTrackers[index]), + imagePath: 'tracker.svg', + subTitle: "", + isEnable: true, + ), + ); + }, + ), + ], + )) + ] + ], + ), + ); + } + + @override + Widget build(BuildContext context) { + return AppScaffold( + appBarTitle: "${widget.deviceModel.name}", + showNewAppBar: true, + isShowDecPage: false, + showNewAppBarTitle: true, + backgroundColor: Color(0xffF8F8F8), + body: Padding( + padding: const EdgeInsets.all(24.0), + child: Consumer( + builder: (BuildContext context, MyTrackersViewModel myTrackersViewModel, Widget child) { + return Column( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + buildAllInOneUI(myTrackersViewModel), + if (myTrackersViewModel.checkMeProUsersList != null) ...[ + Row( + children: [ + Expanded( + child: DefaultButton( + "Disconnect with ${widget.deviceModel.name}", + () async { + myTrackersVm.disConnectDevice(); + Navigator.pop(context); + }, + textColor: Colors.white, + ), + ), + ], + ), + ] + ], + ); + }, + ), + ), + ); + } +} diff --git a/lib/pages/medical/my_trackers/ble_device_type_screens/checkme_all_in_one_info_screen.dart b/lib/pages/medical/my_trackers/ble_device_type_screens/checkme_all_in_one_info_screen.dart new file mode 100644 index 00000000..53b220d0 --- /dev/null +++ b/lib/pages/medical/my_trackers/ble_device_type_screens/checkme_all_in_one_info_screen.dart @@ -0,0 +1,304 @@ +import 'package:diplomaticquarterapp/config/size_config.dart'; +import 'package:diplomaticquarterapp/pages/medical/my_trackers/ble_device_type_screens/checkme_ecg_info_screen.dart'; +import 'package:diplomaticquarterapp/pages/medical/my_trackers/ble_models/checkmepro_all_in_one_models/checkme_bp_model.dart'; +import 'package:diplomaticquarterapp/pages/medical/my_trackers/ble_models/checkmepro_all_in_one_models/checkme_ecg_model.dart'; +import 'package:diplomaticquarterapp/pages/medical/my_trackers/ble_models/checkmepro_all_in_one_models/checkme_glucose_model.dart'; +import 'package:diplomaticquarterapp/pages/medical/my_trackers/ble_models/checkmepro_all_in_one_models/checkme_oxi_model.dart'; +import 'package:diplomaticquarterapp/pages/medical/my_trackers/ble_models/checkmepro_all_in_one_models/checkme_temperature_model.dart'; +import 'package:diplomaticquarterapp/pages/medical/my_trackers/my_trackers_view_model/my_trackers_view_model.dart'; +import 'package:diplomaticquarterapp/uitl/utils_new.dart'; +import 'package:diplomaticquarterapp/widgets/others/app_scaffold_widget.dart'; +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; + +class CheckMeAllInOneInfoScreen extends StatelessWidget { + final TrackerTypeEnum allInOneTrackerTypeEnum; + + const CheckMeAllInOneInfoScreen(this.allInOneTrackerTypeEnum); + + String getTrackerNameByEnum(TrackerTypeEnum trackerTypeEnum) { + switch (trackerTypeEnum) { + case TrackerTypeEnum.OxymeterTracker: + return "Oxymeter"; + + case TrackerTypeEnum.BloodPressureTracker: + return "Blood Pressure"; + + case TrackerTypeEnum.BloodSugarTracker: + return "Blood Glucose"; + + case TrackerTypeEnum.ECGTracker: + return "ECG"; + + case TrackerTypeEnum.WeightScale: + return "Weight Scale"; + + case TrackerTypeEnum.Temperature: + return "Temperature"; + + case TrackerTypeEnum.Spirometer: + return "Spirometer"; + + case TrackerTypeEnum.AllInOneTracker: + return "All in One"; + case TrackerTypeEnum.SmartRing: + return "Smart Ring"; + } + return "All in One"; + } + + Widget getAllInOneOperationWidgets(MyTrackersViewModel myTrackersVm, TrackerTypeEnum trackerTypeEnum, BuildContext context) { + switch (trackerTypeEnum) { + case TrackerTypeEnum.OxymeterTracker: + return buildOxiWidget(myTrackersVm, context); + + case TrackerTypeEnum.BloodPressureTracker: + return buildBPWidget(myTrackersVm, context); + + case TrackerTypeEnum.BloodSugarTracker: + return buildGlucoseWidget(myTrackersVm, context); + + case TrackerTypeEnum.ECGTracker: + return buildECGWidget(myTrackersVm, context); + + case TrackerTypeEnum.Temperature: + return buildTemperatureWidget(myTrackersVm, context); + + case TrackerTypeEnum.Spirometer: + case TrackerTypeEnum.WeightScale: + case TrackerTypeEnum.AllInOneTracker: + case TrackerTypeEnum.SmartRing: + return getNoDataWidget(context); + break; + } + return getNoDataWidget(context); + } + + Widget buildTemperatureWidget(MyTrackersViewModel myTrackersVm, BuildContext context) { + return Column( + mainAxisSize: MainAxisSize.max, + children: [ + if (myTrackersVm.filesLoader) ...[ + Center(child: CircularProgressIndicator()) + ] else if (myTrackersVm.checkMeTemperatures != null && myTrackersVm.checkMeTemperatures.isNotEmpty) ...[ + Material( + child: ListView.separated( + itemCount: myTrackersVm.checkMeTemperatures.length, + separatorBuilder: (context, index) => SizedBox(height: 14), + shrinkWrap: true, + reverse: false, + itemBuilder: (context, index) { + CheckMeTemperatureModel checkMeTemp = myTrackersVm.checkMeTemperatures[index]; + return InkWell( + onTap: () => Navigator.pop(context), + child: Container( + decoration: cardRadius(12), + padding: EdgeInsets.all(12.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text("Temperature: ${checkMeTemp.tmp}"), + mHeight(4.0), + Text("Date: ${checkMeTemp.date}"), + ], + ), + ), + ); + }, + ), + ) + ] else ...[ + getNoDataWidget(context), + ] + ], + ); + } + + Widget buildECGWidget(MyTrackersViewModel myTrackersVm, BuildContext context) { + return Column( + mainAxisSize: MainAxisSize.max, + children: [ + if (myTrackersVm.filesLoader) ...[ + Center(child: CircularProgressIndicator()) + ] else if (myTrackersVm.checkMeEcg != null && myTrackersVm.checkMeEcg.isNotEmpty) ...[ + Material( + child: ListView.separated( + itemCount: myTrackersVm.checkMeEcg.length, + separatorBuilder: (context, index) => SizedBox(height: 14), + shrinkWrap: true, + reverse: false, + itemBuilder: (context, index) { + CheckMeECGModel checkMeECGModel = myTrackersVm.checkMeEcg[index]; + return InkWell( + onTap: () { + myTrackersVm.getEcgWaveDataFromCheckMePro(checkMeECGModel.timeString); + Navigator.push(context, MaterialPageRoute(builder: (context) => CheckMeECGInfoScreen(TrackerTypeEnum.ECGTracker))); + }, + child: Container( + decoration: cardRadius(12), + padding: EdgeInsets.all(12.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text("Date: ${checkMeECGModel.date}"), + mHeight(8), + Text("TimeString: ${checkMeECGModel.timeString}"), + ], + ), + ), + ); + }, + ), + ) + ] else ...[ + getNoDataWidget(context), + ] + ], + ); + } + + Widget buildOxiWidget(MyTrackersViewModel myTrackersVm, BuildContext context) { + return Column( + mainAxisSize: MainAxisSize.max, + children: [ + if (myTrackersVm.filesLoader) ...[ + Center(child: CircularProgressIndicator()) + ] else if (myTrackersVm.checkMeOxi != null && myTrackersVm.checkMeOxi.isNotEmpty) ...[ + Material( + child: ListView.separated( + itemCount: myTrackersVm.checkMeOxi.length, + separatorBuilder: (context, index) => SizedBox(height: 14), + shrinkWrap: true, + reverse: false, + itemBuilder: (context, index) { + CheckMeOxiModel checkMeOxiModel = myTrackersVm.checkMeOxi[index]; + return InkWell( + onTap: () => Navigator.pop(context), + child: Container( + decoration: cardRadius(12), + padding: EdgeInsets.all(12.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text("PR: ${checkMeOxiModel.pr} PI: ${checkMeOxiModel.pi}"), + mHeight(4.0), + Text("Oxi: ${checkMeOxiModel.oxy}"), + mHeight(4.0), + Text("Date: ${checkMeOxiModel.date}"), + ], + ), + ), + ); + }, + ), + ) + ] else ...[ + getNoDataWidget(context), + ] + ], + ); + } + + Widget buildBPWidget(MyTrackersViewModel myTrackersVm, BuildContext context) { + return Column( + mainAxisSize: MainAxisSize.max, + children: [ + if (myTrackersVm.filesLoader) ...[ + Center(child: CircularProgressIndicator()) + ] else if (myTrackersVm.checkMeBP != null && myTrackersVm.checkMeBP.isNotEmpty) ...[ + Material( + child: ListView.separated( + itemCount: myTrackersVm.checkMeBP.length, + separatorBuilder: (context, index) => SizedBox(height: 14), + shrinkWrap: true, + reverse: false, + itemBuilder: (context, index) { + CheckMeBPModel checkMeBPModel = myTrackersVm.checkMeBP[index]; + return InkWell( + onTap: () => Navigator.pop(context), + child: Container( + decoration: cardRadius(12), + padding: EdgeInsets.all(12.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text("DIA: ${checkMeBPModel.dia} SYS: ${checkMeBPModel.sys}"), + mHeight(4.0), + Text("PR: ${checkMeBPModel.pr}"), + mHeight(4.0), + Text("Date: ${checkMeBPModel.date}"), + ], + ), + ), + ); + }, + ), + ) + ] else ...[ + getNoDataWidget(context), + ] + ], + ); + } + + Widget buildGlucoseWidget(MyTrackersViewModel myTrackersVm, BuildContext context) { + return Column( + mainAxisSize: MainAxisSize.max, + children: [ + if (myTrackersVm.filesLoader) ...[ + Center(child: CircularProgressIndicator()) + ] else if (myTrackersVm.checkMeGlucose != null && myTrackersVm.checkMeGlucose.isNotEmpty) ...[ + Material( + child: ListView.separated( + itemCount: myTrackersVm.checkMeGlucose.length, + separatorBuilder: (context, index) => SizedBox(height: 14), + shrinkWrap: true, + reverse: false, + itemBuilder: (context, index) { + CheckMeGlucoseModel checkMeGlucoseModel = myTrackersVm.checkMeGlucose[index]; + return InkWell( + onTap: () => null, + child: Container( + decoration: cardRadius(12), + padding: EdgeInsets.all(12.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text("Glucose: ${checkMeGlucoseModel.glu}"), + mHeight(4.0), + Text("Date: ${checkMeGlucoseModel.date}"), + ], + ), + ), + ); + }, + ), + ) + ] else ...[ + getNoDataWidget(context), + ] + ], + ); + } + + @override + Widget build(BuildContext context) { + return AppScaffold( + appBarTitle: "${getTrackerNameByEnum(allInOneTrackerTypeEnum)}", + showNewAppBar: true, + isShowDecPage: false, + showNewAppBarTitle: true, + backgroundColor: Color(0xffF8F8F8), + body: SingleChildScrollView( + child: Padding( + padding: const EdgeInsets.all(24.0), + child: Consumer( + builder: (BuildContext context, MyTrackersViewModel myTrackersVm, Widget child) { + return getAllInOneOperationWidgets(myTrackersVm, allInOneTrackerTypeEnum, context); + }, + ), + ), + ), + ); + } +} diff --git a/lib/pages/medical/my_trackers/ble_device_type_screens/checkme_ecg_chart_view.dart b/lib/pages/medical/my_trackers/ble_device_type_screens/checkme_ecg_chart_view.dart new file mode 100644 index 00000000..a5ba453a --- /dev/null +++ b/lib/pages/medical/my_trackers/ble_device_type_screens/checkme_ecg_chart_view.dart @@ -0,0 +1,89 @@ +import 'package:diplomaticquarterapp/pages/medical/my_trackers/ble_models/checkmepro_all_in_one_models/checkme_ecg_wave_model.dart'; +import 'package:fl_chart/fl_chart.dart'; +import 'package:flutter/material.dart'; + +class CheckMeECGChartView extends StatelessWidget { + final CheckMeECGWaveModel checkMeECGWave; + + CheckMeECGChartView({@required this.checkMeECGWave}); + + @override + Widget build(BuildContext context) { + List list = checkMeECGWave.bytes; + List> mainList = chunkIntList(list, 1250); + return ListView.separated( + shrinkWrap: true, + physics: ScrollPhysics(), + itemBuilder: (context, index) { + return Padding( + padding: EdgeInsets.only(right: MediaQuery.of(context).size.width - (MediaQuery.of(context).size.width * (mainList[index].length / 1250))), + child: SizedBox( + height: 120.0, + child: LineChart( + LineChartData( + lineTouchData: LineTouchData(handleBuiltInTouches: false), + gridData: FlGridData( + show: true, + verticalInterval: 30, + horizontalInterval: 30, + getDrawingVerticalLine: (value) { + return FlLine( + color: Colors.red[300], + strokeWidth: 0.4, + ); + }, + getDrawingHorizontalLine: (value) { + return FlLine( + color: Colors.red[300], + strokeWidth: 0.4, + ); + }, + ), + titlesData: FlTitlesData(show: false), + borderData: FlBorderData( + show: false, + border: Border.all(color: const Color(0xff37434d), width: 1), + ), + minX: 0, + maxX: (mainList[index].length.toDouble() - 1), + minY: list.reduce((value, element) => value < element ? value : element).toDouble(), + maxY: list.reduce((value, element) => value > element ? value : element).toDouble(), + lineBarsData: [ + LineChartBarData( + isCurved: false, + preventCurveOverShooting: true, + barWidth: 0.5, + dotData: FlDotData(show: false), + spots: getDataList(mainList[index]), + colors: [Colors.grey[800]], + isStrokeCapRound: true, + belowBarData: BarAreaData(show: false), + ), + ], + ), + ), + ), + ); + }, + itemCount: mainList.length, + separatorBuilder: (context, index) => SizedBox(height: 14), + ); + } + + List> chunkIntList(List list, int chunkSize) { + List> chunks = []; + for (int i = 0; i < list.length; i += chunkSize) { + int end = i + chunkSize; + chunks.add(list.sublist(i, end > list.length ? list.length : end)); + } + return chunks; + } + + List getDataList(List list) { + List spotsList = []; + for (int i = 0; i < list.length; i++) { + spotsList.add(FlSpot(i.toDouble(), list[i].toDouble())); + } + return spotsList; + } +} diff --git a/lib/pages/medical/my_trackers/ble_device_type_screens/checkme_ecg_info_screen.dart b/lib/pages/medical/my_trackers/ble_device_type_screens/checkme_ecg_info_screen.dart new file mode 100644 index 00000000..168c117a --- /dev/null +++ b/lib/pages/medical/my_trackers/ble_device_type_screens/checkme_ecg_info_screen.dart @@ -0,0 +1,114 @@ +import 'package:diplomaticquarterapp/config/size_config.dart'; +import 'package:diplomaticquarterapp/pages/medical/my_trackers/ble_device_type_screens/checkme_ecg_chart_view.dart'; +import 'package:diplomaticquarterapp/pages/medical/my_trackers/ble_models/checkmepro_all_in_one_models/checkme_bp_model.dart'; +import 'package:diplomaticquarterapp/pages/medical/my_trackers/ble_models/checkmepro_all_in_one_models/checkme_ecg_model.dart'; +import 'package:diplomaticquarterapp/pages/medical/my_trackers/ble_models/checkmepro_all_in_one_models/checkme_glucose_model.dart'; +import 'package:diplomaticquarterapp/pages/medical/my_trackers/ble_models/checkmepro_all_in_one_models/checkme_oxi_model.dart'; +import 'package:diplomaticquarterapp/pages/medical/my_trackers/ble_models/checkmepro_all_in_one_models/checkme_temperature_model.dart'; +import 'package:diplomaticquarterapp/pages/medical/my_trackers/my_trackers_view_model/my_trackers_view_model.dart'; +import 'package:diplomaticquarterapp/uitl/utils_new.dart'; +import 'package:diplomaticquarterapp/widgets/others/app_scaffold_widget.dart'; +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; + +class CheckMeECGInfoScreen extends StatelessWidget { + final TrackerTypeEnum allInOneTrackerTypeEnum; + + const CheckMeECGInfoScreen(this.allInOneTrackerTypeEnum); + + String getTrackerNameByEnum(TrackerTypeEnum trackerTypeEnum) { + switch (trackerTypeEnum) { + case TrackerTypeEnum.OxymeterTracker: + return "Oxymeter"; + + case TrackerTypeEnum.BloodPressureTracker: + return "Blood Pressure"; + + case TrackerTypeEnum.BloodSugarTracker: + return "Blood Glucose"; + + case TrackerTypeEnum.ECGTracker: + return "ECG"; + + case TrackerTypeEnum.WeightScale: + return "Weight Scale"; + + case TrackerTypeEnum.Temperature: + return "Temperature"; + + case TrackerTypeEnum.Spirometer: + return "Spirometer"; + + case TrackerTypeEnum.AllInOneTracker: + return "All in One"; + } + return "All in One"; + } + + Widget buildECGDetailsWidget(MyTrackersViewModel myTrackersVm, BuildContext context) { + return Container( + decoration: cardRadius(12), + padding: EdgeInsets.all(12.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + Text("HR: ${myTrackersVm.checkMeECGWave.hr} bpm"), + Text("ST: ${myTrackersVm.checkMeECGWave.st} mV"), + Text("QRS: ${myTrackersVm.checkMeECGWave.qrs} ms"), + ], + ), + mHeight(4.0), + Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + Text("PVCS: ${myTrackersVm.checkMeECGWave.pvcs} "), + Text("QTC: ${myTrackersVm.checkMeECGWave.qtc} ms"), + Text("QT: ${myTrackersVm.checkMeECGWave.qt} ms"), + ], + ), + ], + ), + ); + } + + @override + Widget build(BuildContext context) { + return AppScaffold( + appBarTitle: "${getTrackerNameByEnum(allInOneTrackerTypeEnum)}", + showNewAppBar: true, + isShowDecPage: false, + showNewAppBarTitle: true, + backgroundColor: Color(0xffF8F8F8), + body: SingleChildScrollView( + child: Padding( + padding: const EdgeInsets.all(24.0), + child: Consumer( + builder: (BuildContext context, MyTrackersViewModel myTrackersVm, Widget child) { + if (myTrackersVm.checkMeECGWave == null) { + return Padding( + padding: const EdgeInsets.all(24.0), + child: Center( + child: Text( + "Some animation with the instruction", + style: TextStyle(fontSize: 9.0), + ), + ), + ); + } + return Column( + children: [ + buildECGDetailsWidget(myTrackersVm, context), + mHeight(20), + CheckMeECGChartView(checkMeECGWave: myTrackersVm.checkMeECGWave), + ], + ); + }, + ), + ), + ), + ); + } +} diff --git a/lib/pages/medical/my_trackers/ble_device_type_screens/lepu_device_types/all_in_one_tracker_connect_screen.dart b/lib/pages/medical/my_trackers/ble_device_type_screens/lepu_device_types/all_in_one_tracker_connect_screen.dart new file mode 100644 index 00000000..d9b793ab --- /dev/null +++ b/lib/pages/medical/my_trackers/ble_device_type_screens/lepu_device_types/all_in_one_tracker_connect_screen.dart @@ -0,0 +1,109 @@ +import 'package:diplomaticquarterapp/extensions/string_extensions.dart'; +import 'package:diplomaticquarterapp/pages/medical/my_trackers/ble_models/ble_devices_model.dart'; +import 'package:flutter_blue_plus/flutter_blue_plus.dart'; +import 'package:diplomaticquarterapp/pages/medical/my_trackers/my_trackers_view_model/my_trackers_view_model.dart'; +import 'package:diplomaticquarterapp/uitl/utils_new.dart'; +import 'package:diplomaticquarterapp/widgets/buttons/defaultButton.dart'; +import 'package:diplomaticquarterapp/widgets/others/app_scaffold_widget.dart'; +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; + +class AndesAllInOneConnectScreenIos extends StatefulWidget { + final BleDeviceModel deviceModel; + + const AndesAllInOneConnectScreenIos({this.deviceModel}); + + @override + State createState() => _AndesAllInOneConnectScreenState(); +} + +class _AndesAllInOneConnectScreenState extends State { + MyTrackersViewModel myTrackersVm; + + @override + void initState() { + myTrackersVm = context.read(); + myTrackersVm.connectDevice(widget.deviceModel); + super.initState(); + } + + @override + void dispose() { + myTrackersVm.disConnectDevice(); + super.dispose(); + } + + Widget buildWeightScaleUI(MyTrackersViewModel myTrackersViewModel) { + return Expanded( + child: ListView( + children: [ + if (myTrackersViewModel.andesfitWeightScaleData == null && (myTrackersViewModel.isAndesfitDeviceConnected != null && myTrackersViewModel.isAndesfitDeviceConnected)) ...[ + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + mHeight(24.0), + Column( + children: [ + Text("Please Wait", style: TextStyle(fontSize: 20)), + ], + ), + mHeight(24.0), + ], + ), + ] else if (false) ...[ + // Add the UI here for All In One Monitor + ] else ...[ + Padding( + padding: const EdgeInsets.all(24.0), + child: Center( + child: Text( + "Some animation with the instruction", + style: TextStyle(fontSize: 9.0), + ), + ), + ) + ], + ], + ), + ); + } + + @override + Widget build(BuildContext context) { + return AppScaffold( + appBarTitle: "${widget.deviceModel.name}", + showNewAppBar: true, + isShowDecPage: false, + showNewAppBarTitle: true, + backgroundColor: Color(0xffF8F8F8), + body: Padding( + padding: const EdgeInsets.all(24.0), + child: Consumer( + builder: (BuildContext context, MyTrackersViewModel myTrackersViewModel, Widget child) { + return Column( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + // buildWeightScaleUI(myTrackersViewModel), + Row( + children: [ + Expanded( + child: DefaultButton( + "Disconnect with ${widget.deviceModel.name}", + () async { + myTrackersVm.disConnectDevice(); + Navigator.pop(context); + }, + textColor: Colors.white, + ), + ), + ], + ), + ], + ); + }, + ), + ), + ); + } +} diff --git a/lib/pages/medical/my_trackers/ble_device_type_screens/lepu_device_types/bloodpressure_connect_screen.dart b/lib/pages/medical/my_trackers/ble_device_type_screens/lepu_device_types/bloodpressure_connect_screen.dart new file mode 100644 index 00000000..82f55c0c --- /dev/null +++ b/lib/pages/medical/my_trackers/ble_device_type_screens/lepu_device_types/bloodpressure_connect_screen.dart @@ -0,0 +1,410 @@ +import 'package:diplomaticquarterapp/config/shared_pref_kay.dart'; +import 'package:diplomaticquarterapp/pages/landing/landing_page.dart'; +import 'package:diplomaticquarterapp/pages/medical/my_trackers/ble_models/ble_devices_model.dart'; +import 'package:diplomaticquarterapp/pages/medical/my_trackers/my_trackers_view_model/my_trackers_view_model.dart'; +import 'package:diplomaticquarterapp/uitl/app_shared_preferences.dart'; +import 'package:diplomaticquarterapp/uitl/utils_new.dart'; +import 'package:diplomaticquarterapp/widgets/buttons/defaultButton.dart'; +import 'package:diplomaticquarterapp/widgets/others/app_scaffold_widget.dart'; +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; +import 'package:flutter_toggle_tab/flutter_toggle_tab.dart'; + +class BloodPressureConnectScreen extends StatefulWidget { + final BleDeviceModel deviceModel; + final bool isEcgAvailable; + + const BloodPressureConnectScreen({this.deviceModel, this.isEcgAvailable = false}); + + @override + State createState() => _BloodPressureConnectScreenState(); +} + +class _BloodPressureConnectScreenState extends State { + MyTrackersViewModel myTrackersVm; + + @override + void initState() { + myTrackersVm = context.read(); + myTrackersVm.connectDevice(widget.deviceModel); + super.initState(); + } + + @override + void dispose() { + myTrackersVm.bpCurrentStatus = null; + myTrackersVm.bpRtMeasuringModel = null; + myTrackersVm.bpRtResultModel = null; + myTrackersVm.ecgCurrentStatus = null; + myTrackersVm.ecgRtMeasuringModelFromBP = null; + myTrackersVm.ecgRtResultModelFromBP = null; + myTrackersVm.disConnectDevice(); + super.dispose(); + } + + Widget buildBloodPressureUI(MyTrackersViewModel myTrackersViewModel) { + return Expanded( + child: ListView( + children: [ + if (myTrackersViewModel.bpCurrentStatus == kRealTimeDataBPMeasuring) ...[ + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + mHeight(24.0), + Column( + children: [ + Text(myTrackersViewModel.bpRtMeasuringModel.pressure.toString(), style: TextStyle(fontSize: 100, fontWeight: FontWeight.bold)), + Text("Pressure", style: TextStyle(fontSize: 20)), + ], + ), + mHeight(24.0), + ], + ), + ] else if (myTrackersViewModel.bpCurrentStatus == kRealTimeDataBPResult) ...[ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + mHeight(24.0), + Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + Column( + children: [ + Text("Sys", style: TextStyle(fontSize: 20)), + Text(myTrackersViewModel.bpRtResultModel.sys.toString(), style: TextStyle(fontSize: 50, fontWeight: FontWeight.bold)), + Text("mmHg", style: TextStyle(fontSize: 10)), + ], + ), + Column( + children: [ + Text("Dia", style: TextStyle(fontSize: 20)), + Text(myTrackersViewModel.bpRtResultModel.dia.toString(), style: TextStyle(fontSize: 50, fontWeight: FontWeight.bold)), + Text("mmHg", style: TextStyle(fontSize: 10)), + ], + ), + Column( + children: [ + Text("♥︎", style: TextStyle(fontSize: 20)), + Text(myTrackersViewModel.bpRtResultModel.pr.toString(), style: TextStyle(fontSize: 50, fontWeight: FontWeight.bold)), + Text("/min", style: TextStyle(fontSize: 10)), + ], + ), + ], + ), + mHeight(30.0), + buildStatusForBP(myTrackersViewModel.bpRtResultModel.result), + mHeight(24.0), + ], + ), + ] else ...[ + Padding( + padding: const EdgeInsets.all(24.0), + child: Center( + child: Text( + "Some animation with the instruction", + style: TextStyle(fontSize: 9.0), + ), + ), + ) + ], + ], + ), + ); + } + + Widget buildECGUI(MyTrackersViewModel myTrackersViewModel) { + return Expanded( + child: ListView( + children: [ + if (myTrackersViewModel.ecgCurrentStatus == kRealTimeDataECGMeasuring) ...[ + mHeight(24.0), + Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + Column( + children: [ + Text("Duration", style: TextStyle(fontSize: 20)), + Text(myTrackersViewModel.ecgRtMeasuringModelFromBP.curDuration.toString(), style: TextStyle(fontSize: 50, fontWeight: FontWeight.bold)), + Text("seconds", style: TextStyle(fontSize: 10)), + ], + ), + Column( + children: [ + Text("♥︎", style: TextStyle(fontSize: 20)), + Text(myTrackersViewModel.ecgRtMeasuringModelFromBP.hr.toString(), style: TextStyle(fontSize: 50, fontWeight: FontWeight.bold)), + Text("/min", style: TextStyle(fontSize: 10)), + ], + ), + ], + ), + mHeight(30.0), + ] else if (myTrackersViewModel.ecgCurrentStatus == kRealTimeDataECGResult) ...[ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + mHeight(24.0), + Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + Column( + children: [ + Text("Regular", style: TextStyle(fontSize: 20)), + Text(myTrackersViewModel.ecgRtResultModelFromBP.diagnosis.isRegular ? "YES" : "NO", style: TextStyle(fontSize: 50, fontWeight: FontWeight.bold)), + Text("", style: TextStyle(fontSize: 10)), + ], + ), + Column( + children: [ + Text("♥︎", style: TextStyle(fontSize: 20)), + Text(myTrackersViewModel.ecgRtResultModelFromBP.hr.toString(), style: TextStyle(fontSize: 50, fontWeight: FontWeight.bold)), + Text("/min", style: TextStyle(fontSize: 10)), + ], + ), + // Column( + // children: [ + // Text("Heart Pause", style: TextStyle(fontSize: 20)), + // Text(myTrackersViewModel.ecgRtResultModelFromBP.diagnosis.isHeartPause ? "YES" : "NO", style: TextStyle(fontSize: 40, fontWeight: FontWeight.bold)), + // Text("", style: TextStyle(fontSize: 10)), + // ], + // ), + ], + ), + mHeight(30.0), + // Row( + // mainAxisAlignment: MainAxisAlignment.spaceEvenly, + // children: [ + // Column( + // children: [ + // Text("Lead Off", style: TextStyle(fontSize: 20)), + // Text(myTrackersViewModel.ecgRtResultModelFromBP.diagnosis.isLeadOff ? "YES" : "NO", style: TextStyle(fontSize: 40, fontWeight: FontWeight.bold)), + // Text("", style: TextStyle(fontSize: 10)), + // ], + // ), + // Column( + // children: [ + // Text("Poor Signal", style: TextStyle(fontSize: 20)), + // Text(myTrackersViewModel.ecgRtResultModelFromBP.diagnosis.isPoorSignal ? "YES" : "NO", style: TextStyle(fontSize: 40, fontWeight: FontWeight.bold)), + // Text("", style: TextStyle(fontSize: 10)), + // ], + // ), + // Column( + // children: [ + // Text("PVCs", style: TextStyle(fontSize: 20)), + // Text(myTrackersViewModel.ecgRtResultModelFromBP.diagnosis.isPvcs ? "YES" : "NO", style: TextStyle(fontSize: 40, fontWeight: FontWeight.bold)), + // Text("", style: TextStyle(fontSize: 10)), + // ], + // ), + // ], + // ), + mHeight(30.0), + buildStatusForECG(myTrackersViewModel.ecgRtResultModelFromBP.result), + ], + ), + ] else ...[ + Padding( + padding: const EdgeInsets.all(24.0), + child: Center( + child: Text( + "Some animation with the instruction", + style: TextStyle(fontSize: 9.0), + ), + ), + ) + ], + ], + ), + ); + } + + Widget buildStatusForBP(int result) { + String resultStatus = ""; + + if (result == 0) { + resultStatus = "Normal"; + } else if (result == 1) { + resultStatus = "Unable to analyze(cuff is too loose, inflation is slow, slow air leakage, large air volume)"; + } else if (result == 2) { + resultStatus = "Waveform disorder(arm movement or other interference detected during pumping)"; + } else if (result == 3) { + resultStatus = "Weak signal, unable to detect pulse wave(clothes with interference sleeves)"; + } else { + resultStatus = "Equipment error(valve blocking, over-range blood pressure measurement, serious cuff leakage, software system abnormality, hardware system error, and other abnormalities)"; + } + return Column( + children: [ + Center(child: Text("$resultStatus", style: TextStyle(fontSize: 20))), + if (result != 0) ...[ + mHeight(24.0), + Center(child: Text("Press START/STOP Button on the device to restart.", style: TextStyle(fontSize: 15))), + ], + ], + ); + } + + Widget buildStatusForECG(int result) { + String resultStatus = ""; + + if (result == 0) { + // resultStatus = "Normal"; + resultStatus = ""; // NO NEED TO SHOW "NORMAL" + } else if (result == 1) { + resultStatus = "Unable to analyze(cuff is too loose, inflation is slow, slow air leakage, large air volume)"; + } else if (result == 2) { + resultStatus = "Waveform disorder(arm movement or other interference detected during pumping)"; + } else if (result == 3) { + resultStatus = "Weak signal, unable to detect pulse wave(clothes with interference sleeves)"; + } else { + resultStatus = "Equipment error(valve blocking, over-range blood pressure measurement, serious cuff leakage, software system abnormality, hardware system error, and other abnormalities)"; + } + return Column( + children: [ + Center(child: Text("$resultStatus", style: TextStyle(fontSize: 20))), + if (result != 0) ...[ + mHeight(24.0), + Center(child: Text("Press LIST Button on the device to restart.", style: TextStyle(fontSize: 15))), + ], + ], + ); + } + + showHistoryDialog(BuildContext context) { + return showDialog( + context: context, + builder: (context) => Container( + decoration: BoxDecoration(borderRadius: BorderRadius.all(Radius.circular(15)), color: Colors.white), + padding: const EdgeInsets.all(20), + margin: const EdgeInsets.symmetric(vertical: 35, horizontal: 30), + child: SingleChildScrollView( + child: Consumer(builder: (BuildContext context, MyTrackersViewModel myTrackersVm, Widget child) { + return Column( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Material(child: Center(child: Text("Files History", style: TextStyle(fontSize: 20)))), + if (myTrackersVm.filesLoader) ...[ + Center(child: CircularProgressIndicator()) + ] else if (myTrackersVm.ecgHistoryFiles.isNotEmpty) ...[ + myTrackersVm.getFilesListWidget(myTrackersVm.ecgHistoryFiles, context), + mHeight(15), + Row( + children: [ + Expanded( + child: DefaultButton( + "Reset History", + () async { + myTrackersVm.clearEcgHistoryFiles(); + myTrackersVm.factoryResetECG(widget.deviceModel); + // Navigator.pop(context); + }, + textColor: Colors.white, + ), + ), + ], + ), + ] else ...[ + Material(child: Center(child: Text("No History Files to show", style: TextStyle(fontSize: 20)))), + ] + ], + ); + }), + ), + ), + ); + } + + @override + Widget build(BuildContext context) { + return AppScaffold( + appBarTitle: "${widget.deviceModel.name}", + showNewAppBar: true, + isShowDecPage: false, + showNewAppBarTitle: true, + backgroundColor: Color(0xffF8F8F8), + appBarIcons: [ + if (myTrackersVm.isECGSelected) ...[ + IconButton( + onPressed: () async { + myTrackersVm.updateFilesLoader(true); + showHistoryDialog(context); + await myTrackersVm.getEcgFilesList(widget.deviceModel); + }, + icon: Icon(Icons.format_list_numbered_rounded), + ), + ], + IconButton( + onPressed: () { + AppSharedPreferences().remove(IS_LIVECARE_APPOINTMENT); + Navigator.pushAndRemoveUntil( + context, + MaterialPageRoute(builder: (context) => LandingPage()), + (Route route) => false, + ); + }, + icon: Icon(Icons.home), + ), + ], + body: Padding( + padding: const EdgeInsets.all(24.0), + child: Consumer( + builder: (BuildContext context, MyTrackersViewModel myTrackersViewModel, Widget child) { + return Column( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + if (widget.isEcgAvailable) ...[ + FlutterToggleTab( + borderRadius: 30, + height: 40, + width: 80, + selectedIndex: myTrackersViewModel.isECGSelected ? 1 : 0, + selectedBackgroundColors: [Color(0xffD02127), Color(0xffD02127)], + selectedTextStyle: TextStyle(color: Colors.white, fontSize: 18, fontWeight: FontWeight.w700), + unSelectedTextStyle: TextStyle(color: Colors.black87, fontSize: 14, fontWeight: FontWeight.w500), + labels: ["BP", "ECG"], + selectedLabelIndex: (index) { + if (index == 1 && myTrackersViewModel.bpCurrentStatus == kRealTimeDataBPMeasuring) { + return; + } + if (index == 0 && myTrackersViewModel.ecgCurrentStatus == kRealTimeDataECGMeasuring) { + return; + } + if (index == 0) { + myTrackersViewModel.updateIsECGSelected(false); + } else { + myTrackersViewModel.updateIsECGSelected(true); + } + setState(() {}); + }, + isScroll: false, + ), + ], + myTrackersViewModel.isECGSelected ? buildECGUI(myTrackersViewModel) : buildBloodPressureUI(myTrackersViewModel), + if (myTrackersViewModel.bpCurrentStatus != null || myTrackersViewModel.ecgCurrentStatus != null) ...[ + Row( + children: [ + Expanded( + child: DefaultButton( + "Disconnect with ${widget.deviceModel.name}", + () async { + myTrackersVm.bpCurrentStatus = null; + myTrackersVm.bpRtMeasuringModel = null; + myTrackersVm.bpRtResultModel = null; + myTrackersVm.ecgCurrentStatus = null; + myTrackersVm.ecgRtMeasuringModelFromBP = null; + myTrackersVm.ecgRtResultModelFromBP = null; + Navigator.pop(context); + }, + textColor: Colors.white, + ), + ), + ], + ), + ] + ], + ); + }, + ), + ), + ); + } +} diff --git a/lib/pages/medical/my_trackers/ble_device_type_screens/lepu_device_types/bloodsugar_connect_screen.dart b/lib/pages/medical/my_trackers/ble_device_type_screens/lepu_device_types/bloodsugar_connect_screen.dart new file mode 100644 index 00000000..e69de29b diff --git a/lib/pages/medical/my_trackers/ble_device_type_screens/lepu_device_types/ecg_connect_screen.dart b/lib/pages/medical/my_trackers/ble_device_type_screens/lepu_device_types/ecg_connect_screen.dart new file mode 100644 index 00000000..0005bac8 --- /dev/null +++ b/lib/pages/medical/my_trackers/ble_device_type_screens/lepu_device_types/ecg_connect_screen.dart @@ -0,0 +1,227 @@ +import 'package:diplomaticquarterapp/config/shared_pref_kay.dart'; +import 'package:diplomaticquarterapp/pages/landing/landing_page.dart'; +import 'package:diplomaticquarterapp/pages/medical/my_trackers/ble_models/ble_devices_model.dart'; +import 'package:diplomaticquarterapp/pages/medical/my_trackers/ble_models/viatom_devices/ecg_file_detail_model.dart'; +import 'package:diplomaticquarterapp/pages/medical/my_trackers/my_trackers_view_model/my_trackers_view_model.dart'; +import 'package:diplomaticquarterapp/pages/medical/my_trackers/viatom_devices/ekg_chart_view.dart'; +import 'package:diplomaticquarterapp/uitl/app_shared_preferences.dart'; +import 'package:diplomaticquarterapp/uitl/date_uitl.dart'; +import 'package:diplomaticquarterapp/uitl/gif_loader_dialog_utils.dart'; +import 'package:diplomaticquarterapp/uitl/utils_new.dart'; +import 'package:diplomaticquarterapp/widgets/buttons/defaultButton.dart'; +import 'package:diplomaticquarterapp/widgets/others/app_scaffold_widget.dart'; +import 'package:diplomaticquarterapp/widgets/transitions/fade_page.dart'; +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; + +class ECGConnectScreen extends StatefulWidget { + final BleDeviceModel deviceModel; + + const ECGConnectScreen({this.deviceModel}); + + @override + State createState() => _ECGConnectScreenState(); +} + +class _ECGConnectScreenState extends State { + MyTrackersViewModel myTrackersVm; + + @override + void initState() { + myTrackersVm = context.read(); + myTrackersVm.connectDevice(widget.deviceModel); + super.initState(); + } + + @override + void dispose() { + myTrackersVm.disConnectDevice(); + super.dispose(); + } + + Widget buildStatus(int currentStatus) { + String resultStatus = ""; + + if (currentStatus == 0) { + resultStatus = "Idle"; + } else if (currentStatus == 1) { + resultStatus = "Preparing"; + } else if (currentStatus == 2) { + resultStatus = "Measuring"; + } else if (currentStatus == 3) { + resultStatus = "Saving File"; + } else if (currentStatus == 4) { + resultStatus = "File Successfully Saved"; + } else if (currentStatus == 5) { + resultStatus = "Can't measure before 30s, file was not saved"; + } else if (currentStatus == 6) { + resultStatus = "Retests"; + } else if (currentStatus == 7) { + resultStatus = "Lead Off"; + } else { + resultStatus = ""; + } + return Column( + children: [ + Center(child: Text("$resultStatus", style: TextStyle(fontSize: 20))), + if (currentStatus == 5) ...[ + mHeight(24.0), + Center(child: Text("Press hold on the device to restart.", style: TextStyle(fontSize: 15))), + ], + ], + ); + } + + + + showHistoryDialog(BuildContext context) { + return showDialog( + context: context, + builder: (context) => Container( + decoration: BoxDecoration(borderRadius: BorderRadius.all(Radius.circular(15)), color: Colors.white), + padding: const EdgeInsets.all(20), + margin: const EdgeInsets.symmetric(vertical: 35, horizontal: 30), + child: SingleChildScrollView( + child: Consumer(builder: (BuildContext context, MyTrackersViewModel myTrackersVm, Widget child) { + return Column( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Material(child: Center(child: Text("Files History", style: TextStyle(fontSize: 20)))), + if (myTrackersVm.filesLoader) ...[ + Center(child: CircularProgressIndicator()) + ] else if (myTrackersVm.ecgHistoryFiles.isNotEmpty) ...[ + myTrackersVm.getFilesListWidget(myTrackersVm.ecgHistoryFiles, context), + mHeight(15), + Row( + children: [ + Expanded( + child: DefaultButton( + "Reset History", + () async { + myTrackersVm.clearEcgHistoryFiles(); + myTrackersVm.factoryResetECG(widget.deviceModel); + // Navigator.pop(context); + }, + textColor: Colors.white, + ), + ), + ], + ), + ] else ...[ + Material(child: Center(child: Text("No History Files to show", style: TextStyle(fontSize: 20)))), + ] + ], + ); + }), + ), + ), + ); + } + + @override + Widget build(BuildContext context) { + MyTrackersViewModel myTrackersViewModel = context.watch(); + return AppScaffold( + appBarTitle: "${widget.deviceModel.name}", + showNewAppBar: true, + isShowDecPage: false, + showNewAppBarTitle: true, + backgroundColor: Color(0xffF8F8F8), + appBarIcons: [ + IconButton( + onPressed: () async { + myTrackersViewModel.updateFilesLoader(true); + showHistoryDialog(context); + await myTrackersViewModel.getEcgFilesList(widget.deviceModel); + }, + icon: Icon(Icons.format_list_numbered_rounded), + ), + IconButton( + onPressed: () { + AppSharedPreferences().remove(IS_LIVECARE_APPOINTMENT); + Navigator.pushAndRemoveUntil( + context, + MaterialPageRoute(builder: (context) => LandingPage()), + (Route route) => false, + ); + }, + icon: Icon(Icons.home), + ), + ], + body: Padding( + padding: const EdgeInsets.all(24.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Expanded( + child: ListView( + children: [ + if (myTrackersViewModel.ecgRtModel != null) ...[ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + mHeight(24.0), + Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + Column( + children: [ + Text("Record Time", style: TextStyle(fontSize: 20)), + Text(myTrackersViewModel.ecgRtModel.param.recordTime.toString(), style: TextStyle(fontSize: 50, fontWeight: FontWeight.bold)), + Text("seconds", style: TextStyle(fontSize: 10)), + ], + ), + Column( + children: [ + Text("♥︎", style: TextStyle(fontSize: 20)), + Text(myTrackersViewModel.ecgRtModel.param.hr.toString(), style: TextStyle(fontSize: 50, fontWeight: FontWeight.bold)), + Text("/min", style: TextStyle(fontSize: 10)), + ], + ), + ], + ), + mHeight(30.0), + buildStatus(myTrackersViewModel.ecgRtModel.param.curStatus), + ], + ) + ], + if (myTrackersViewModel.ecgRtModel == null) ...[ + Padding( + padding: const EdgeInsets.all(24.0), + child: Center( + child: Text( + "Some animation with the instruction", + style: TextStyle(fontSize: 9.0), + ), + ), + ), + ] + ], + ), + ), + if (myTrackersViewModel.ecgRtModel != null) ...[ + mHeight(10.0), + Row( + children: [ + Expanded( + flex: 2, + child: DefaultButton( + "Disconnect ${widget.deviceModel.name}", + () async { + myTrackersViewModel.ecgRtModel = null; + Navigator.pop(context); + }, + textColor: Colors.white, + ), + ), + ], + ), + ] + ], + ), + ), + ); + } +} diff --git a/lib/pages/medical/my_trackers/ble_device_type_screens/lepu_device_types/oxymeter_connect_screen.dart b/lib/pages/medical/my_trackers/ble_device_type_screens/lepu_device_types/oxymeter_connect_screen.dart new file mode 100644 index 00000000..bd7b7247 --- /dev/null +++ b/lib/pages/medical/my_trackers/ble_device_type_screens/lepu_device_types/oxymeter_connect_screen.dart @@ -0,0 +1,129 @@ +import 'package:diplomaticquarterapp/pages/medical/my_trackers/ble_models/ble_devices_model.dart'; +import 'package:diplomaticquarterapp/pages/medical/my_trackers/my_trackers_view_model/my_trackers_view_model.dart'; +import 'package:diplomaticquarterapp/uitl/utils_new.dart'; +import 'package:diplomaticquarterapp/widgets/buttons/defaultButton.dart'; +import 'package:diplomaticquarterapp/widgets/others/app_scaffold_widget.dart'; +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; + +class OxymeterConnectScreen extends StatefulWidget { + final BleDeviceModel deviceModel; + + const OxymeterConnectScreen({this.deviceModel}); + + @override + State createState() => _OxymeterConnectScreenState(); +} + +class _OxymeterConnectScreenState extends State { + MyTrackersViewModel myTrackersVm; + + @override + void initState() { + myTrackersVm = context.read(); + myTrackersVm.connectDevice(widget.deviceModel); + super.initState(); + } + + @override + void dispose() { + myTrackersVm.disConnectDevice(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return AppScaffold( + appBarTitle: "${widget.deviceModel.name}", + showNewAppBar: true, + isShowDecPage: false, + showNewAppBarTitle: true, + backgroundColor: Color(0xffF8F8F8), + body: Padding( + padding: const EdgeInsets.all(24.0), + child: Consumer( + builder: (BuildContext context, MyTrackersViewModel myTrackersViewModel, Widget child) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Expanded( + child: ListView( + children: [ + if (myTrackersViewModel.oxyRtModel != null) ...[ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + mHeight(24.0), + Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + if (myTrackersViewModel.oxyRtModel.isProbeOff) ...[ + Text("Check Probe", style: TextStyle(fontSize: 50, fontWeight: FontWeight.bold)), + ] else ...[ + Column( + children: [ + Text("SPO₂", style: TextStyle(fontSize: 20)), + Text(myTrackersViewModel.oxyRtModel.spo2.toString(), style: TextStyle(fontSize: 50, fontWeight: FontWeight.bold)), + Text("%", style: TextStyle(fontSize: 10)), + ], + ), + Column( + children: [ + Text("Pulse", style: TextStyle(fontSize: 20)), + Text(myTrackersViewModel.oxyRtModel.pr.toString(), style: TextStyle(fontSize: 50, fontWeight: FontWeight.bold)), + Text("bpm", style: TextStyle(fontSize: 10)), + ], + ), + Column( + children: [ + Text("PI", style: TextStyle(fontSize: 20)), + Text(myTrackersViewModel.oxyRtModel.pi.toString(), style: TextStyle(fontSize: 50, fontWeight: FontWeight.bold)), + Text("%", style: TextStyle(fontSize: 10)), + ], + ), + ], + ], + ), + mHeight(24.0), + ], + ) + ], + if (myTrackersViewModel.oxyRtModel == null) ...[ + Padding( + padding: const EdgeInsets.all(24.0), + child: Center( + child: Text( + "Some animation with the instruction", + style: TextStyle(fontSize: 9.0), + ), + ), + ), + ] + ], + ), + ), + if (myTrackersViewModel.oxyRtModel != null) ...[ + Row( + children: [ + Expanded( + child: DefaultButton( + "Disconnect with ${widget.deviceModel.name}", + () async { + myTrackersVm.oxyRtModel = null; + Navigator.pop(context); + }, + textColor: Colors.white, + ), + ), + ], + ), + ] + ], + ); + }, + ), + ), + ); + } +} diff --git a/lib/pages/medical/my_trackers/ble_device_type_screens/select_tracker_type.dart b/lib/pages/medical/my_trackers/ble_device_type_screens/select_tracker_type.dart new file mode 100644 index 00000000..aea30209 --- /dev/null +++ b/lib/pages/medical/my_trackers/ble_device_type_screens/select_tracker_type.dart @@ -0,0 +1,181 @@ +import 'package:diplomaticquarterapp/pages/medical/my_trackers/ble_device_type_screens/ble_devices_screen.dart'; +import 'package:diplomaticquarterapp/pages/medical/my_trackers/my_trackers_view_model/my_trackers_view_model.dart'; +import 'package:diplomaticquarterapp/widgets/data_display/medical/medical_profile_item.dart'; +import 'package:diplomaticquarterapp/widgets/others/app_scaffold_widget.dart'; +import 'package:diplomaticquarterapp/widgets/transitions/fade_page.dart'; +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; + +class SelectTrackerType extends StatelessWidget { + const SelectTrackerType(); + + List myTrackersTypeList({BuildContext context}) { + List medical = []; + medical.add(InkWell( + onTap: () { + context.read().updateSelectedTrackerType(TrackerTypeEnum.OxymeterTracker); + Navigator.of(context).push(FadePage(page: BleDevicesScreen())); + }, + child: MedicalProfileItem( + title: "Oxymeter", + imagePath: 'tracker.svg', + subTitle: "Tracker", + isEnable: true, + ), + )); + medical.add( + InkWell( + onTap: () { + context.read().updateSelectedTrackerType(TrackerTypeEnum.BloodPressureTracker); + Navigator.of(context).push(FadePage(page: BleDevicesScreen())); + }, + child: MedicalProfileItem( + title: "Blood Pressure", + imagePath: 'tracker.svg', + subTitle: "Tracker", + isEnable: true, + ), + ), + ); + medical.add( + InkWell( + onTap: () { + context.read().updateSelectedTrackerType(TrackerTypeEnum.BloodSugarTracker); + Navigator.of(context).push(FadePage(page: BleDevicesScreen())); + }, + child: MedicalProfileItem( + title: "Blood Sugar", + imagePath: 'tracker.svg', + subTitle: "Tracker", + isEnable: true, + ), + ), + ); + medical.add( + InkWell( + onTap: () { + context.read().updateSelectedTrackerType(TrackerTypeEnum.ECGTracker); + Navigator.of(context).push(FadePage(page: BleDevicesScreen())); + }, + child: MedicalProfileItem( + title: "ECG", + imagePath: 'tracker.svg', + subTitle: "Tracker", + isEnable: true, + ), + ), + ); + medical.add( + InkWell( + onTap: () { + context.read().updateSelectedTrackerType(TrackerTypeEnum.WeightScale); + Navigator.of(context).push(FadePage(page: BleDevicesScreen())); + }, + child: MedicalProfileItem( + title: "Weight", + imagePath: 'tracker.svg', + subTitle: "Scale", + isEnable: true, + ), + ), + ); + medical.add( + InkWell( + onTap: () { + context.read().updateSelectedTrackerType(TrackerTypeEnum.Temperature); + Navigator.of(context).push(FadePage(page: BleDevicesScreen())); + }, + child: MedicalProfileItem( + title: "Temperature", + imagePath: 'tracker.svg', + subTitle: "Scale", + isEnable: true, + ), + ), + ); + medical.add( + InkWell( + onTap: () { + context.read().updateSelectedTrackerType(TrackerTypeEnum.Spirometer); + Navigator.of(context).push(FadePage(page: BleDevicesScreen())); + }, + child: MedicalProfileItem( + title: "Spirometer", + imagePath: 'tracker.svg', + subTitle: "Tracker", + isEnable: true, + ), + ), + ); + medical.add( + InkWell( + onTap: () { + context.read().updateSelectedTrackerType(TrackerTypeEnum.SmartRing); + Navigator.of(context).push(FadePage(page: BleDevicesScreen())); + }, + child: MedicalProfileItem( + title: "SmartRing", + imagePath: 'tracker.svg', + subTitle: "Tracker", + isEnable: true, + ), + ), + ); + medical.add( + InkWell( + onTap: () { + context.read().updateSelectedTrackerType(TrackerTypeEnum.AllInOneTracker); + Navigator.of(context).push(FadePage(page: BleDevicesScreen())); + }, + child: MedicalProfileItem( + title: "All in one Monitor", + imagePath: 'tracker.svg', + subTitle: "Tracker", + isEnable: true, + ), + ), + ); + + return medical; + } + + @override + Widget build(BuildContext context) { + List myMedicalList = myTrackersTypeList(context: context); + return AppScaffold( + isShowDecPage: false, + isShowAppBar: false, + showNewAppBar: true, + isHelp: true, + showNewAppBarTitle: true, + onTap: () { + context.read().disConnectDevice(); + Navigator.pop(context); + }, + appBarTitle: "Select Tracker", + body: Container( + child: SingleChildScrollView( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: EdgeInsets.only(left: 12, right: 12, top: 20), + child: GridView.builder( + shrinkWrap: true, + primary: false, + physics: NeverScrollableScrollPhysics(), + gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 3, childAspectRatio: 2 / 2, crossAxisSpacing: 12, mainAxisSpacing: 12), + padding: EdgeInsets.zero, + itemCount: myMedicalList.length, + itemBuilder: (BuildContext context, int index) { + return myMedicalList[index]; + }, + ), + ), + ], + ), + ), + ), + ); + } +} diff --git a/lib/pages/medical/my_trackers/ble_device_type_screens/smart_ring_connect_screen.dart b/lib/pages/medical/my_trackers/ble_device_type_screens/smart_ring_connect_screen.dart new file mode 100644 index 00000000..7aa66b24 --- /dev/null +++ b/lib/pages/medical/my_trackers/ble_device_type_screens/smart_ring_connect_screen.dart @@ -0,0 +1,191 @@ +import 'package:diplomaticquarterapp/pages/medical/my_trackers/ble_device_type_screens/smart_ring_info_screen.dart'; +import 'package:diplomaticquarterapp/pages/medical/my_trackers/ble_models/ble_devices_model.dart'; +import 'package:diplomaticquarterapp/pages/medical/my_trackers/my_trackers_view_model/my_trackers_view_model.dart'; +import 'package:diplomaticquarterapp/widgets/buttons/defaultButton.dart'; +import 'package:diplomaticquarterapp/widgets/data_display/medical/medical_profile_item.dart'; +import 'package:diplomaticquarterapp/widgets/others/app_scaffold_widget.dart'; +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; +import 'package:diplomaticquarterapp/widgets/transitions/fade_page.dart'; + +class SmartRingAllInOneConnectScreen extends StatefulWidget { + final BleDeviceModel deviceModel; + + const SmartRingAllInOneConnectScreen({this.deviceModel}); + + @override + State createState() => _SmartRingAllInOneConnectScreenState(); +} + +class _SmartRingAllInOneConnectScreenState extends State { + MyTrackersViewModel myTrackersVm; + + @override + void initState() { + myTrackersVm = context.read(); + myTrackersVm.connectDevice(widget.deviceModel); + super.initState(); + } + + @override + void dispose() { + myTrackersVm.disConnectDevice(); + super.dispose(); + } + + String getTrackerNameByEnum(SmartRingOperationsEnum smartRingOperationsEnum) { + switch (smartRingOperationsEnum) { + case SmartRingOperationsEnum.Temperature: + return "Temperature"; + + case SmartRingOperationsEnum.HeartRate: + return "HeartRate"; + + case SmartRingOperationsEnum.RealTimeHeartRate: + return "Realtime HeartRate"; + + case SmartRingOperationsEnum.TotalStepCount: + return "Total Step Count"; + + case SmartRingOperationsEnum.HRV: + return "HRV"; + + case SmartRingOperationsEnum.BloodOxygen: + return "Blood Oxygen"; + + case SmartRingOperationsEnum.SportsMode: + return "Sports Mode"; + + case SmartRingOperationsEnum.BatteryLevel: + return "Battery Level"; + } + return "All in One"; + } + + Future onTrackerTypePressed(SmartRingOperationsEnum smartRingOperationsEnum, MyTrackersViewModel myTrackersViewModel) async { + switch (smartRingOperationsEnum) { + case SmartRingOperationsEnum.Temperature: + myTrackersViewModel.getTemperatureSmartRing(); + Navigator.of(context).push(FadePage(page: SmartRingInfoScreen(smartRingOperationsEnum))); + break; + case SmartRingOperationsEnum.HeartRate: + myTrackersViewModel.getStaticHeartRateSmartRing(); + Navigator.of(context).push(FadePage(page: SmartRingInfoScreen(smartRingOperationsEnum))); + break; + case SmartRingOperationsEnum.RealTimeHeartRate: + // myTrackersViewModel.getDynamicHeartRateSmartRing(); + // Navigator.of(context).push(FadePage(page: SmartRingInfoScreen(smartRingOperationsEnum))); + break; + case SmartRingOperationsEnum.TotalStepCount: + myTrackersViewModel.getTotalStepCountSmartRing(); + Navigator.of(context).push(FadePage(page: SmartRingInfoScreen(smartRingOperationsEnum))); + break; + case SmartRingOperationsEnum.HRV: + myTrackersViewModel.getHRVSmartRing(); + Navigator.of(context).push(FadePage(page: SmartRingInfoScreen(smartRingOperationsEnum))); + break; + case SmartRingOperationsEnum.BloodOxygen: + myTrackersViewModel.getBloodOxygenSmartRing(); + Navigator.of(context).push(FadePage(page: SmartRingInfoScreen(smartRingOperationsEnum))); + break; + case SmartRingOperationsEnum.SportsMode: + myTrackersViewModel.getSportsModeDataSmartRing(); + Navigator.of(context).push(FadePage(page: SmartRingInfoScreen(smartRingOperationsEnum))); + + break; + case SmartRingOperationsEnum.BatteryLevel: + myTrackersViewModel.getBatteryLevelSmartRing(); + Navigator.of(context).push(FadePage(page: SmartRingInfoScreen(smartRingOperationsEnum))); + break; + default: + break; + } + } + + Widget buildSmartRingOperationsUI(MyTrackersViewModel myTrackersViewModel) { + return Expanded( + child: ListView( + children: [ + if (myTrackersViewModel.smartRingBatteryLevel == null) ...[ + Padding( + padding: const EdgeInsets.all(24.0), + child: Center( + child: Text( + "Some animation with the instruction", + style: TextStyle(fontSize: 9.0), + ), + ), + ) + ] else ...[ + Center( + child: Column( + children: [ + GridView.builder( + shrinkWrap: true, + primary: false, + physics: NeverScrollableScrollPhysics(), + gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 3, childAspectRatio: 2 / 2, crossAxisSpacing: 12, mainAxisSpacing: 12), + padding: EdgeInsets.zero, + itemCount: myTrackersViewModel.smartRingTrackers.length, + itemBuilder: (BuildContext context, int index) { + return InkWell( + onTap: () => onTrackerTypePressed(myTrackersViewModel.smartRingTrackers[index], myTrackersViewModel), + child: MedicalProfileItem( + title: getTrackerNameByEnum(myTrackersViewModel.smartRingTrackers[index]), + imagePath: 'tracker.svg', + subTitle: "", + isEnable: true, + ), + ); + }, + ), + ], + ), + ) + ] + ], + ), + ); + } + + @override + Widget build(BuildContext context) { + return AppScaffold( + appBarTitle: "${widget.deviceModel.name}", + showNewAppBar: true, + isShowDecPage: false, + showNewAppBarTitle: true, + backgroundColor: Color(0xffF8F8F8), + body: Padding( + padding: const EdgeInsets.all(24.0), + child: Consumer( + builder: (BuildContext context, MyTrackersViewModel myTrackersViewModel, Widget child) { + return Column( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + buildSmartRingOperationsUI(myTrackersViewModel), + if (myTrackersViewModel.smartRingBatteryLevel != null) ...[ + Row( + children: [ + Expanded( + child: DefaultButton( + "Disconnect with ${widget.deviceModel.name}", + () async { + myTrackersVm.disConnectDevice(); + Navigator.pop(context); + }, + textColor: Colors.white, + ), + ), + ], + ), + ] + ], + ); + }, + ), + ), + ); + } +} diff --git a/lib/pages/medical/my_trackers/ble_device_type_screens/smart_ring_info_screen.dart b/lib/pages/medical/my_trackers/ble_device_type_screens/smart_ring_info_screen.dart new file mode 100644 index 00000000..ea4134ad --- /dev/null +++ b/lib/pages/medical/my_trackers/ble_device_type_screens/smart_ring_info_screen.dart @@ -0,0 +1,408 @@ +import 'package:diplomaticquarterapp/pages/medical/my_trackers/ble_models/smart_ring_models.dart'; +import 'package:diplomaticquarterapp/pages/medical/my_trackers/my_trackers_view_model/my_trackers_view_model.dart'; +import 'package:diplomaticquarterapp/uitl/utils_new.dart'; +import 'package:diplomaticquarterapp/widgets/others/app_scaffold_widget.dart'; +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; + +class SmartRingInfoScreen extends StatefulWidget { + final SmartRingOperationsEnum smartRingOperationsEnum; + + const SmartRingInfoScreen(this.smartRingOperationsEnum); + + @override + State createState() => _SmartRingInfoScreenState(); +} + +class _SmartRingInfoScreenState extends State { + MyTrackersViewModel myTrackersViewModel; + + @override + void initState() { + myTrackersViewModel = context.read(); + super.initState(); + } + + @override + void dispose() { + myTrackersViewModel.stopSportsModeDataSmartRing(); + super.dispose(); + } + + String getTrackerNameByEnum(SmartRingOperationsEnum typeEnum) { + switch (typeEnum) { + case SmartRingOperationsEnum.Temperature: + return "Temperature"; + + case SmartRingOperationsEnum.BloodOxygen: + return "Blood Oxygen"; + + case SmartRingOperationsEnum.RealTimeHeartRate: + return "HeartRate Dynamic"; + + case SmartRingOperationsEnum.HeartRate: + return "HeartRate Static"; + + case SmartRingOperationsEnum.HRV: + return "HRV"; + + case SmartRingOperationsEnum.BatteryLevel: + return "Battery Level"; + + case SmartRingOperationsEnum.TotalStepCount: + return "Total Step Count"; + + case SmartRingOperationsEnum.SportsMode: + return "Sports Mode"; + } + return "Battery Level"; + } + + + Widget getAllInOneOperationWidgets(MyTrackersViewModel myTrackersVm, SmartRingOperationsEnum smartRingOperationsEnum, BuildContext context) { + switch (smartRingOperationsEnum) { + case SmartRingOperationsEnum.Temperature: + return buildTemperatureWidget(myTrackersVm, context); + case SmartRingOperationsEnum.HeartRate: + return buildHeartRateStaticWidget(myTrackersVm, context); + case SmartRingOperationsEnum.RealTimeHeartRate: + return buildHeartRateDynamicWidget(myTrackersVm, context); + case SmartRingOperationsEnum.TotalStepCount: + return buildTotalStepCountHistoryWidget(myTrackersVm, context); + case SmartRingOperationsEnum.HRV: + return buildHrvWidget(myTrackersVm, context); + case SmartRingOperationsEnum.BloodOxygen: + return buildBloodOxygenWidget(myTrackersVm, context); + case SmartRingOperationsEnum.BatteryLevel: + return buildBatteryLevelWidget(myTrackersVm, context); + case SmartRingOperationsEnum.SportsMode: + return buildRtSportsDataSmartRingWidget(myTrackersVm, context); + break; + } + return getNoDataWidget(context); + } + + Widget buildTemperatureWidget(MyTrackersViewModel myTrackersVm, BuildContext context) { + if (myTrackersVm.filesLoader) { + return Center(child: CircularProgressIndicator()); + } else if (myTrackersVm.smartRingTemperatureHistory != null && myTrackersVm.smartRingTemperatureHistory.isNotEmpty) { + return Expanded( + child: ListView.separated( + itemCount: myTrackersVm.smartRingTemperatureHistory.length, + separatorBuilder: (context, index) => SizedBox(height: 14), + shrinkWrap: true, + reverse: false, + itemBuilder: (context, index) { + SmartRingGenericModel smartRingTemp = myTrackersVm.smartRingTemperatureHistory[index]; + return InkWell( + onTap: () => Navigator.pop(context), + child: Container( + decoration: cardRadius(12), + padding: EdgeInsets.all(12.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text("Temperature: ${smartRingTemp.value}"), + mHeight(4.0), + Text("Date: ${smartRingTemp.date}"), + ], + ), + ), + ); + }, + ), + ); + } else { + return getNoDataWidget(context); + } + } + + Widget buildHeartRateDynamicWidget(MyTrackersViewModel myTrackersVm, BuildContext context) { + if (myTrackersVm.filesLoader) { + return Center(child: CircularProgressIndicator()); + } else if (myTrackersVm.smartRingHeartRateDynamicHistory != null && myTrackersVm.smartRingHeartRateDynamicHistory.isNotEmpty) { + return Expanded( + child: ListView.separated( + itemCount: myTrackersVm.smartRingHeartRateDynamicHistory.length, + separatorBuilder: (context, index) => SizedBox(height: 14), + shrinkWrap: true, + reverse: false, + itemBuilder: (context, index) { + SmartRingGenericModel smartRingHr = myTrackersVm.smartRingHeartRateDynamicHistory[index]; + return InkWell( + onTap: () => Navigator.pop(context), + child: Container( + decoration: cardRadius(12), + padding: EdgeInsets.all(12.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text("arrayDynamicHR: ${smartRingHr.value}"), + mHeight(4.0), + Text("Date: ${smartRingHr.date}"), + ], + ), + ), + ); + }, + ), + ); + } else { + return getNoDataWidget(context); + } + } + + Widget buildHeartRateStaticWidget(MyTrackersViewModel myTrackersVm, BuildContext context) { + if (myTrackersVm.filesLoader) { + return Center(child: CircularProgressIndicator()); + } else if (myTrackersVm.smartRingHeartRateStaticHistory != null && myTrackersVm.smartRingHeartRateStaticHistory.isNotEmpty) { + return Expanded( + child: ListView.separated( + itemCount: myTrackersVm.smartRingHeartRateStaticHistory.length, + separatorBuilder: (context, index) => SizedBox(height: 14), + shrinkWrap: true, + reverse: false, + itemBuilder: (context, index) { + SmartRingGenericModel smartRingHr = myTrackersVm.smartRingHeartRateStaticHistory[index]; + return InkWell( + onTap: () => Navigator.pop(context), + child: Container( + decoration: cardRadius(12), + padding: EdgeInsets.all(12.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text("onceHeartValue: ${smartRingHr.value}"), + mHeight(4.0), + Text("Date: ${smartRingHr.date}"), + ], + ), + ), + ); + }, + ), + ); + } else { + return getNoDataWidget(context); + } + } + + Widget buildRtSportsDataSmartRingWidget(MyTrackersViewModel myTrackersVm, BuildContext context) { + if (myTrackersVm.filesLoader) { + return Center(child: CircularProgressIndicator()); + } else if (myTrackersVm.rtSportDataSmartRingModel != null) { + return Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + mHeight(24.0), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Column( + children: [ + Text("Step", style: TextStyle(fontSize: 20)), + Text(myTrackersViewModel.rtSportDataSmartRingModel.step.toString(), style: TextStyle(fontSize: 50, fontWeight: FontWeight.bold)), + Text("", style: TextStyle(fontSize: 10)), + ], + ), + Column( + children: [ + Text("Calories", style: TextStyle(fontSize: 20)), + Text(myTrackersViewModel.rtSportDataSmartRingModel.calories.toString(), style: TextStyle(fontSize: 50, fontWeight: FontWeight.bold)), + Text("kCal", style: TextStyle(fontSize: 10)), + ], + ), + Column( + children: [ + Text("♥︎", style: TextStyle(fontSize: 20)), + Text(myTrackersViewModel.rtSportDataSmartRingModel.heartRate.toString(), style: TextStyle(fontSize: 50, fontWeight: FontWeight.bold)), + Text("/min", style: TextStyle(fontSize: 10)), + ], + ), + ], + ), + mHeight(30.0), + Row( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Column( + children: [ + Text("Time", style: TextStyle(fontSize: 20)), + Text(myTrackersViewModel.rtSportDataSmartRingModel.exerciseTime.toString(), style: TextStyle(fontSize: 50, fontWeight: FontWeight.bold)), + Text("seconds", style: TextStyle(fontSize: 10)), + ], + ), + ], + ), + // buildStatusForBP(myTrackersViewModel.bpRtResultModel.result), + // mHeight(24.0), + ], + ), + ); + } else { + return getNoDataWidget(context); + } + } + + Widget buildHrvWidget(MyTrackersViewModel myTrackersVm, BuildContext context) { + if (myTrackersVm.filesLoader) { + return Center(child: CircularProgressIndicator()); + } else if (myTrackersVm.smartRingHrvHistory != null && myTrackersVm.smartRingHrvHistory.isNotEmpty) { + return Expanded( + child: ListView.separated( + itemCount: myTrackersVm.smartRingHrvHistory.length, + separatorBuilder: (context, index) => SizedBox(height: 14), + shrinkWrap: true, + reverse: false, + itemBuilder: (context, index) { + SmartRingHrvModel smartRingHrvModel = myTrackersVm.smartRingHrvHistory[index]; + return InkWell( + onTap: () => Navigator.pop(context), + child: Container( + decoration: cardRadius(12), + padding: EdgeInsets.all(12.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text("Date: ${smartRingHrvModel.date}"), + mHeight(4.0), + Text("highBP: ${smartRingHrvModel.highBP}"), + mHeight(4.0), + Text("lowBP: ${smartRingHrvModel.lowBP}"), + mHeight(4.0), + Text("stress: ${smartRingHrvModel.stress}"), + mHeight(4.0), + Text("heartRate: ${smartRingHrvModel.heartRate}"), + mHeight(4.0), + Text("hrv: ${smartRingHrvModel.hrv}"), + mHeight(4.0), + Text("vascularAging: ${smartRingHrvModel.vascularAging}"), + ], + ), + ), + ); + }, + ), + ); + } else { + return getNoDataWidget(context); + } + } + + Widget buildTotalStepCountHistoryWidget(MyTrackersViewModel myTrackersVm, BuildContext context) { + if (myTrackersVm.filesLoader) { + return Center(child: CircularProgressIndicator()); + } else if (myTrackersVm.smartRingStepCountHistory != null && myTrackersVm.smartRingStepCountHistory.isNotEmpty) { + return Expanded( + child: ListView.separated( + itemCount: myTrackersVm.smartRingStepCountHistory.length, + separatorBuilder: (context, index) => SizedBox(height: 14), + shrinkWrap: true, + reverse: false, + itemBuilder: (context, index) { + SmartRingStepCountModel smartRingStepCountModel = myTrackersVm.smartRingStepCountHistory[index]; + return InkWell( + onTap: () => Navigator.pop(context), + child: Container( + decoration: cardRadius(12), + padding: EdgeInsets.all(12.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text("date: ${smartRingStepCountModel.date}"), + mHeight(4.0), + Text("calories: ${smartRingStepCountModel.calories}"), + mHeight(4.0), + Text("distance: ${smartRingStepCountModel.distance}"), + mHeight(4.0), + Text("exerciseMinutes: ${smartRingStepCountModel.exerciseMinutes}"), + mHeight(4.0), + Text("exerciseTime: ${smartRingStepCountModel.exerciseTime}"), + mHeight(4.0), + Text("distance: ${smartRingStepCountModel.distance}"), + mHeight(4.0), + Text("goal: ${smartRingStepCountModel.goal}"), + mHeight(4.0), + Text("step: ${smartRingStepCountModel.step}"), + ], + ), + ), + ); + }, + ), + ); + } else { + return getNoDataWidget(context); + } + } + + Widget buildBloodOxygenWidget(MyTrackersViewModel myTrackersVm, BuildContext context) { + if (myTrackersVm.filesLoader) { + return Center(child: CircularProgressIndicator()); + } else if (myTrackersVm.smartRingBloodOxygenHistory != null && myTrackersVm.smartRingBloodOxygenHistory.isNotEmpty) { + return Expanded( + child: ListView.separated( + itemCount: myTrackersVm.smartRingBloodOxygenHistory.length, + separatorBuilder: (context, index) => SizedBox(height: 14), + shrinkWrap: true, + reverse: false, + itemBuilder: (context, index) { + SmartRingGenericModel smartRingGenericModel = myTrackersVm.smartRingBloodOxygenHistory[index]; + return InkWell( + onTap: () => Navigator.pop(context), + child: Container( + decoration: cardRadius(12), + padding: EdgeInsets.all(12.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text("Date: ${smartRingGenericModel.date}"), + mHeight(4.0), + Text("BloodOxygen: ${smartRingGenericModel.value}"), + ], + ), + ), + ); + }, + ), + ); + } else { + return getNoDataWidget(context); + } + } + + Widget buildBatteryLevelWidget(MyTrackersViewModel myTrackersVm, BuildContext context) { + if (myTrackersVm.filesLoader) { + return Center(child: CircularProgressIndicator()); + } else if (myTrackersVm.smartRingBatteryLevel != null && myTrackersVm.smartRingBatteryLevel.isNotEmpty) { + return Center( + child: Text("Battery Level: ${myTrackersVm.smartRingBatteryLevel}"), + ); + } else { + return getNoDataWidget(context); + } + } + + @override + Widget build(BuildContext context) { + return AppScaffold( + appBarTitle: "${getTrackerNameByEnum(widget.smartRingOperationsEnum)}", + showNewAppBar: true, + isShowDecPage: false, + showNewAppBarTitle: true, + backgroundColor: Color(0xffF8F8F8), + body: Padding( + padding: const EdgeInsets.all(24.0), + child: Column( + children: [ + Consumer( + builder: (BuildContext context, MyTrackersViewModel myTrackersVm, Widget child) { + return getAllInOneOperationWidgets(myTrackersVm, widget.smartRingOperationsEnum, context); + }, + ), + ], + )), + ); + } +} diff --git a/lib/pages/medical/my_trackers/ble_helpers/ble_connect_helper.dart b/lib/pages/medical/my_trackers/ble_helpers/ble_connect_helper.dart new file mode 100644 index 00000000..5bee3fb0 --- /dev/null +++ b/lib/pages/medical/my_trackers/ble_helpers/ble_connect_helper.dart @@ -0,0 +1,376 @@ +import 'dart:io'; + +import 'package:flutter/services.dart'; + +class BleChannel { + static const platform = MethodChannel('BLE-Platform-Bridge'); + + static const platform_ios_ekg = MethodChannel('BLE-Platform-Bridge-IOS-EKG'); + + // static const platform_ios_ekg = MethodChannel('BLE-Platform-Bridge-IOS-EKG'); + +//BLE-Platform-Bridge + static Future scanCheckMeProNative() async { + try { + String result; + print("----------Flutter scanCheckMeProNative -------"); + + result = await platform.invokeMethod('scanForCheckMePro'); + + print("----------Flutter scanCheckMeProNative Result -------"); + + return result; + } catch (e) { + return "Error: $e"; + } + } + + static Future scanResultsNative(List deviceType) async { + try { + String result; + print("----------Flutter scanResults -------"); + + result = await platform.invokeMethod('scanDevices', deviceType); + + print("----------Flutter Result -------"); + + return result; + } catch (e) { + return "Error: $e"; + } + } + + static Future stopNativeScan() async { + try { + String result; + print("----------Flutter stopNativeScan -------"); + + result = await platform.invokeMethod('stopScan'); + result = await platform.invokeMethod('stopScanCheckMe'); + + print("----------Flutter Result stopNativeScan -------"); + + return result; + } catch (e) { + return "Error: $e"; + } + } + + static Future connectDevice(List device) async { + try { + String result; + print("----------Flutter init connectDevice -------"); + + result = await platform.invokeMethod('connectDevice', device); + + print("----------Flutter Result connectDevice -------"); + + return result; + } catch (e) { + return "Error: $e"; + } + } + + static Future connectDeviceCheckMe(List device) async { + try { + String result; + print("----------Flutter init connectDeviceCheckMe -------"); + + result = await platform.invokeMethod('connectDeviceCheckMe', device); + + print("----------Flutter Result connectDeviceCheckMe -------"); + + return result; + } catch (e) { + return "Error: $e"; + } + } + + static Future getECGFilesList(List device) async { + try { + print("----------Flutter getECGFilesList -------"); + final String result = await platform.invokeMethod('ecgFilesList', device); + print("----------Flutter Result -------"); + + return result; + } catch (e) { + return "Error: $e"; + } + } + + static Future factoryResetECG(List device) async { + try { + print("----------Flutter duoEkFactoryReset -------"); + final String result = await platform.invokeMethod('factoryResetECG', device); + print("----------Flutter Result -------"); + + return result; + } catch (e) { + return "Error: $e"; + } + } + + static Future getBP2FilesList(List deviceType) async { + try { + print("----------Flutter getBP2FilesList -------"); + final String result = await platform.invokeMethod('bp2FilesList', deviceType); + print("----------Flutter Result -------"); + + return result; + } catch (e) { + return "Error: $e"; + } + } + + static Future disconnect() async { + try { + print("----------Flutter disconnect -------"); + final String result = await platform.invokeMethod('disconnectDevice'); + final String result2 = await platform.invokeMethod('disconnectCheckMe'); + print("----------Flutter Result -------"); + + return result; + } catch (e) { + return "Error: $e"; + } + } + + static Future getDeviceInfoCheckMePro() async { + try { + print("----------Flutter getDeviceInfoCheckMePro -------"); + final String result = await platform.invokeMethod('getDeviceInfoCheckMePro'); + print("----------Flutter getDeviceInfoCheckMePro result -------"); + + return result; + } catch (e) { + return "Error: $e"; + } + } + + static Future getGlucoseDataFromCheckMePro(String userId) async { + try { + print("----------Flutter getDeviceInfoCheckMePro -------"); + final String result = await platform.invokeMethod('getGlucoseDataFromCheckMePro', userId); + print("----------Flutter getDeviceInfoCheckMePro result -------"); + + return result; + } catch (e) { + return "Error: $e"; + } + } + + static Future getBPDataFromCheckMePro(String userId) async { + try { + print("----------Flutter getBPDataFromCheckMePro -------"); + final String result = await platform.invokeMethod('getBPDataFromCheckMePro', userId); + print("----------Flutter getBPDataFromCheckMePro result -------"); + + return result; + } catch (e) { + return "Error: $e"; + } + } + + static Future getOxiDataFromCheckMePro() async { + try { + print("----------Flutter getOxiDataFromCheckMePro -------"); + final String result = await platform.invokeMethod('getOxiDataFromCheckMePro'); + print("----------Flutter getOxiDataFromCheckMePro result -------"); + + return result; + } catch (e) { + return "Error: $e"; + } + } + + static Future getTempDataFromCheckMePro() async { + try { + print("----------Flutter getTempDataFromCheckMePro -------"); + final String result = await platform.invokeMethod('getTempDataFromCheckMePro'); + print("----------Flutter getTempDataFromCheckMePro result -------"); + + return result; + } catch (e) { + return "Error: $e"; + } + } + + static Future getEcgDataFromCheckMePro() async { + try { + print("----------Flutter getEcgDataFromCheckMePro -------"); + final String result = await platform.invokeMethod('getEcgDataFromCheckMePro'); + print("----------Flutter getEcgDataFromCheckMePro result -------"); + + return result; + } catch (e) { + return "Error: $e"; + } + } + + static Future getEcgWaveDataFromCheckMePro(String timeString) async { + try { + print("----------Flutter getEcgWaveFromCheckMePro -------"); + final String result = await platform.invokeMethod('getEcgWaveFromCheckMePro', timeString); + print("----------Flutter getEcgWaveFromCheckMePro result -------"); + + return result; + } catch (e) { + return "Error: $e"; + } + } + + // SMART RING SERVICES + + static Future scanSmartRingNative() async { + try { + String result; + print("----------Flutter scanForSmartRing -------"); + + result = await platform.invokeMethod('scanForSmartRing'); + + print("----------Flutter scanForSmartRing Result -------"); + + return result; + } catch (e) { + return "Error: $e"; + } + } + + static Future connectDeviceSmartRing(String deviceAddress) async { + try { + String result; + print("----------Flutter init connectDeviceSmartRing -------"); + + result = await platform.invokeMethod('connectDeviceSmartRing', deviceAddress); + + print("----------Flutter Result connectDeviceSmartRing -------"); + + return result; + } catch (e) { + return "Error: $e"; + } + } + + static Future disConnectDeviceSmartRing() async { + try { + String result; + print("----------Flutter init disConnectDeviceSmartRing -------"); + + result = await platform.invokeMethod('disConnectDeviceSmartRing'); + + print("----------Flutter Result disConnectDeviceSmartRing -------"); + + return result; + } catch (e) { + return "Error: $e"; + } + } + + static Future getBatteryLevelSmartRing() async { + try { + print("----------Flutter getBatteryLevelSmartRing -------"); + final String result = await platform.invokeMethod('getBatteryLevelSmartRing'); + print("----------Flutter getBatteryLevelSmartRing result -------"); + + return result; + } catch (e) { + return "Error: $e"; + } + } + + static Future getTemperatureSmartRing() async { + try { + print("----------Flutter getTemperatureSmartRing -------"); + final String result = await platform.invokeMethod('getTemperatureSmartRing'); + print("----------Flutter getTemperatureSmartRing result -------"); + + return result; + } catch (e) { + return "Error: $e"; + } + } + + static Future getDynamicHeartRateSmartRing() async { + try { + print("----------Flutter getDynamicHeartRateSmartRing -------"); + final String result = await platform.invokeMethod('getDynamicHeartRateSmartRing'); + print("----------Flutter getDynamicHeartRateSmartRing result -------"); + + return result; + } catch (e) { + return "Error: $e"; + } + } + + static Future getStaticHeartRateSmartRing() async { + try { + print("----------Flutter getStaticHeartRateSmartRing -------"); + final String result = await platform.invokeMethod('getStaticHeartRateSmartRing'); + print("----------Flutter getStaticHeartRateSmartRing result -------"); + + return result; + } catch (e) { + return "Error: $e"; + } + } + + static Future getHRVSmartRing() async { + try { + print("----------Flutter getHRVSmartRing -------"); + final String result = await platform.invokeMethod('getHRVSmartRing'); + print("----------Flutter getHRVSmartRing result -------"); + + return result; + } catch (e) { + return "Error: $e"; + } + } + + static Future getBloodOxygenSmartRing() async { + try { + print("----------Flutter getBloodOxygenSmartRing -------"); + final String result = await platform.invokeMethod('getBloodOxygenSmartRing'); + print("----------Flutter getBloodOxygenSmartRing result -------"); + + return result; + } catch (e) { + return "Error: $e"; + } + } + + static Future getTotalStepCountSmartRing() async { + try { + print("----------Flutter getTotalStepCountSmartRing -------"); + final String result = await platform.invokeMethod('getTotalStepCountSmartRing'); + print("----------Flutter getTotalStepCountSmartRing result -------"); + + return result; + } catch (e) { + return "Error: $e"; + } + } + + static Future startSportsModeDataSmartRing() async { + try { + print("----------Flutter getSportsModeDataSmartRing -------"); + final String result = await platform.invokeMethod('getSportsModeDataSmartRing'); + print("----------Flutter getSportsModeDataSmartRing result -------"); + + return result; + } catch (e) { + return "Error: $e"; + } + } + + static Future stopSportsModeDataSmartRing() async { + try { + print("----------Flutter stopSportsModeDataSmartRing -------"); + final String result = await platform.invokeMethod('stopSportsModeDataSmartRing'); + print("----------Flutter stopSportsModeDataSmartRing result -------"); + + return result; + } catch (e) { + return "Error: $e"; + } + } +} diff --git a/lib/pages/medical/my_trackers/ble_models/andesfit_devices/bt_constants.dart b/lib/pages/medical/my_trackers/ble_models/andesfit_devices/bt_constants.dart new file mode 100644 index 00000000..90357cb1 --- /dev/null +++ b/lib/pages/medical/my_trackers/ble_models/andesfit_devices/bt_constants.dart @@ -0,0 +1,265 @@ +class BTConstants { + static final int CMD_WORD_START_READ = 0x03; + static final int CMD_WORD_READ_CONTENT = 0x04; + static final int CMD_WORD_END_READ = 0x05; + static final int COMMON_PKG_LENGTH = 8; + + static final List 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, + ]; +} diff --git a/lib/pages/medical/my_trackers/ble_models/andesfit_devices/weight_data_model.dart b/lib/pages/medical/my_trackers/ble_models/andesfit_devices/weight_data_model.dart new file mode 100644 index 00000000..c89e339c --- /dev/null +++ b/lib/pages/medical/my_trackers/ble_models/andesfit_devices/weight_data_model.dart @@ -0,0 +1,11 @@ +class WeightScaleData { + String weight; + String bmi; + String fat; + String bone; + String water; + String muscle; + String bmr; + + WeightScaleData({this.weight, this.bmi, this.fat, this.bone, this.water, this.muscle, this.bmr}); +} diff --git a/lib/pages/medical/my_trackers/ble_models/ble_devices_model.dart b/lib/pages/medical/my_trackers/ble_models/ble_devices_model.dart new file mode 100644 index 00000000..86e403a9 --- /dev/null +++ b/lib/pages/medical/my_trackers/ble_models/ble_devices_model.dart @@ -0,0 +1,27 @@ +import 'package:diplomaticquarterapp/pages/medical/my_trackers/my_trackers_view_model/my_trackers_view_model.dart'; +import 'package:flutter_blue_plus/flutter_blue_plus.dart'; + +class BleDeviceModel { + String macAddr; + int model; + String name; + int rssi; + TrackerTypeEnum deviceType; + BluetoothDevice andesfitBluetoothDevice; + + BleDeviceModel({this.macAddr, this.model, this.name, this.rssi, this.andesfitBluetoothDevice}); + + BleDeviceModel.fromJson(Map json, TrackerTypeEnum trackerType) { + macAddr = json['macAddr']; + model = json['model']; + name = json['name']; + rssi = json['rssi']; + deviceType = trackerType; + andesfitBluetoothDevice = null; + } + + @override + String toString() { + return 'BleDeviceModel{macAddr: $macAddr, model: $model, name: $name, rssi: $rssi, deviceType: $deviceType, andesfitBluetoothDevice: ${andesfitBluetoothDevice.toString()}'; + } +} diff --git a/lib/pages/medical/my_trackers/ble_models/checkmepro_all_in_one_models/check_me_info_model.dart b/lib/pages/medical/my_trackers/ble_models/checkmepro_all_in_one_models/check_me_info_model.dart new file mode 100644 index 00000000..6d625ff1 --- /dev/null +++ b/lib/pages/medical/my_trackers/ble_models/checkmepro_all_in_one_models/check_me_info_model.dart @@ -0,0 +1,61 @@ +class CheckMeInfoModel { + String region; + String model; + String hardwareVer; + String softwareVer; + String languageVer; + String curLanguage; + String fileVer; + String sPCPVer; + String application; + String sN; + String branchCode; + + CheckMeInfoModel( + {this.region, + this.model, + this.hardwareVer, + this.softwareVer, + this.languageVer, + this.curLanguage, + this.fileVer, + this.sPCPVer, + this.application, + this.sN, + this.branchCode}); + + @override + String toString() { + return 'CheckMeInfoModel{region: $region, model: $model, hardwareVer: $hardwareVer, softwareVer: $softwareVer, languageVer: $languageVer, curLanguage: $curLanguage, fileVer: $fileVer, sPCPVer: $sPCPVer, application: $application, sN: $sN, branchCode: $branchCode}'; + } + + CheckMeInfoModel.fromJson(Map json) { + region = json['Region']; + model = json['Model']; + hardwareVer = json['HardwareVer']; + softwareVer = json['SoftwareVer']; + languageVer = json['LanguageVer']; + curLanguage = json['CurLanguage']; + fileVer = json['FileVer']; + sPCPVer = json['SPCPVer']; + application = json['Application']; + sN = json['SN']; + branchCode = json['BranchCode']; + } + + Map toJson() { + final Map data = new Map(); + data['Region'] = this.region; + data['Model'] = this.model; + data['HardwareVer'] = this.hardwareVer; + data['SoftwareVer'] = this.softwareVer; + data['LanguageVer'] = this.languageVer; + data['CurLanguage'] = this.curLanguage; + data['FileVer'] = this.fileVer; + data['SPCPVer'] = this.sPCPVer; + data['Application'] = this.application; + data['SN'] = this.sN; + data['BranchCode'] = this.branchCode; + return data; + } +} diff --git a/lib/pages/medical/my_trackers/ble_models/checkmepro_all_in_one_models/checkme_bp_model.dart b/lib/pages/medical/my_trackers/ble_models/checkmepro_all_in_one_models/checkme_bp_model.dart new file mode 100644 index 00000000..0dc05d99 --- /dev/null +++ b/lib/pages/medical/my_trackers/ble_models/checkmepro_all_in_one_models/checkme_bp_model.dart @@ -0,0 +1,27 @@ +class CheckMeBPModel { + String date; + int dia; + int pr; + int sys; + String timeString; + + CheckMeBPModel({this.date, this.dia, this.pr, this.sys, this.timeString}); + + CheckMeBPModel.fromJson(Map json) { + date = json['date']; + dia = json['dia']; + pr = json['pr']; + sys = json['sys']; + timeString = json['timeString']; + } + + Map toJson() { + final Map data = new Map(); + data['date'] = this.date; + data['dia'] = this.dia; + data['pr'] = this.pr; + data['sys'] = this.sys; + data['timeString'] = this.timeString; + return data; + } +} diff --git a/lib/pages/medical/my_trackers/ble_models/checkmepro_all_in_one_models/checkme_ecg_model.dart b/lib/pages/medical/my_trackers/ble_models/checkmepro_all_in_one_models/checkme_ecg_model.dart new file mode 100644 index 00000000..3a57e762 --- /dev/null +++ b/lib/pages/medical/my_trackers/ble_models/checkmepro_all_in_one_models/checkme_ecg_model.dart @@ -0,0 +1,27 @@ +class CheckMeECGModel { + String date; + int face; + String timeString; + int voice; + int way; + + CheckMeECGModel({this.date, this.face, this.timeString, this.voice, this.way}); + + CheckMeECGModel.fromJson(Map json) { + date = json['date']; + face = json['face']; + timeString = json['timeString']; + voice = json['voice']; + way = json['way']; + } + + Map toJson() { + final Map data = new Map(); + data['date'] = this.date; + data['face'] = this.face; + data['timeString'] = this.timeString; + data['voice'] = this.voice; + data['way'] = this.way; + return data; + } +} diff --git a/lib/pages/medical/my_trackers/ble_models/checkmepro_all_in_one_models/checkme_ecg_wave_model.dart b/lib/pages/medical/my_trackers/ble_models/checkmepro_all_in_one_models/checkme_ecg_wave_model.dart new file mode 100644 index 00000000..6f763f1a --- /dev/null +++ b/lib/pages/medical/my_trackers/ble_models/checkmepro_all_in_one_models/checkme_ecg_wave_model.dart @@ -0,0 +1,58 @@ +class CheckMeECGWaveModel { + List bytes; + int hr; + List hrList; + int hrSize; + int pvcs; + int qrs; + int qt; + int qtc; + int result; + int st; + int total; + int waveIntSize; + List waveList; + int waveSize; + int waveViewSize; + + CheckMeECGWaveModel( + {this.bytes, this.hr, this.hrList, this.hrSize, this.pvcs, this.qrs, this.qt, this.qtc, this.result, this.st, this.total, this.waveIntSize, this.waveList, this.waveSize, this.waveViewSize}); + + CheckMeECGWaveModel.fromJson(Map json) { + bytes = json['bytes'].cast(); + hr = json['hr']; + hrList = json['hrList'].cast(); + hrSize = json['hrSize']; + pvcs = json['pvcs']; + qrs = json['qrs']; + qt = json['qt']; + qtc = json['qtc']; + result = json['result']; + st = json['st']; + total = json['total']; + waveIntSize = json['waveIntSize']; + waveList = json['waveList'].cast(); + waveSize = json['waveSize']; + waveViewSize = json['waveViewSize']; + } + + Map toJson() { + final Map data = new Map(); + data['bytes'] = this.bytes; + data['hr'] = this.hr; + data['hrList'] = this.hrList; + data['hrSize'] = this.hrSize; + data['pvcs'] = this.pvcs; + data['qrs'] = this.qrs; + data['qt'] = this.qt; + data['qtc'] = this.qtc; + data['result'] = this.result; + data['st'] = this.st; + data['total'] = this.total; + data['waveIntSize'] = this.waveIntSize; + data['waveList'] = this.waveList; + data['waveSize'] = this.waveSize; + data['waveViewSize'] = this.waveViewSize; + return data; + } +} diff --git a/lib/pages/medical/my_trackers/ble_models/checkmepro_all_in_one_models/checkme_glucose_model.dart b/lib/pages/medical/my_trackers/ble_models/checkmepro_all_in_one_models/checkme_glucose_model.dart new file mode 100644 index 00000000..6b5d6797 --- /dev/null +++ b/lib/pages/medical/my_trackers/ble_models/checkmepro_all_in_one_models/checkme_glucose_model.dart @@ -0,0 +1,24 @@ +class CheckMeGlucoseModel { + String date; + double glu; + String note; + String timeString; + + CheckMeGlucoseModel({this.date, this.glu, this.note, this.timeString}); + + CheckMeGlucoseModel.fromJson(Map json) { + date = json['date']; + glu = json['glu']; + note = json['note']; + timeString = json['timeString']; + } + + Map toJson() { + final Map data = new Map(); + data['date'] = this.date; + data['glu'] = this.glu; + data['note'] = this.note; + data['timeString'] = this.timeString; + return data; + } +} diff --git a/lib/pages/medical/my_trackers/ble_models/checkmepro_all_in_one_models/checkme_oxi_model.dart b/lib/pages/medical/my_trackers/ble_models/checkmepro_all_in_one_models/checkme_oxi_model.dart new file mode 100644 index 00000000..5167e7ae --- /dev/null +++ b/lib/pages/medical/my_trackers/ble_models/checkmepro_all_in_one_models/checkme_oxi_model.dart @@ -0,0 +1,36 @@ +class CheckMeOxiModel { + String date; + int face; + int oxy; + num pi; + int pr; + String timeString; + int way; + + CheckMeOxiModel({this.date, this.face, this.oxy, this.pi, this.pr, this.timeString, this.way}); + + CheckMeOxiModel.fromJson(Map json) { + date = json['date']; + face = json['face']; + oxy = json['oxy']; + pi = json['pi']; + pr = json['pr']; + timeString = json['timeString']; + way = json['way']; + } + + Map toJson() { + final Map data = new Map(); + data['date'] = this.date; + data['face'] = this.face; + data['oxy'] = this.oxy; + data['pi'] = this.pi; + data['pr'] = this.pr; + data['timeString'] = this.timeString; + data['way'] = this.way; + return data; + } +} + + + diff --git a/lib/pages/medical/my_trackers/ble_models/checkmepro_all_in_one_models/checkme_temperature_model.dart b/lib/pages/medical/my_trackers/ble_models/checkmepro_all_in_one_models/checkme_temperature_model.dart new file mode 100644 index 00000000..90265fe9 --- /dev/null +++ b/lib/pages/medical/my_trackers/ble_models/checkmepro_all_in_one_models/checkme_temperature_model.dart @@ -0,0 +1,29 @@ +class CheckMeTemperatureModel { + String date; + int face; + String timeString; + double tmp; + int way; + + CheckMeTemperatureModel({this.date, this.face, this.timeString, this.tmp, this.way}); + + CheckMeTemperatureModel.fromJson(Map json) { + date = json['date']; + face = json['face']; + timeString = json['timeString']; + tmp = json['tmp']; + way = json['way']; + } + + Map toJson() { + final Map data = new Map(); + data['date'] = this.date; + data['face'] = this.face; + data['timeString'] = this.timeString; + data['tmp'] = this.tmp; + data['way'] = this.way; + return data; + } +} + + diff --git a/lib/pages/medical/my_trackers/ble_models/checkmepro_all_in_one_models/checkme_user_model.dart b/lib/pages/medical/my_trackers/ble_models/checkmepro_all_in_one_models/checkme_user_model.dart new file mode 100644 index 00000000..faf377fc --- /dev/null +++ b/lib/pages/medical/my_trackers/ble_models/checkmepro_all_in_one_models/checkme_user_model.dart @@ -0,0 +1,36 @@ +class CheckMeUserInfoModel { + String birthday; + int color; + int height; + int ico; + String id; + String name; + int sex; + int weight; + + CheckMeUserInfoModel({this.birthday, this.color, this.height, this.ico, this.id, this.name, this.sex, this.weight}); + + CheckMeUserInfoModel.fromJson(Map json) { + birthday = json['birthday']; + color = json['color']; + height = json['height']; + ico = json['ico']; + id = json['id']; + name = json['name']; + sex = json['sex']; + weight = json['weight']; + } + + Map toJson() { + final Map data = new Map(); + data['birthday'] = this.birthday; + data['color'] = this.color; + data['height'] = this.height; + data['ico'] = this.ico; + data['id'] = this.id; + data['name'] = this.name; + data['sex'] = this.sex; + data['weight'] = this.weight; + return data; + } +} diff --git a/lib/pages/medical/my_trackers/ble_models/smart_ring_models.dart b/lib/pages/medical/my_trackers/ble_models/smart_ring_models.dart new file mode 100644 index 00000000..03380caa --- /dev/null +++ b/lib/pages/medical/my_trackers/ble_models/smart_ring_models.dart @@ -0,0 +1,133 @@ +//TEMPERATURE + +import 'package:diplomaticquarterapp/pages/medical/my_trackers/my_trackers_view_model/my_trackers_view_model.dart'; + +String getValueKeyBySmartRingOperation(SmartRingOperationsEnum smartRingOperationsEnum) { + switch (smartRingOperationsEnum) { + case SmartRingOperationsEnum.Temperature: + return "temperature"; + case SmartRingOperationsEnum.HeartRate: + return "onceHeartValue"; + case SmartRingOperationsEnum.RealTimeHeartRate: + return "arrayDynamicHR"; + case SmartRingOperationsEnum.HRV: + break; + case SmartRingOperationsEnum.BloodOxygen: + return "Blood_oxygen"; + case SmartRingOperationsEnum.BatteryLevel: + return "batteryLevel"; + } + return "batteryLevel"; +} + +class SmartRingGenericModel { + DateTime date; + String value; + + SmartRingGenericModel({this.date, this.value}); + + SmartRingGenericModel.fromJson(Map json, SmartRingOperationsEnum smartRingOperationsEnum) { + date = json['date'] != null ? DateTime.parse(((json["date"] as String).replaceAll(".", "-"))) : DateTime.now(); + value = json['${getValueKeyBySmartRingOperation(smartRingOperationsEnum)}']; + } + + Map toJson(SmartRingOperationsEnum smartRingOperationsEnum) { + final Map data = new Map(); + data['date'] = this.date; + data['${getValueKeyBySmartRingOperation(smartRingOperationsEnum)}'] = this.value; + return data; + } +} + +class SmartRingStepCountModel { + String date; + String goal; + String distance; + String step; + String exerciseTime; + String calories; + String exerciseMinutes; + + SmartRingStepCountModel({this.date, this.goal, this.distance, this.step, this.exerciseTime, this.calories, this.exerciseMinutes}); + + SmartRingStepCountModel.fromJson(Map json) { + date = json['date']; + goal = json['goal']; + distance = json['distance']; + step = json['step']; + exerciseTime = json['ExerciseTime']; + calories = json['calories']; + exerciseMinutes = json['exerciseMinutes']; + } + + Map toJson() { + final Map data = new Map(); + data['date'] = this.date; + data['goal'] = this.goal; + data['distance'] = this.distance; + data['step'] = this.step; + data['ExerciseTime'] = this.exerciseTime; + data['calories'] = this.calories; + data['exerciseMinutes'] = this.exerciseMinutes; + return data; + } +} + +class SmartRingHrvModel { + String date; + String highBP; + String stress; + String lowBP; + String heartRate; + String hrv; + String vascularAging; + + SmartRingHrvModel({this.date, this.highBP, this.stress, this.lowBP, this.heartRate, this.hrv, this.vascularAging}); + + SmartRingHrvModel.fromJson(Map json) { + date = json['date']; + highBP = json['highBP']; + stress = json['stress']; + lowBP = json['lowBP']; + heartRate = json['heartRate']; + hrv = json['hrv']; + vascularAging = json['vascularAging']; + } + + Map toJson() { + final Map data = new Map(); + data['date'] = this.date; + data['highBP'] = this.highBP; + data['stress'] = this.stress; + data['lowBP'] = this.lowBP; + data['heartRate'] = this.heartRate; + data['hrv'] = this.hrv; + data['vascularAging'] = this.vascularAging; + return data; + } +} + +class RtSportDataSmartRingModel { + String heartRate; + String step; + String exerciseTime; + String calories; + + RtSportDataSmartRingModel({this.heartRate, this.step, this.exerciseTime, this.calories}); + + RtSportDataSmartRingModel.fromJson(Map json) { + heartRate = json['heartRate']; + step = json['step']; + exerciseTime = json['ExerciseTime']; + calories = json['calories']; + } + + Map toJson() { + final Map data = new Map(); + data['heartRate'] = this.heartRate; + data['step'] = this.step; + data['ExerciseTime'] = this.exerciseTime; + data['calories'] = this.calories; + return data; + } +} diff --git a/lib/pages/medical/my_trackers/ble_models/viatom_devices/bp_rt_model.dart b/lib/pages/medical/my_trackers/ble_models/viatom_devices/bp_rt_model.dart new file mode 100644 index 00000000..dbd6a4c4 --- /dev/null +++ b/lib/pages/medical/my_trackers/ble_models/viatom_devices/bp_rt_model.dart @@ -0,0 +1,143 @@ +class BpRtMeasuringModel { + bool deflate; + int pr; + int pressure; + bool pulse; + + BpRtMeasuringModel({this.deflate, this.pr, this.pressure, this.pulse}); + + BpRtMeasuringModel.fromJson(Map json) { + deflate = json['deflate']; + pr = json['pr']; + pressure = json['pressure']; + pulse = json['pulse']; + } + + Map toJson() { + final Map data = new Map(); + data['deflate'] = this.deflate; + data['pr'] = this.pr; + data['pressure'] = this.pressure; + data['pulse'] = this.pulse; + return data; + } +} + +class BpRtResultModel { + bool deflate; + int dia; + int mean; + int pr; + int pressure; + int result; + int sys; + bool isBpHigh; + bool isPulseHigh; + + BpRtResultModel({ + this.deflate, + this.dia, + this.mean, + this.pr, + this.pressure, + this.result, + this.sys, + this.isBpHigh, + this.isPulseHigh, + }); + + BpRtResultModel.fromJson(Map json) { + deflate = json['deflate']; + dia = json['dia']; + mean = json['mean']; + pr = json['pr']; + pressure = json['pressure']; + result = json['result']; + sys = json['sys']; + isBpHigh = false; + isPulseHigh = false; + } +} + +class ECGRtMeasuringModelFromBP { + int curDuration; + int hr; + bool leadOff; + bool poolSignal; + + ECGRtMeasuringModelFromBP({this.curDuration, this.hr, this.leadOff, this.poolSignal}); + + ECGRtMeasuringModelFromBP.fromJson(Map json) { + curDuration = json['curDuration']; + hr = json['hr']; + leadOff = json['leadOff']; + poolSignal = json['poolSignal']; + } +} + +class ECGRtResultModelFromBP { + Diagnosis diagnosis; + int hr; + int pvcs; + int qrs; + int qtc; + int result; + + ECGRtResultModelFromBP({this.diagnosis, this.hr, this.pvcs, this.qrs, this.qtc, this.result}); + + ECGRtResultModelFromBP.fromJson(Map json) { + diagnosis = json['diagnosis'] != null ? new Diagnosis.fromJson(json['diagnosis']) : null; + hr = json['hr']; + pvcs = json['pvcs']; + qrs = json['qrs']; + qtc = json['qtc']; + result = json['result']; + } +} + +class Diagnosis { + List bytes; + bool isFastHr; + bool isFibrillation; + bool isHeartPause; + bool isIrregular; + bool isLeadOff; + bool isPoorSignal; + bool isProlongedQtc; + bool isPvcs; + bool isRegular; + bool isShortQtc; + bool isSlowHr; + bool isWideQrs; + + Diagnosis( + {this.bytes, + this.isFastHr, + this.isFibrillation, + this.isHeartPause, + this.isIrregular, + this.isLeadOff, + this.isPoorSignal, + this.isProlongedQtc, + this.isPvcs, + this.isRegular, + this.isShortQtc, + this.isSlowHr, + this.isWideQrs}); + + Diagnosis.fromJson(Map json) { + bytes = json['bytes'].cast(); + isFastHr = json['isFastHr']; + isFibrillation = json['isFibrillation']; + isHeartPause = json['isHeartPause']; + isIrregular = json['isIrregular']; + isLeadOff = json['isLeadOff']; + isPoorSignal = json['isPoorSignal']; + isProlongedQtc = json['isProlongedQtc']; + isPvcs = json['isPvcs']; + isRegular = json['isRegular']; + isShortQtc = json['isShortQtc']; + isSlowHr = json['isSlowHr']; + isWideQrs = json['isWideQrs']; + } +} diff --git a/lib/pages/medical/my_trackers/ble_models/viatom_devices/ecg_file_detail_model.dart b/lib/pages/medical/my_trackers/ble_models/viatom_devices/ecg_file_detail_model.dart new file mode 100644 index 00000000..17989e63 --- /dev/null +++ b/lib/pages/medical/my_trackers/ble_models/viatom_devices/ecg_file_detail_model.dart @@ -0,0 +1,25 @@ +class ECGFileDetailModel { + int duration; + String fileName; + List shortData; + int startTime; + + ECGFileDetailModel( + {this.duration, this.fileName, this.shortData, this.startTime}); + + ECGFileDetailModel.fromJson(Map json) { + duration = json['duration']; + fileName = json['fileName']; + shortData = json['shortData'].cast(); + startTime = json['startTime']; + } + + Map toJson() { + final Map data = new Map(); + data['duration'] = this.duration; + data['fileName'] = this.fileName; + data['shortData'] = this.shortData; + data['startTime'] = this.startTime; + return data; + } +} diff --git a/lib/pages/medical/my_trackers/ble_models/viatom_devices/ecg_rt_model.dart b/lib/pages/medical/my_trackers/ble_models/viatom_devices/ecg_rt_model.dart new file mode 100644 index 00000000..d4433f3d --- /dev/null +++ b/lib/pages/medical/my_trackers/ble_models/viatom_devices/ecg_rt_model.dart @@ -0,0 +1,78 @@ +class ECGRtModel { + Param param; + Wave wave; + + ECGRtModel({this.param, this.wave}); + + ECGRtModel.fromJson(Map json) { + param = json['param'] != null ? new Param.fromJson(json['param']) : null; + wave = json['wave'] != null ? new Wave.fromJson(json['wave']) : null; + } + + Map toJson() { + final Map data = new Map(); + if (this.param != null) { + data['param'] = this.param.toJson(); + } + if (this.wave != null) { + data['wave'] = this.wave.toJson(); + } + return data; + } +} + +class Param { + num battery; + num batteryState; + num curStatus; + num hr; + bool poorSignal; + bool rSignal; + num recordTime; + + Param({this.battery, this.batteryState, this.curStatus, this.hr, this.poorSignal, this.rSignal, this.recordTime}); + + Param.fromJson(Map json) { + battery = json['battery']; + batteryState = json['batteryState']; + curStatus = json['curStatus']; + hr = json['hr']; + poorSignal = json['poorSignal']; + rSignal = json['rSignal']; + recordTime = json['recordTime']; + } + + Map toJson() { + final Map data = new Map(); + data['battery'] = this.battery; + data['batteryState'] = this.batteryState; + data['curStatus'] = this.curStatus; + data['hr'] = this.hr; + data['poorSignal'] = this.poorSignal; + data['rSignal'] = this.rSignal; + data['recordTime'] = this.recordTime; + return data; + } +} + +class Wave { + List ecgBytes; + List ecgFloats; + List ecgShorts; + + Wave({this.ecgBytes, this.ecgFloats, this.ecgShorts}); + + Wave.fromJson(Map json) { + ecgBytes = json['ecgBytes'].cast(); + ecgFloats = json['ecgFloats'].cast(); + ecgShorts = json['ecgShorts'].cast(); + } + + Map toJson() { + final Map data = new Map(); + data['ecgBytes'] = this.ecgBytes; + data['ecgFloats'] = this.ecgFloats; + data['ecgShorts'] = this.ecgShorts; + return data; + } +} diff --git a/lib/pages/medical/my_trackers/ble_models/viatom_devices/oxy_rt_model.dart b/lib/pages/medical/my_trackers/ble_models/viatom_devices/oxy_rt_model.dart new file mode 100644 index 00000000..5cc32528 --- /dev/null +++ b/lib/pages/medical/my_trackers/ble_models/viatom_devices/oxy_rt_model.dart @@ -0,0 +1,33 @@ +class OxyRtModel { + int battery; + bool isCheckProbe; + bool isProbeOff; + bool isPulseSearching; + num pi; + int pr; + int spo2; + + OxyRtModel({this.battery, this.isCheckProbe, this.isProbeOff, this.isPulseSearching, this.pi, this.pr, this.spo2}); + + OxyRtModel.fromJson(Map json) { + battery = json['battery']; + isCheckProbe = json['isCheckProbe']; + isProbeOff = json['isProbeOff']; + isPulseSearching = json['isPulseSearching']; + pi = json['pi']; + pr = json['pr']; + spo2 = json['spo2']; + } + + Map toJson() { + final Map data = new Map(); + data['battery'] = this.battery; + data['isCheckProbe'] = this.isCheckProbe; + data['isProbeOff'] = this.isProbeOff; + data['isPulseSearching'] = this.isPulseSearching; + data['pi'] = this.pi; + data['pr'] = this.pr; + data['spo2'] = this.spo2; + return data; + } +} diff --git a/lib/pages/medical/my_trackers/blood_glucose_ble.dart b/lib/pages/medical/my_trackers/blood_glucose_ble.dart new file mode 100644 index 00000000..66df5fe4 --- /dev/null +++ b/lib/pages/medical/my_trackers/blood_glucose_ble.dart @@ -0,0 +1,171 @@ +import 'dart:async'; + +import 'package:diplomaticquarterapp/theme/colors.dart'; +import 'package:diplomaticquarterapp/uitl/ble_utils.dart'; +import 'package:diplomaticquarterapp/uitl/bluetooth_off.dart'; +import 'package:diplomaticquarterapp/uitl/translations_delegate_base.dart'; +import 'package:diplomaticquarterapp/widgets/buttons/defaultButton.dart'; +import 'package:diplomaticquarterapp/widgets/others/app_scaffold_widget.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_blue_plus/flutter_blue_plus.dart'; +import 'package:permission_handler/permission_handler.dart'; + +class BloodGlucoseBLE extends StatefulWidget { + @override + State createState() => _BloodGlucoseBLEState(); +} + +class _BloodGlucoseBLEState extends State { + String connectionStatus = "disconnected"; + String currentBloodGlucose = "0.0"; + + BluetoothDevice currentConnectedDevice; + + StreamSubscription bleDevicesStream; + StreamSubscription bloodGlucoseValuesStream; + + bool isDataMeasured = false; + + @override + void dispose() { + super.dispose(); + if (bleDevicesStream != null) bleDevicesStream.cancel(); + if (bloodGlucoseValuesStream != null) bloodGlucoseValuesStream.cancel(); + if (currentConnectedDevice != null) currentConnectedDevice.disconnect(); + } + + @override + Widget build(BuildContext context) { + return AppScaffold( + appBarTitle: TranslationBase.of(context).bloodSugar, + showNewAppBar: true, + isShowDecPage: true, + showNewAppBarTitle: true, + backgroundColor: Color(0xffF8F8F8), + body: SingleChildScrollView( + child: StreamBuilder( + stream: FlutterBluePlus.adapterState, + initialData: BluetoothAdapterState.unknown, + builder: (c, snapshot) { + final adapterState = snapshot.data; + if (adapterState == BluetoothAdapterState.on) { + return Container( + margin: EdgeInsets.only(top: 200.0, left: 50.0, right: 50.0), + child: Column( + children: [ + Center( + child: DefaultButton( + TranslationBase.of(context).start.toUpperCase(), + () { + checkBLEPermissions(); + }, + color: CustomColors.green, + ), + ), + SizedBox( + height: 50.0, + ), + Text("Connection state: $connectionStatus"), + SizedBox( + height: 50.0, + ), + Text("Current Temp: $currentBloodGlucose" + " mg/dL"), + ], + ), + ); + } else { + FlutterBluePlus.stopScan(); + return SizedBox(height: 300.0, child: BluetoothOffScreen(adapterState: adapterState)); + } + }), + ), + ); + } + + void checkBLEPermissions() { + [Permission.location, Permission.storage, Permission.bluetooth, Permission.bluetoothConnect, Permission.bluetoothScan].request().then((status) { + startBLEConnection(); + }); + } + + void startBLEConnection() { + if (FlutterBluePlus.isScanningNow == false) { + setState(() { + connectionStatus = "Connecting..."; + }); + + bleDevicesStream = FlutterBluePlus.scanResults.listen( + (results) { + List blueToothDevices = results; + blueToothDevices.forEach((element) async { + if (element.device.localName.isNotEmpty) { + if (element.device.localName.toLowerCase() == "samico gl") { + element.device.connectionState.listen((BluetoothConnectionState state) async { + setState(() { + connectionStatus = state.toString(); + }); + if (state == BluetoothConnectionState.disconnected) { + // typically, start a periodic timer that tries to periodically reconnect. + // Note: you must always re-discover services after disconnection! + } + if (state == BluetoothConnectionState.connected) { + currentConnectedDevice = element.device; + bleDevicesStream.cancel(); + List services = await element.device.discoverServices(); + services.forEach((service) { + if (service.serviceUuid.toString().toLowerCase() == BLEUtils.BLOOD_GLUCOSE_SERVICE) { + print(service.serviceUuid); + service.characteristics.forEach((characteristic) async { + if (characteristic.characteristicUuid.toString().toLowerCase() == BLEUtils.BLOOD_GLUCOSE_CHARACTERISTIC) { + print(characteristic.characteristicUuid); + bloodGlucoseValuesStream = characteristic.onValueReceived.listen((event) { + print("onValueReceived Stream"); + print(event); + setState(() { + if (!isDataMeasured) currentBloodGlucose = getBloodGlucoseMeasurements(event)[0]; + // currentTempInCelsius = convertIntListToHex(event); + // String currentTempInFahrenheit = ((num.parse(currentTempInCelsius) * 1.8) + 32).toStringAsFixed(1); + // currentTempInCelsius = currentTempInCelsius + "\u2103" + " / " + currentTempInFahrenheit + "\u2109"; + }); + }); + await characteristic.setNotifyValue(true); + } + }); + return true; + } + }); + } + }); + await element.device.connect(timeout: Duration(seconds: 35)); + return true; + } + } + }); + }, + // onError(e) => print(e); + ); + + FlutterBluePlus.startScan(timeout: const Duration(seconds: 5), androidUsesFineLocation: false); + } + } + + List getBloodGlucoseMeasurements(List byteArray) { + List results = []; + + //[85, 6, 2, 0, 4, 99] + //[85, 12, 3, 23, 9, 12, 12, 25, 0, 75, 0, 2] + + if (byteArray.length == 6) { + int secsRemaining = 0; + secsRemaining = byteArray[4]; + results.add("Measuring: $secsRemaining"); + } else { + num measuredValue = byteArray[9]; + results.add("Measured: $measuredValue"); + isDataMeasured = true; + bloodGlucoseValuesStream.cancel(); + } + + return results; + } +} diff --git a/lib/pages/medical/my_trackers/blood_pressure/BloodPressureBLE.dart b/lib/pages/medical/my_trackers/blood_pressure/BloodPressureBLE.dart new file mode 100644 index 00000000..0ab0c0f1 --- /dev/null +++ b/lib/pages/medical/my_trackers/blood_pressure/BloodPressureBLE.dart @@ -0,0 +1,322 @@ +import 'dart:async'; +import 'dart:developer'; +import 'dart:io'; + +import 'package:diplomaticquarterapp/theme/colors.dart'; +import 'package:diplomaticquarterapp/uitl/ble_utils.dart'; +import 'package:diplomaticquarterapp/uitl/translations_delegate_base.dart'; +import 'package:diplomaticquarterapp/widgets/buttons/defaultButton.dart'; +import 'package:diplomaticquarterapp/widgets/others/app_scaffold_widget.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_blue_plus/flutter_blue_plus.dart'; +import 'package:permission_handler/permission_handler.dart'; + +class BloodPressureBLE extends StatefulWidget { + @override + State createState() => _BloodPressureBLEState(); +} + +class _BloodPressureBLEState extends State { + String connectionStatus = "disconnected"; + String currentBPmmHG = "0/0 mmHG"; + String currentPulse = "0 PPM"; + + BluetoothDevice currentConnectedDevice; + + StreamSubscription bloodPressureReadingStream; + + StreamSubscription bleDevicesStream; + StreamSubscription bleDeviceConnectionStream; + + final bleConnectionStatus = ValueNotifier("Disconnected"); + + Timer _timer; + + @override + void dispose() { + super.dispose(); + if (bleDevicesStream != null) bleDevicesStream.cancel(); + if (currentConnectedDevice != null) currentConnectedDevice.disconnect(); + bleConnectionStatus.dispose(); + if (bleDeviceConnectionStream != null) bleDeviceConnectionStream.cancel(); + if (bloodPressureReadingStream != null) bloodPressureReadingStream.cancel(); + if (currentConnectedDevice != null) currentConnectedDevice.disconnect(); + if (_timer != null && _timer.isActive) _timer.cancel(); + } + + @override + Widget build(BuildContext context) { + return AppScaffold( + appBarTitle: TranslationBase.of(context).bloodPressure, + showNewAppBar: true, + isShowDecPage: true, + showNewAppBarTitle: true, + backgroundColor: Color(0xffF8F8F8), + body: SingleChildScrollView( + child: StreamBuilder( + stream: FlutterBluePlus.adapterState, + initialData: BluetoothAdapterState.unknown, + builder: (c, snapshot) { + final adapterState = snapshot.data; + if (adapterState == BluetoothAdapterState.on) { + return Container( + margin: EdgeInsets.only(top: 200.0, left: 50.0, right: 50.0), + child: Column( + children: [ + Center( + child: DefaultButton( + TranslationBase.of(context).start.toUpperCase(), + () { + checkBLEPermissions(); + }, + color: CustomColors.green, + ), + ), + SizedBox( + height: 50.0, + ), + Text("Connection state: $connectionStatus"), + SizedBox( + height: 50.0, + ), + Text("Current BP Level: $currentBPmmHG"), + SizedBox( + height: 20.0, + ), + Text("Current Pulse: $currentPulse"), + ], + ), + ); + } else { + FlutterBluePlus.stopScan(); + return SizedBox(height: 300.0, child: BluetoothOffScreen(adapterState: adapterState)); + } + }), + ), + ); + } + + void checkBLEPermissions() { + [Permission.location, Permission.storage, Permission.bluetooth, Permission.bluetoothConnect, Permission.bluetoothScan].request().then((status) { + startBLEConnection(); + }); + } + + void startBLEConnection() { + if (FlutterBluePlus.isScanningNow == false) { + bleConnectionStatus.value = "Connecting..."; + + bleDevicesStream = FlutterBluePlus.scanResults.listen((results) { + List blueToothDevices = results; + blueToothDevices.forEach((element) async { + if (element.device.localName.isNotEmpty) { + log("blueToothDevices: ${element.device.localName}"); + if (element.device.localName.toLowerCase() == "pm101897") { + bleDevicesStream.cancel(); + element.device.connect(timeout: Duration(seconds: 30), autoConnect: false).then((value) async { + bleConnectionStatus.value = "Connected..."; + print("Device Connected-------"); + currentConnectedDevice = element.device; + bleDeviceConnectionStream = currentConnectedDevice.connectionState.listen((event) { + if (event == BluetoothConnectionState.disconnected) { + bleConnectionStatus.value = "Disconnected..."; + print("Device Disconnected-------"); + // if (_timer.isActive) _timer.cancel(); + } + }); + FlutterBluePlus.stopScan(); + + List services = await element.device.discoverServices(timeout: 30).catchError((err) { + print(err.toString()); + element.device.disconnect(timeout: 15); + }); + + if (services != null && services.isNotEmpty) { + services.forEach((service) { + if (service.serviceUuid.toString().toLowerCase() == BLEUtils.ECG_SERVICE) { + print(service.serviceUuid); + service.characteristics.forEach((characteristic) async { + if (characteristic.characteristicUuid.toString().toLowerCase() == BLEUtils.ECG_READ_CHARACTERISTIC) { + print(characteristic.characteristicUuid); + print(characteristic.properties.toString()); + + characteristic.onValueReceived.listen((event) { + print("onValueReceived 1e4d Stream"); + print(event); + }); + + await Future.delayed(Duration(milliseconds: 1000)).then((value) async { + print("-----Delayed 1e4d notify true done-----"); + if (!characteristic.isNotifying) await characteristic.setNotifyValue(true).catchError((err) {}); + }); + } + + if (characteristic.characteristicUuid.toString().toLowerCase() == BLEUtils.ECG_WRITE_CHARACTERISTIC) { + print(characteristic.characteristicUuid); + print(characteristic.properties.toString()); + + // Write get cases command to 8841 + await Future.delayed(Duration(milliseconds: 1000)).then((value) async { + // 0x90 00 00 00 1 + characteristic.write([0x90, 0x00, 0x00, 0x00, 0x01], withoutResponse: false).then((value) { + print("----8841 get device info command data written----"); + }); + }); + } + }); + } + }); + } + }).catchError((onError) { + print("----- ERRORRRR!!!!!! -------"); + print(onError.toString()); + }); + } + } + + }); + }); + FlutterBluePlus.startScan(timeout: const Duration(seconds: 15), androidUsesFineLocation: false); + } + } + +void startBLEConnectionII() { + if (FlutterBluePlus.isScanningNow == false) { + setState(() { + connectionStatus = "Connecting..."; + }); + + FlutterBluePlus.startScan(timeout: const Duration(seconds: 5), androidUsesFineLocation: false).then((value) { + // List blueToothDevices = value; + List blueToothDevices = []; + blueToothDevices.forEach((element) async { + if (element.device.localName.isNotEmpty) { + if (element.device.localName.toLowerCase() == "bpm") { + element.device.connectionState.listen((BluetoothConnectionState state) async { + setState(() { + connectionStatus = state.toString(); + }); + if (state == BluetoothConnectionState.disconnected) { + // typically, start a periodic timer that tries to periodically reconnect. + // Note: you must always re-discover services after disconnection! + + // _timer = Timer.periodic(Duration(seconds: 2), (Timer timer) { + // startBLEConnection(); + // }); + } + if (state == BluetoothConnectionState.connected) { + currentConnectedDevice = element.device; + // _timer.cancel(); + List services = await element.device.discoverServices(); + services.forEach((service) { + if (service.serviceUuid.toString().toLowerCase() == BLEUtils.BLOOD_PRESSURE_SERVICE) { + print(service.serviceUuid); + service.characteristics.forEach((characteristic) async { + if (characteristic.characteristicUuid.toString().toLowerCase() == BLEUtils.BLOOD_PRESSURE_CHARACTERISTIC) { + print(characteristic.characteristicUuid); + bloodPressureReadingStream = characteristic.onValueReceived.listen((event) { + print("onValueReceived Stream"); + print(event); + setState(() { + if (event.length < 12) { + currentBPmmHG = "Measuring..."; + } else { + currentBPmmHG = "Measurement complete!"; + Map results = handleBPResult(event, characteristic); + currentBPmmHG = results["systolic"] + "/" + results["diastolic"] + " - " + (results["isBPHigh"] == "true" ? "High BP" : "Normal BP"); + currentPulse = results["pulse"] + " - " + (results["isPulseHigh"] == "true" ? "High pulse" : "Normal Pulse"); + } + }); + }); + await characteristic.setNotifyValue(true); + } + }); + return true; + } + }); + } + }); + await element.device.connect(timeout: Duration(seconds: 35)); + return true; + } + } + }); + }); + } +} +} + +Map handleBPResult(List byteArray, BluetoothCharacteristic characteristic) { + // [28, 0, 148, 0, 118, 0, 0, 0, 106, 0, 0, 0] + + // {isBPHigh: true, systolic: 145, diastolic: 111, pulse: 109, isPulseHigh: true} + + Map results = Map(); + + if (byteArray[1] != 0 && byteArray[3] != 0) { + results["isBPHigh"] = "false"; + results["systolic"] = byteArray[1].toString(); + results["diastolic"] = byteArray[3].toString(); + } else { + results["isBPHigh"] = "true"; + results["systolic"] = byteArray[2].toString(); + results["diastolic"] = byteArray[4].toString(); + } + + if (byteArray[8] != 0) { + results["pulse"] = byteArray[8].toString(); + results["isPulseHigh"] = "true"; + } else { + results["pulse"] = byteArray[9].toString(); + results["isPulseHigh"] = "false"; + } + + if (characteristic.isNotifying) characteristic.setNotifyValue(false); + + print(results); + + return results; +} + +class BluetoothOffScreen extends StatelessWidget { + const BluetoothOffScreen({Key key, this.adapterState}) : super(key: key); + + final BluetoothAdapterState adapterState; + + @override + Widget build(BuildContext context) { + return ScaffoldMessenger( + child: Scaffold( + backgroundColor: Colors.lightBlue, + body: Center( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + const Icon( + Icons.bluetooth_disabled, + size: 200.0, + color: Colors.white54, + ), + Text( + 'Bluetooth Adapter is ${adapterState != null ? adapterState.toString().split(".").last + ", Please turn on your bluetooth to continue." : 'not available'}.', + textAlign: TextAlign.center, + style: Theme.of(context).primaryTextTheme.titleSmall?.copyWith(color: Colors.white), + ), + if (Platform.isAndroid) + ElevatedButton( + child: const Text('TURN ON'), + onPressed: () async { + try { + if (Platform.isAndroid) { + await FlutterBluePlus.turnOn(); + } + } catch (e) {} + }, + ), + ], + ), + ), + ), + ); + } +} diff --git a/lib/pages/medical/my_trackers/ecg_ble.dart b/lib/pages/medical/my_trackers/ecg_ble.dart new file mode 100644 index 00000000..df3e4a70 --- /dev/null +++ b/lib/pages/medical/my_trackers/ecg_ble.dart @@ -0,0 +1,234 @@ +import 'dart:async'; +import 'dart:developer'; + +import 'package:diplomaticquarterapp/theme/colors.dart'; +import 'package:diplomaticquarterapp/uitl/ble_utils.dart'; +import 'package:diplomaticquarterapp/uitl/bluetooth_off.dart'; +import 'package:diplomaticquarterapp/uitl/translations_delegate_base.dart'; +import 'package:diplomaticquarterapp/widgets/buttons/defaultButton.dart'; +import 'package:diplomaticquarterapp/widgets/others/app_scaffold_widget.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_blue_plus/flutter_blue_plus.dart'; +import 'package:permission_handler/permission_handler.dart'; + +class ECG_BLE extends StatefulWidget { + @override + State createState() => _ECG_BLEState(); +} + +class _ECG_BLEState extends State { + String connectionStatus = "disconnected"; + BluetoothDevice currentConnectedDevice; + + BluetoothCharacteristic ecgWriteCharacteristic; + + StreamSubscription bleDevicesStream; + StreamSubscription bleDeviceConnectionStream; + + final bleConnectionStatus = ValueNotifier("Disconnected"); + + @override + void dispose() { + super.dispose(); + if (bleDevicesStream != null) bleDevicesStream.cancel(); + if (currentConnectedDevice != null) currentConnectedDevice.disconnect(); + bleConnectionStatus.dispose(); + if (bleDeviceConnectionStream != null) bleDeviceConnectionStream.cancel(); + } + + @override + void initState() { + super.initState(); + // FlutterBluePlus.setLogLevel(LogLevel.verbose, color: false); + } + + @override + Widget build(BuildContext context) { + return AppScaffold( + appBarTitle: "ECG", + showNewAppBar: true, + isShowDecPage: false, + showNewAppBarTitle: true, + backgroundColor: Color(0xffF8F8F8), + body: SingleChildScrollView( + child: StreamBuilder( + stream: FlutterBluePlus.adapterState, + initialData: BluetoothAdapterState.unknown, + builder: (c, snapshot) { + final adapterState = snapshot.data; + if (adapterState == BluetoothAdapterState.on) { + return Container( + margin: EdgeInsets.only(top: 200.0, left: 50.0, right: 50.0), + child: Column( + children: [ + Center( + child: DefaultButton( + TranslationBase.of(context).start.toUpperCase(), + () { + checkBLEPermissions(); + }, + color: CustomColors.green, + ), + ), + SizedBox( + height: 50.0, + ), + ValueListenableBuilder( + valueListenable: bleConnectionStatus, + builder: (context, value, _) { + return Text("Connection state: $value"); + }, + ), + SizedBox( + height: 50.0, + ), + // Text("Current Temp: $currentTempInCelsius"), + ], + ), + ); + } else { + FlutterBluePlus.stopScan(); + return SizedBox(height: 300.0, child: BluetoothOffScreen(adapterState: adapterState)); + } + }), + ), + ); + } + + void checkBLEPermissions() { + [Permission.location, Permission.storage, Permission.bluetooth, Permission.bluetoothConnect, Permission.bluetoothScan].request().then((status) { + startBLEConnection(); + }); + } + + void startBLEConnection() { + if (FlutterBluePlus.isScanningNow == false) { + bleConnectionStatus.value = "Connecting..."; + + bleDevicesStream = FlutterBluePlus.scanResults.listen((results) { + List blueToothDevices = results; + blueToothDevices.forEach((element) async { + if (element.device.localName.isNotEmpty) { + log("blueToothDevices: ${element.device.localName}"); + if (element.device.localName.toLowerCase() == "pm101897") { + bleDevicesStream.cancel(); + element.device.connect(timeout: Duration(seconds: 30), autoConnect: false).then((value) async { + bleConnectionStatus.value = "Connected..."; + print("Device Connected-------"); + currentConnectedDevice = element.device; + bleDeviceConnectionStream = currentConnectedDevice.connectionState.listen((event) { + if (event == BluetoothConnectionState.disconnected) { + bleConnectionStatus.value = "Disconnected..."; + print("Device Disconnected-------"); + // if (_timer.isActive) _timer.cancel(); + } + }); + FlutterBluePlus.stopScan(); + + List services = await element.device.discoverServices(timeout: 30).catchError((err) { + print(err.toString()); + element.device.disconnect(timeout: 15); + }); + + if (services != null && services.isNotEmpty) { + services.forEach((service) { + if (service.serviceUuid.toString().toLowerCase() == BLEUtils.ECG_SERVICE) { + print(service.serviceUuid); + service.characteristics.forEach((characteristic) async { + if (characteristic.characteristicUuid.toString().toLowerCase() == BLEUtils.ECG_READ_CHARACTERISTIC) { + print(characteristic.characteristicUuid); + print(characteristic.properties.toString()); + + characteristic.onValueReceived.listen((event) { + print("onValueReceived 1e4d Stream"); + print(event); + }); + + await Future.delayed(Duration(milliseconds: 1000)).then((value) async { + print("-----Delayed 1e4d notify true done-----"); + if (!characteristic.isNotifying) await characteristic.setNotifyValue(true).catchError((err) {}); + }); + } + + if (characteristic.characteristicUuid.toString().toLowerCase() == BLEUtils.ECG_WRITE_CHARACTERISTIC) { + print(characteristic.characteristicUuid); + print(characteristic.properties.toString()); + + // Write get cases command to 8841 + await Future.delayed(Duration(milliseconds: 1000)).then((value) async { + // 0x90 00 00 00 1 + characteristic.write([0x90, 0x00, 0x00, 0x00, 0x01], withoutResponse: false).then((value) { + print("----8841 get device info command data written----"); + }); + }); + } + }); + } + }); + } + }).catchError((onError) { + print("----- ERRORRRR!!!!!! -------"); + print(onError.toString()); + }); + } + } + + // if (element.device.localName.isNotEmpty) { + // if (element.device.localName.toLowerCase() == "pm101897") { + // bleDevicesStream.cancel(); + // element.device.connectionState.listen((BluetoothConnectionState state) async { + // if (mounted) { + // setState(() { + // connectionStatus = state.toString(); + // }); + // } + // if (state == BluetoothConnectionState.disconnected) { + // // typically, start a periodic timer that tries to periodically reconnect. + // // Note: you must always re-discover services after disconnection! + // } + // if (state == BluetoothConnectionState.connected) { + // if (FlutterBluePlus.isScanningNow) { + // FlutterBluePlus.stopScan(); + // } + // currentConnectedDevice = element.device; + // // currentConnectedDevice.clearGattCache(); + // // currentConnectedDevice.requestConnectionPriority(connectionPriorityRequest: ConnectionPriority.high); + // // currentConnectedDevice.requestMtu(512); + // // currentConnectedDevice.mtu.first.then((value) { + // // print("MTU Size: $value"); + // // }); + // List services = await element.device.discoverServices(); + // services.forEach((service) { + // if (service.serviceUuid.toString().toLowerCase() == BLEUtils.ECG_SERVICE) { + // print(service.serviceUuid); + // service.characteristics.forEach((characteristic) async { + // if (characteristic.characteristicUuid.toString().toLowerCase() == BLEUtils.ECG_READ_CHARACTERISTIC) { + // print(characteristic.characteristicUuid); + // characteristic.onValueReceived.listen((event) { + // print("onValueReceived Stream"); + // print(event); + // }); + // if (!characteristic.isNotifying) await characteristic.setNotifyValue(true); + // } + // + // if (characteristic.characteristicUuid.toString().toLowerCase() == BLEUtils.ECG_WRITE_CHARACTERISTIC) { + // print("Write Characteristic: ${characteristic.characteristicUuid}"); + // ecgWriteCharacteristic = characteristic; + // await ecgWriteCharacteristic.write([0x83]); + // } + // }); + // return true; + // } + // }); + // } + // }); + // await element.device.connect(timeout: Duration(seconds: 35)); + // return true; + // } + // } + }); + }); + FlutterBluePlus.startScan(timeout: const Duration(seconds: 15), androidUsesFineLocation: false); + } + } +} diff --git a/lib/pages/medical/my_trackers/my_trackers.dart b/lib/pages/medical/my_trackers/my_trackers.dart index c6e7d5dc..c9c0309c 100644 --- a/lib/pages/medical/my_trackers/my_trackers.dart +++ b/lib/pages/medical/my_trackers/my_trackers.dart @@ -1,6 +1,14 @@ import 'package:diplomaticquarterapp/core/model/ImagesInfo.dart'; import 'package:diplomaticquarterapp/pages/medical/my_trackers/Weight/WeightHomePage.dart'; +import 'package:diplomaticquarterapp/pages/medical/my_trackers/blood_glucose_ble.dart'; +import 'package:diplomaticquarterapp/pages/medical/my_trackers/blood_pressure/BloodPressureBLE.dart'; import 'package:diplomaticquarterapp/pages/medical/my_trackers/blood_suger/blood_sugar_home_page.dart'; +import 'package:diplomaticquarterapp/pages/medical/my_trackers/ecg_ble.dart'; +import 'package:diplomaticquarterapp/pages/medical/my_trackers/spirometer.dart'; +import 'package:diplomaticquarterapp/pages/medical/my_trackers/temperature.dart'; +import 'package:diplomaticquarterapp/pages/medical/my_trackers/viatom_devices/ekg_tracker_ble.dart'; +import 'package:diplomaticquarterapp/pages/medical/my_trackers/viatom_devices/oxymeter_ble.dart'; +import 'package:diplomaticquarterapp/pages/medical/my_trackers/weight_scale_ble.dart'; import 'package:diplomaticquarterapp/uitl/translations_delegate_base.dart'; import 'package:diplomaticquarterapp/widgets/data_display/medical/medical_profile_item.dart'; import 'package:diplomaticquarterapp/widgets/others/app_scaffold_widget.dart'; @@ -16,7 +24,7 @@ class MyTrackers extends StatelessWidget { return AppScaffold( appBarTitle: TranslationBase.of(context).myTracker, showNewAppBar: true, - isShowDecPage: true, + isShowDecPage: false, showNewAppBarTitle: true, backgroundColor: Color(0xffF8F8F8), description: TranslationBase.of(context).infoTrackers, @@ -25,12 +33,12 @@ class MyTrackers extends StatelessWidget { ], body: SingleChildScrollView( child: Container( - child: GridView( - gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 3, childAspectRatio: 2 / 2, crossAxisSpacing: 12, mainAxisSpacing: 12), - physics: NeverScrollableScrollPhysics(), - padding: EdgeInsets.all(21), - shrinkWrap: true, - children: [ + child: GridView( + gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 3, childAspectRatio: 2 / 2, crossAxisSpacing: 12, mainAxisSpacing: 12), + physics: NeverScrollableScrollPhysics(), + padding: EdgeInsets.all(21), + shrinkWrap: true, + children: [ InkWell( onTap: () { Navigator.push(context, FadePage(page: BloodSugarHomePage())); @@ -70,7 +78,113 @@ class MyTrackers extends StatelessWidget { height: 45.0, ), ), - ])), + InkWell( + onTap: () { + Navigator.push(context, FadePage(page: TemperatureHomePage())); + }, + child: MedicalProfileItem( + title: TranslationBase.of(context).temperature, + imagePath: 'assets/tracker/weight.png', + subTitle: null, + isPngImage: true, + width: 45.0, + height: 45.0, + ), + ), + InkWell( + onTap: () { + Navigator.push(context, FadePage(page: BloodPressureBLE())); + }, + child: MedicalProfileItem( + title: TranslationBase.of(context).bloodPressure + " BLE", + imagePath: 'assets/tracker/blood-pressure.png', + subTitle: null, + isPngImage: true, + width: 45.0, + height: 45.0, + ), + ), + InkWell( + onTap: () { + Navigator.push(context, FadePage(page: SpirometerBLE())); + }, + child: MedicalProfileItem( + title: TranslationBase.of(context).spirometer + " BLE", + imagePath: 'assets/tracker/blood-pressure.png', + subTitle: null, + isPngImage: true, + width: 45.0, + height: 45.0, + ), + ), + InkWell( + onTap: () { + Navigator.push(context, FadePage(page: ECG_BLE())); + }, + child: MedicalProfileItem( + title: "ECG BLE", + imagePath: 'assets/tracker/blood-pressure.png', + subTitle: null, + isPngImage: true, + width: 45.0, + height: 45.0, + ), + ), + InkWell( + onTap: () { + Navigator.push(context, FadePage(page: BloodGlucoseBLE())); + }, + child: MedicalProfileItem( + title: "Blood Glucose BLE", + imagePath: 'assets/tracker/blood-pressure.png', + subTitle: null, + isPngImage: true, + width: 45.0, + height: 45.0, + ), + ), + InkWell( + onTap: () { + Navigator.push(context, FadePage(page: WeightScaleBLE())); + }, + child: MedicalProfileItem( + title: "Weight BLE", + imagePath: 'assets/tracker/weight.png', + subTitle: null, + isPngImage: true, + width: 45.0, + height: 45.0, + ), + ), + InkWell( + onTap: () { + Navigator.push(context, FadePage(page: Oxymeter_BLE())); + }, + child: MedicalProfileItem( + title: "Oxymeter BLE", + imagePath: 'assets/tracker/weight.png', + subTitle: null, + isPngImage: true, + width: 45.0, + height: 45.0, + ), + ), + InkWell( + onTap: () { + Navigator.push(context, FadePage(page: EKG_BLE())); + }, + child: MedicalProfileItem( + title: "EKG BLE", + imagePath: 'assets/tracker/weight.png', + subTitle: null, + isPngImage: true, + width: 45.0, + height: 45.0, + ), + ), + ], + ), + ), ), ); } diff --git a/lib/pages/medical/my_trackers/my_trackers_view_model/my_trackers_view_model.dart b/lib/pages/medical/my_trackers/my_trackers_view_model/my_trackers_view_model.dart new file mode 100644 index 00000000..96581cca --- /dev/null +++ b/lib/pages/medical/my_trackers/my_trackers_view_model/my_trackers_view_model.dart @@ -0,0 +1,1638 @@ +import 'dart:async'; +import 'dart:convert'; +import 'dart:developer'; +import 'dart:io'; +import 'package:diplomaticquarterapp/config/size_config.dart'; +import 'package:diplomaticquarterapp/main.dart'; +import 'package:diplomaticquarterapp/pages/medical/my_trackers/ble_device_type_screens/checkme_all_in_one_info_screen.dart'; +import 'package:diplomaticquarterapp/pages/medical/my_trackers/ble_models/andesfit_devices/weight_data_model.dart'; +import 'package:diplomaticquarterapp/pages/medical/my_trackers/ble_models/ble_devices_model.dart'; +import 'package:diplomaticquarterapp/pages/medical/my_trackers/ble_models/checkmepro_all_in_one_models/checkme_bp_model.dart'; +import 'package:diplomaticquarterapp/pages/medical/my_trackers/ble_models/checkmepro_all_in_one_models/checkme_ecg_model.dart'; +import 'package:diplomaticquarterapp/pages/medical/my_trackers/ble_models/checkmepro_all_in_one_models/checkme_ecg_wave_model.dart'; +import 'package:diplomaticquarterapp/pages/medical/my_trackers/ble_models/checkmepro_all_in_one_models/checkme_glucose_model.dart'; +import 'package:diplomaticquarterapp/pages/medical/my_trackers/ble_models/checkmepro_all_in_one_models/checkme_oxi_model.dart'; +import 'package:diplomaticquarterapp/pages/medical/my_trackers/ble_models/checkmepro_all_in_one_models/checkme_temperature_model.dart'; +import 'package:diplomaticquarterapp/pages/medical/my_trackers/ble_models/checkmepro_all_in_one_models/checkme_user_model.dart'; +import 'package:diplomaticquarterapp/pages/medical/my_trackers/ble_models/smart_ring_models.dart'; +import 'package:diplomaticquarterapp/pages/medical/my_trackers/ble_models/viatom_devices/bp_rt_model.dart'; +import 'package:diplomaticquarterapp/pages/medical/my_trackers/ble_models/viatom_devices/ecg_file_detail_model.dart'; +import 'package:diplomaticquarterapp/pages/medical/my_trackers/ble_models/viatom_devices/ecg_rt_model.dart'; +import 'package:diplomaticquarterapp/pages/medical/my_trackers/ble_models/viatom_devices/oxy_rt_model.dart'; +import 'package:diplomaticquarterapp/pages/medical/my_trackers/viatom_devices/ekg_chart_view.dart'; +import 'package:diplomaticquarterapp/uitl/app_toast.dart'; +import 'package:diplomaticquarterapp/uitl/ble_utils.dart'; +import 'package:diplomaticquarterapp/uitl/date_uitl.dart'; +import 'package:diplomaticquarterapp/uitl/utils_new.dart'; +import 'package:diplomaticquarterapp/widgets/transitions/fade_page.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_blue_plus/flutter_blue_plus.dart'; +import 'package:permission_handler/permission_handler.dart'; +import 'package:diplomaticquarterapp/pages/medical/my_trackers/ble_helpers/ble_connect_helper.dart'; +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; + +enum TrackerTypeEnum { OxymeterTracker, BloodPressureTracker, BloodSugarTracker, ECGTracker, WeightScale, Temperature, Spirometer, SmartRing, AllInOneTracker } + +enum SmartRingOperationsEnum { Temperature, HeartRate, RealTimeHeartRate, TotalStepCount, HRV, BloodOxygen, SportsMode, BatteryLevel } + +//NativeEventNames + +String kRealTimeDataBPMeasuring = "RealTimeDataBPMeasuring"; +String kRealTimeDataBPResult = "RealTimeDataBPResult"; +String kOxyRtParam = "OxyRtParam"; +String kDevicesList = "DevicesList"; +String kRealTimeDataECGMeasuring = "RealTimeDataECGMeasuring"; // This is for the device that measures BP and ECG also Like BP2 0567 +String kRealTimeDataECGResult = "RealTimeDataECGResult"; +String kRealTimeDataECG = "RealTimeDataECG"; +String kFileDetail = "FileDetail"; +String kDevicesListCheckMe = "DevicesListCheckMe"; +String kCheckMeUsersList = "CheckMeUsersList"; +String kTemperatureCheckMePro = "TemperatureCheckMePro"; +String kECGCheckMePro = "ECGCheckMePro"; +String kECGWaveCheckMePro = "ECGWaveCheckMePro"; +String kOxiCheckMePro = "OxiCheckMePro"; +String kGlucoseCheckMePro = "GlucoseCheckMePro"; +String kBPCheckMePro = "BPCheckMePro"; + +String kDevicesListSmartRing = "DevicesListSmartRing"; +String kBatteryLevelSmartRing = "BatteryLevelSmartRing"; +String kTemperatureSmartRing = "TemperatureSmartRing"; +String kDynamicHeartRateSmartRing = "DynamicHeartRateSmartRing"; +String kStaticHeartRateSmartRing = "StaticHeartRateSmartRing"; +String kHrvSmartRing = "HrvSmartRing"; +String kBloodOxygenSmartRing = "BloodOxygenSmartRing"; +String kTotalStepCountSmartRing = "TotalStepCountSmartRing"; +String kRTSportDataSmartRing = "RTSportDataSmartRing"; + +class MyTrackersViewModel extends ChangeNotifier { + EventChannel eventChannel = EventChannel('BLE-Platform-Bridge-Event'); + + List devicesList = []; + + var devicesInfoJsonViatom = { + "OxymeterTracker": [ + {"model": "SP-20"}, // DONE + {"model": "POD-1"}, // DONE + {"model": "O2M"}, // DONE + {"model": "PC-60"}, //DONE + {"model": "O2Ring"}, // DONE + ], + "BloodPressureTracker": [ + {"model": "BP2"}, //Done + {"model": ""}, + ], + "BloodSugarTracker": [ + {"model": ""}, + ], + "ECGTracker": [ + {"model": "DuoEK"}, + {"model": "ER1"}, + ], + "WeightScale": [ + {"model": ""}, + ], + "Temperature": [ + {"model": ""}, + ], + "Spirometer": [ + {"model": "BLE-MSA"}, + ], + "SmartRing": [ + {"model": "2301A"}, + ], + "AllInOneTracker": [ + {"model": "Checkme 1316"}, + ], + }; + + var devicesInfoJsonAndesfit = { + "OxymeterTracker": [ + {"model": ""} + ], + "BloodPressureTracker": [ + {"model": "BPM"}, //Done + ], + "BloodSugarTracker": [ + {"model": "Samico GL"}, + ], + "ECGTracker": [ + {"model": "PM101897"}, //Done + ], + "WeightScale": [ + {"model": "SDIC"}, + ], + "Temperature": [ + {"model": "TEMP"}, + ], + "Spirometer": [ + {"model": "BLE-MSA"}, + ], + "SmartRing": [ + {"model": ""}, + ], + "AllInOneTracker": [ + {"model": "Checkme 1316"}, + ], + }; + + List ecgEnabledDevices = ["BP2 0567", "DuoEK", "ER1", "PM101897"]; + + List checkMeProTrackers = [ + TrackerTypeEnum.Temperature, + TrackerTypeEnum.BloodPressureTracker, + TrackerTypeEnum.OxymeterTracker, + TrackerTypeEnum.BloodSugarTracker, + TrackerTypeEnum.ECGTracker, + ]; + List smartRingTrackers = [ + SmartRingOperationsEnum.Temperature, + SmartRingOperationsEnum.HeartRate, + SmartRingOperationsEnum.TotalStepCount, + SmartRingOperationsEnum.SportsMode, + SmartRingOperationsEnum.HRV, + SmartRingOperationsEnum.BloodOxygen, + SmartRingOperationsEnum.BatteryLevel, + ]; + + List andesFitDevices = [ + "BPM", + "PM101897", + "SDIC", + "TEMP", + "Samico GL", + "BLE-MSA", + ]; + + StreamSubscription bleDevicesStream; + + //************************************* OXYMETER ************************************* + + OxyRtModel oxyRtModel; + + void updateOxyRtModel(Map mapData) { + oxyRtModel = OxyRtModel.fromJson(mapData); + notifyListeners(); + } + + // ************************************* BLOOD PRESSURE ************************************* + + BpRtMeasuringModel bpRtMeasuringModel; + + void updateBpRtMeasuringModel(Map mapData) { + bpRtMeasuringModel = BpRtMeasuringModel.fromJson(mapData); + notifyListeners(); + } + + BpRtResultModel bpRtResultModel; + + void updateBpRtResultModel(Map mapData) { + bpRtResultModel = BpRtResultModel.fromJson(mapData); + notifyListeners(); + } + + String bpCurrentStatus; + + updateBpCurrentStatus(var value) { + bpCurrentStatus = value; + notifyListeners(); + } + + bool isECGSelected = false; + + updateIsECGSelected(var value) { + isECGSelected = value; + notifyListeners(); + } + + //************************************* ECG ************************************* + + bool filesLoader = false; + + updateFilesLoader(var value) { + filesLoader = value; + } + + ECGRtModel ecgRtModel; + + void updateEcgRtModel(Map mapData) { + ecgRtModel = ECGRtModel.fromJson(mapData); + notifyListeners(); + } + + List ecgHistoryFiles = []; + + updateEcgHistoryFiles(var value) { + ecgHistoryFiles = value; + notifyListeners(); + } + + clearEcgHistoryFiles() { + ecgHistoryFiles.clear(); + ecgHistoryFiles = []; + notifyListeners(); + } + + parseEcgHistoryFiles(String mapData) { + ecgHistoryFiles.clear(); + ecgHistoryFiles = []; + json.decode(mapData).forEach((v) { + ecgHistoryFiles.add(new ECGFileDetailModel.fromJson(v)); + }); + notifyListeners(); + } + + String ecgCurrentStatus; + + updateEcgCurrentStatus(var value) { + ecgCurrentStatus = value; + notifyListeners(); + } + + ECGRtMeasuringModelFromBP ecgRtMeasuringModelFromBP; + + void updateEcgRtMeasuringModelFromBP(Map mapData) { + ecgRtMeasuringModelFromBP = ECGRtMeasuringModelFromBP.fromJson(mapData); + notifyListeners(); + } + + ECGRtResultModelFromBP ecgRtResultModelFromBP; + + void updateEcgRtResultModelFromBP(Map mapData) { + ecgRtResultModelFromBP = ECGRtResultModelFromBP.fromJson(mapData); + notifyListeners(); + } + + Widget getFilesListWidget(List filesList, BuildContext context) { + return Material( + child: SizedBox( + height: MediaQuery.of(context).size.height * 0.7, + child: ListView.separated( + itemCount: filesList.length, + separatorBuilder: (context, index) => SizedBox(height: 14), + shrinkWrap: true, + reverse: false, + itemBuilder: (context, index) { + ECGFileDetailModel ecgFileDetailModel = filesList[index]; + return InkWell( + onTap: () => Navigator.push(context, FadePage(page: EKGChartView(ekgFileDetailResponseModel: ecgFileDetailModel))), + child: Container( + decoration: cardRadius(12), + padding: EdgeInsets.all(12.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text(ecgFileDetailModel.fileName), + mHeight(6.0), + Text("Duration: ${ecgFileDetailModel.duration} seconds"), + mHeight(6.0), + Text("Date: ${DateUtil.getDayMonthYearHourMinuteDateFormatted(DateTime.fromMillisecondsSinceEpoch((ecgFileDetailModel.startTime * 1000)))}"), + ], + ), + ), + ); + }, + ), + ), + ); + } + + void filterOutTheSearchedDevicesNative(List allDevices) { + devicesList.clear(); + notifyListeners(); + if (devicesInfoJsonViatom.containsKey(currentSelectedTrackerType.name)) { + allDevices.forEach( + (foundDevice) { + List devicesInSelectedType = devicesInfoJsonViatom[currentSelectedTrackerType.name]; + for (var device in devicesInSelectedType) { + // log("foundDevice.name: ${foundDevice.name.toString()}"); + foundDevice.deviceType = currentSelectedTrackerType; + // log("device['model']: ${device['model']}"); + if (device['model'] != "" && foundDevice.name.contains(device['model'])) { + devicesList.add(foundDevice); + } + } + }, + ); + notifyListeners(); + } + } + + void filterOutTheSearchedDevicesFlutter(List allDevices) { + if (devicesInfoJsonAndesfit.containsKey(currentSelectedTrackerType.name)) { + List devicesInSelectedType = devicesInfoJsonAndesfit[currentSelectedTrackerType.name]; + for (var foundDevice in allDevices) { + for (var device in devicesInSelectedType) { + if (isDeviceSelected) { + log("CONNECTED, Breaking the loop"); + break; + } + foundDevice.deviceType = currentSelectedTrackerType; + log("here1: ${device['model']}"); + log("here2: ${foundDevice.name}"); + if (device['model'] != "" && foundDevice.name.contains(device['model'])) { + if (!ifDeviceAlreadyThere(devicesList, foundDevice)) { + devicesList.add(foundDevice); + notifyListeners(); + } + } + } + } + + notifyListeners(); + } + } + + TrackerTypeEnum currentSelectedTrackerType = TrackerTypeEnum.OxymeterTracker; + + updateSelectedTrackerType(var value) { + devicesList.clear(); + currentSelectedTrackerType = value; + notifyListeners(); + } + + Future checkBLEPermissions() async { + return [Permission.location, Permission.storage, Permission.bluetooth, Permission.bluetoothConnect, Permission.bluetoothScan].request().then((value) { + print(value); + }); + } + + Future startSearchingForTracker() async { + log("selectedTracker: ${currentSelectedTrackerType.name}"); + + await checkBLEPermissions(); + eventChannel.receiveBroadcastStream().listen((event) { + print('Received event---: $event'); + print(event['type']); + + // Get Devices List + if (event['type'] == kDevicesList) { + // if(Platform.isIOS) { + // parsesDevicesList(json.decode(event['data'])); + // } else if(Platform.isAndroid) { + parsesDevicesList(json.decode(event['data']) as List, currentSelectedTrackerType); + // } + } + + // Get Oxymeter Readings + if (event['type'] == kOxyRtParam) { + updateOxyRtModel(json.decode(event['data'])); + } + + // Get Blood Pressure Readings while measuring + if (event['type'] == kRealTimeDataBPMeasuring) { + updateBpRtMeasuringModel(json.decode(event['data'])); + updateBpCurrentStatus(kRealTimeDataBPMeasuring); + } + + // Get Blood Pressure Readings after result + if (event['type'] == kRealTimeDataBPResult) { + updateBpRtResultModel(json.decode(event['data'])); + updateBpCurrentStatus(kRealTimeDataBPResult); + } + + // Get ECG Readings while measuring with Blood Pressure Device (BP 0567) + if (event['type'] == kRealTimeDataECGMeasuring) { + updateEcgRtMeasuringModelFromBP(json.decode(event['data'])); + updateEcgCurrentStatus(kRealTimeDataECGMeasuring); + } + + // Get ECG Readings after result with Blood Pressure Device (BP 0567) + if (event['type'] == kRealTimeDataECGResult) { + updateEcgRtResultModelFromBP(json.decode(event['data'])); + updateEcgCurrentStatus(kRealTimeDataECGResult); + } + + // Get ECG Readings from ECG Device like DuoEK + if (event['type'] == kRealTimeDataECG) { + updateEcgRtModel(json.decode(event['data'])); + } + + // Get ECG File History Details + if ((currentSelectedTrackerType == TrackerTypeEnum.ECGTracker || currentSelectedTrackerType == TrackerTypeEnum.BloodPressureTracker) && event['type'] == kFileDetail) { + updateFilesLoader(false); + parseEcgHistoryFiles(event['data']); + } + }); + + await scanDevicesNative(currentSelectedTrackerType); + } + + void parsesDevicesList(List returnData, TrackerTypeEnum trackerType) { + var devicesList = List.generate(returnData.length, (index) => BleDeviceModel.fromJson(returnData[index], trackerType)); + log("devicesList: ${devicesList.toString()}"); + filterOutTheSearchedDevicesNative(devicesList); + } + + void resetList() { + devicesList.clear(); + if (bleDevicesStream != null) { + bleDevicesStream.cancel(); + } + FlutterBluePlus.stopScan(); + secondsToWaitForNativeScan = 5; + } + + Future getEcgFilesList(BleDeviceModel device) async { + await BleChannel.getECGFilesList([device.name, device.model.toString()]); + } + + Future factoryResetECG(BleDeviceModel device) async { + await BleChannel.factoryResetECG([device.name, device.model.toString()]); + } + + Future connectDevice(BleDeviceModel device) async { + if (currentSelectedTrackerType == TrackerTypeEnum.AllInOneTracker) { + await BleChannel.connectDeviceCheckMe([device.name, device.model.toString(), currentSelectedTrackerType.name]); + } else if (currentSelectedTrackerType == TrackerTypeEnum.SmartRing) { + await BleChannel.connectDeviceSmartRing(device.macAddr); + } else { + await BleChannel.connectDevice([device.name, device.model.toString(), currentSelectedTrackerType.name]); + } + } + + Future disConnectDevice() async { + isDeviceSelected = false; + if (currentSelectedTrackerType == TrackerTypeEnum.SmartRing) { + await BleChannel.disConnectDeviceSmartRing(); + return; + } + await BleChannel.disconnect(); + } + + Future scanDevicesNative(TrackerTypeEnum currentSelectedTrackerType) async { + await BleChannel.scanResultsNative([currentSelectedTrackerType.name]); + } + + Future stopNativeScan() async { + await BleChannel.stopNativeScan(); + } + + void startFlutterScan() { + log("FlutterBluePlus.isScanningNow: ${FlutterBluePlus.isScanningNow}"); + if (FlutterBluePlus.isScanningNow == false) { + bleDevicesStream = FlutterBluePlus.scanResults.listen((results) { + List blueToothDevices = results; + List bleDevices = []; + if (isAndesfitDeviceConnected != null && isAndesfitDeviceConnected) { + bleDevicesStream.cancel(); + FlutterBluePlus.stopScan(); + return; + } + + blueToothDevices.forEach((element) async { + if (element.device.localName.isNotEmpty) { + log("blueToothDevicesFlutter: ${element.device.localName}"); + BleDeviceModel bleDeviceModel = BleDeviceModel(name: element.device.localName, model: 0, rssi: 0, macAddr: '', andesfitBluetoothDevice: element.device); + bleDevices.add(bleDeviceModel); + } + }); + filterOutTheSearchedDevicesFlutter(bleDevices); + }); + FlutterBluePlus.startScan( + timeout: const Duration(seconds: 10), androidUsesFineLocation: false, + // withServices: [Guid(BLEUtils.TEMPERATURE_SERVICE)] + ).catchError((err) { + debugPrint(err.toString()); + }); + } + } + + bool ifDeviceAlreadyThere(List devicesList, BleDeviceModel device) { + int index = devicesList.indexWhere((element) => element.name.toLowerCase() == device.name.toLowerCase()); + return index != -1; + } + + bool isDeviceFromAndesFit(String deviceName) { + bool isPresent = false; + + for (var value in andesFitDevices) { + if (deviceName.contains(value)) { + isPresent = true; + } + } + return isPresent; + } + + int secondsToWaitForNativeScan = 5; + + set updateSecondsToWaitForNativeScan(int value) { + secondsToWaitForNativeScan = value; + notifyListeners(); + } + + Timer timerToWaitForNativeScan = Timer.periodic(const Duration(seconds: 1), (Timer timer) {}); + + void startTimerForNativeScan() { + const Duration oneSec = Duration(seconds: 1); + timerToWaitForNativeScan = Timer.periodic( + oneSec, + (Timer timer) async { + log("I will stop scan after: $secondsToWaitForNativeScan"); + + if (secondsToWaitForNativeScan <= 0) { + secondsToWaitForNativeScan--; + timer.cancel(); + timerToWaitForNativeScan.cancel(); + stopNativeScan(); + startFlutterScan(); + return; + } else { + secondsToWaitForNativeScan--; + } + notifyListeners(); + }, + ); + } + + // *********************** ANDESFIT ************************ + + bool isAndesfitDeviceConnected; + + Future connectAndesfitBloodPressureDevice(BluetoothDevice device) async { + device.connectionState.listen((BluetoothConnectionState state) async { + if (state == BluetoothConnectionState.disconnected) { + isAndesfitDeviceConnected = false; + notifyListeners(); + } + if (state == BluetoothConnectionState.connected) { + isAndesfitDeviceConnected = true; + notifyListeners(); + bleDevicesStream.cancel(); + List services = await device.discoverServices(); + services.forEach((service) { + if (service.serviceUuid.toString().toLowerCase() == BLEUtils.BLOOD_PRESSURE_SERVICE) { + print(service.serviceUuid); + service.characteristics.forEach((characteristic) async { + if (characteristic.characteristicUuid.toString().toLowerCase() == BLEUtils.BLOOD_PRESSURE_CHARACTERISTIC) { + print(characteristic.characteristicUuid); + characteristic.onValueReceived.listen((event) { + print("onValueReceived Stream"); + print(event); + + handleAndesfitBloodPressureStream(event, characteristic); + }); + await characteristic.setNotifyValue(true); + } + }); + } + }); + } + }); + await device.connect(timeout: Duration(seconds: 35)); + } + + Future disConnectAndesfitDevice(BluetoothDevice device) async { + selectedAndesFitScanResult = null; + isAndesfitDeviceConnected = null; + device.disconnect(); + } + + ScanResult selectedAndesFitScanResult; + + updateSelectedAndesFitScanResult(var value) { + selectedAndesFitScanResult = value; + notifyListeners(); + } + + BpRtMeasuringModel andesfitBpRtMeasuringModel; + + void updateAndesfitBpRtMeasuringModel(BpRtMeasuringModel model) { + andesfitBpRtMeasuringModel = model; + notifyListeners(); + } + + BpRtResultModel andesfitBpRtResultModel; + + void updateAndesfitBpRtResultModel(BpRtResultModel model) { + andesfitBpRtResultModel = model; + notifyListeners(); + } + + String andesfitBpCurrentStatus; + + updateAndesfitBpCurrentStatus(var value) { + andesfitBpCurrentStatus = value; + notifyListeners(); + } + + void handleAndesfitBloodPressureStream(List byteArray, BluetoothCharacteristic characteristic) { + if (byteArray.length < 12) { + updateAndesfitBpCurrentStatus(kRealTimeDataBPMeasuring); + int pressure = 0; + if (byteArray.length > 1) { + pressure = byteArray[1]; //pressure value + } + updateAndesfitBpRtMeasuringModel(BpRtMeasuringModel(deflate: false, pr: 0, pressure: pressure)); + } else { + updateAndesfitBpCurrentStatus(kRealTimeDataBPResult); + + int sys = 0; + int dias = 0; + int pulse = 0; + bool idPulseHigh = false; + bool isBPHigh = false; + Map results = Map(); + + if (byteArray[1] != 0 && byteArray[3] != 0) { + isBPHigh = false; + sys = byteArray[1]; + dias = byteArray[3]; + } else { + isBPHigh = true; + sys = byteArray[2]; + dias = byteArray[4]; + } + + if (byteArray[8] != 0) { + pulse = byteArray[8]; + idPulseHigh = true; + } else { + pulse = byteArray[9]; + idPulseHigh = false; + } + + updateAndesfitBpRtResultModel(BpRtResultModel( + deflate: false, + pr: pulse, + pressure: 0, + dia: dias, + sys: sys, + isBpHigh: isBPHigh, + isPulseHigh: idPulseHigh, + )); + + if (characteristic.isNotifying) characteristic.setNotifyValue(false); + + print(results); + } + } + + WeightScaleData andesfitWeightScaleData; + + void updateAndesfitWeightScaleData(WeightScaleData model) { + andesfitWeightScaleData = model; + notifyListeners(); + } + + String resultSet1 = ""; + String resultSet2 = ""; + + Future connectAndesfitWeightDevice(BluetoothDevice device) async { + log("deviceToConnect: ${device.toString()}"); + + device.connectionState.listen((BluetoothConnectionState state) async { + if (state == BluetoothConnectionState.disconnected) { + isAndesfitDeviceConnected = false; + notifyListeners(); + } + if (state == BluetoothConnectionState.connected) { + isAndesfitDeviceConnected = true; + notifyListeners(); + bleDevicesStream.cancel(); + List services = await device.discoverServices(); + services.forEach((service) { + log("services in weightscale: ${service.serviceUuid}"); + + if (service.serviceUuid.toString().toLowerCase() == BLEUtils.WEIGHT_MEASUREMENT_SERVICE) { + print(service.serviceUuid); + service.characteristics.forEach((characteristic) async { + if (characteristic.characteristicUuid.toString().toLowerCase() == BLEUtils.WEIGHT_MEASUREMENT_CHARACTERISTIC_FFF1) { + print(characteristic.characteristicUuid); + print(characteristic.properties.toString()); + characteristic.onValueReceived.listen((event) { + print("onValueReceived fff1 Stream"); + print(event); + if (event[0] == 129) { + // Issue result ack command + Future.delayed(Duration(milliseconds: 1000)).then((value) async { + characteristic.write([0x01]).then((value) { + print("----fff1 result ack data written----"); + }); + }); + } + // Error + else if (event[0] == 65) { + AppToast.showErrorToast(message: "Measurement Error Occurred!"); + } else { + //Sample Results + // ----------Result Set 1----------: 42;0;1;0;0;0;0;65;9;b0;d2;0;7f;3;44;0;ac;1;cc;0 + // ----------Result Set 2----------: 42;1;32;0;2c;0;1;3;84;9;1b;0;29;2;9c;0;10;0;e9 + + if (event[1] == 0) { + resultSet1 = convertResultSetToString(event); + print("----------Result Set 1----------: $resultSet1"); + print("----------Result Set 2----------: $resultSet2"); + } else { + resultSet2 = convertResultSetToString(event); + print("----------Result Set 1----------: $resultSet1"); + print("----------Result Set 2----------: $resultSet2"); + parseResultData(); + } + } + }); + await Future.delayed(Duration(milliseconds: 500)).then((value) async { + print("Delayed fff1 done"); + if (!characteristic.isNotifying) await characteristic.setNotifyValue(true).catchError((err) {}); + }); + } + + if (characteristic.characteristicUuid.toString().toLowerCase() == BLEUtils.WEIGHT_MEASUREMENT_CHARACTERISTIC_FFF2) { + print(characteristic.characteristicUuid); + print(characteristic.properties.toString()); + characteristic.onValueReceived.listen((event) { + print("onValueReceived fff2 Stream"); + print(event); + }); + + await Future.delayed(Duration(milliseconds: 1000)).then((value) async { + print("Delayed fff2 done"); + if (!characteristic.isNotifying) await characteristic.setNotifyValue(true).catchError((err) {}); + }); + + // Write user data to fff2 + await Future.delayed(Duration(milliseconds: 1000)).then((value) async { + characteristic.write([0x81, 0x00, 0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0xB2, 0x19, 0x01, 0x00, 0x00], withoutResponse: true).then((value) { + print("----fff2 user data written----"); + }); + + // Issue get result command + await Future.delayed(Duration(milliseconds: 1000)).then((value) async { + characteristic.write([0x41, 0x00, 0x82, 0x00, 0x00, 0x00, 0x00], withoutResponse: true).then((value) { + print("----fff2 get result data written----"); + }); + }); + }); + } + }); + } + }); + } + }); + await device.connect(timeout: Duration(seconds: 35)); + } + + String adjustHexString(String value) { + return value.length == 1 ? "0" + value : value; + } + + void parseResultData() { + List hexStringResSet1 = resultSet1.split(";"); + List hexStringResSet2 = resultSet2.split(";"); + + String hexStringWeight = adjustHexString(hexStringResSet1[13]) + adjustHexString(hexStringResSet1[14]); + String hexStringBMI = adjustHexString(hexStringResSet1[15]) + adjustHexString(hexStringResSet1[16]); + String hexStringFat = adjustHexString(hexStringResSet2[3]) + adjustHexString(hexStringResSet2[4]); + String hexStringBone = adjustHexString(hexStringResSet2[11]) + adjustHexString(hexStringResSet2[12]); + String hexStringWater = adjustHexString(hexStringResSet2[13]) + adjustHexString(hexStringResSet2[14]); + String hexStringMuscle = adjustHexString(hexStringResSet2[7]) + adjustHexString(hexStringResSet2[8]); + String hexStringBMR = adjustHexString(hexStringResSet2[9]) + adjustHexString(hexStringResSet2[10]); + + print((int.parse(hexStringWeight, radix: 16) * 0.1).toStringAsFixed(1)); + print((int.parse(hexStringBMI, radix: 16) * 0.1).toStringAsFixed(1)); + print((int.parse(hexStringFat, radix: 16) * 0.1).toStringAsFixed(1)); + print((int.parse(hexStringMuscle, radix: 16) * 0.1).toStringAsFixed(1)); + print((int.parse(hexStringBMR, radix: 16))); + print((int.parse(hexStringBone, radix: 16) * 0.1).toStringAsFixed(1)); + print((int.parse(hexStringWater, radix: 16) * 0.1).toStringAsFixed(1)); + + var weight = (int.parse(hexStringWeight, radix: 16) * 0.1).toStringAsFixed(1); + var bmi = (int.parse(hexStringBMI, radix: 16) * 0.1).toStringAsFixed(1); + var fat = (int.parse(hexStringFat, radix: 16) * 0.1).toStringAsFixed(1); + var muscle = (int.parse(hexStringMuscle, radix: 16) * 0.1).toStringAsFixed(1); + var bmr = (int.parse(hexStringBMR, radix: 16)).toString(); + var bone = (int.parse(hexStringBone, radix: 16) * 0.1).toStringAsFixed(1); + var water = (int.parse(hexStringWater, radix: 16) * 0.1).toStringAsFixed(1); + updateAndesfitWeightScaleData(WeightScaleData(weight: weight, bmi: bmi, fat: fat, muscle: muscle, bmr: bmr, bone: bone, water: water)); + } + + void parseResultDataTest() { + List hexStringResSet1 = resultSet1.split(";"); + List hexStringResSet2 = resultSet2.split(";"); + + String hexStringWeight = adjustHexString(hexStringResSet1[13]) + adjustHexString(hexStringResSet1[14]); + String hexStringBMI = adjustHexString(hexStringResSet1[15]) + adjustHexString(hexStringResSet1[16]); + String hexStringFat = adjustHexString(hexStringResSet2[3]) + adjustHexString(hexStringResSet2[4]); + String hexStringBone = adjustHexString(hexStringResSet2[11]) + adjustHexString(hexStringResSet2[12]); + String hexStringWater = adjustHexString(hexStringResSet2[13]) + adjustHexString(hexStringResSet2[14]); + String hexStringMuscle = adjustHexString(hexStringResSet2[7]) + adjustHexString(hexStringResSet2[8]); + String hexStringBMR = adjustHexString(hexStringResSet2[9]) + adjustHexString(hexStringResSet2[10]); + + print((int.parse(hexStringWeight, radix: 16) * 0.1).toStringAsFixed(1)); + print((int.parse(hexStringBMI, radix: 16) * 0.1).toStringAsFixed(1)); + print((int.parse(hexStringFat, radix: 16) * 0.1).toStringAsFixed(1)); + print((int.parse(hexStringMuscle, radix: 16) * 0.1).toStringAsFixed(1)); + print((int.parse(hexStringBMR, radix: 16))); + print((int.parse(hexStringBone, radix: 16) * 0.1).toStringAsFixed(1)); + print((int.parse(hexStringWater, radix: 16) * 0.1).toStringAsFixed(1)); + + var weight = (int.parse(hexStringWeight, radix: 16) * 0.1).toStringAsFixed(1); + var bmi = (int.parse(hexStringBMI, radix: 16) * 0.1).toStringAsFixed(1); + var fat = (int.parse(hexStringFat, radix: 16) * 0.1).toStringAsFixed(1); + var muscle = (int.parse(hexStringMuscle, radix: 16) * 0.1).toStringAsFixed(1); + var bmr = (int.parse(hexStringBMR, radix: 16)).toString(); + var bone = (int.parse(hexStringBone, radix: 16) * 0.1).toStringAsFixed(1); + var water = (int.parse(hexStringWater, radix: 16) * 0.1).toStringAsFixed(1); + updateAndesfitWeightScaleData(WeightScaleData(weight: weight, bmi: bmi, fat: fat, muscle: muscle, bmr: bmr, bone: bone, water: water)); + } + + String convertResultSetToString(List byteArray) { + String resSet; + resSet = (byteArray.map( + (x) { + return x.toRadixString(16); + }, + )).join(";"); + return resSet; + } + + String currentTempInCelsius; + + updateCurrentTempInCelsius(var value) { + currentTempInCelsius = value; + notifyListeners(); + } + + Future connectAndesfitTemperatureDevice(BluetoothDevice device) async { + device.connectionState.listen((BluetoothConnectionState state) async { + if (state == BluetoothConnectionState.disconnected) { + isAndesfitDeviceConnected = false; + notifyListeners(); + } + if (state == BluetoothConnectionState.connected) { + isAndesfitDeviceConnected = true; + notifyListeners(); + bleDevicesStream.cancel(); + List services = await device.discoverServices(); + services.forEach((service) { + if (service.serviceUuid.toString().toLowerCase() == BLEUtils.TEMPERATURE_SERVICE) { + print(service.serviceUuid); + service.characteristics.forEach((characteristic) async { + if (characteristic.characteristicUuid.toString().toLowerCase() == BLEUtils.TEMPERATURE_CHARACTERISTIC) { + print(characteristic.characteristicUuid); + characteristic.onValueReceived.listen((event) { + print("onValueReceived Stream"); + print(event); + updateCurrentTempInCelsius(convertIntListToHexTemp(event)); + String currentTempInFahrenheit = ((num.parse(currentTempInCelsius) * 1.8) + 32).toStringAsFixed(1); + String tempCurrentTempInCelsius = currentTempInCelsius + "\u2103" + " / " + currentTempInFahrenheit + "\u2109"; + updateCurrentTempInCelsius(tempCurrentTempInCelsius); + }); + await characteristic.setNotifyValue(true); + } + }); + return true; + } + }); + } + }); + await device.connect(timeout: Duration(seconds: 35)); + } + + String convertIntListToHexTemp(List byteArray) { + String b = (byteArray.map((x) { + return x.toRadixString(16); + })).join(";"); + List hexString = b.split(";"); + print("HexString: $hexString"); + String returnString = hexString[3] + hexString[2] + hexString[1]; + print("Temp Hex String: $returnString"); + final number = int.parse(returnString, radix: 16); + print("Temp Number: ${number * 0.01}"); + + return (number * 0.01).toStringAsFixed(1); + } + + // String convertIntListToHex(List byteArray) { + // String b = (byteArray.map((x) { + // return x.toRadixString(16); + // })).join(";"); + // List hexString = b.split(";"); + // print("HexString: $hexString"); + // String returnString = hexString[2] + hexString[3]; + // print("Temp Hex String: $returnString"); + // final number = int.parse(returnString, radix: 16); + // print("Temp Number: ${number * 0.1}"); + // + // return (number * 0.1).toStringAsFixed(1); + // } + + StreamSubscription bloodSugarValuesStream; + + String currentBloodGlucose; + + updateCurrentBloodGlucose(var value) { + currentBloodGlucose = value; + notifyListeners(); + } + + bool isDataMeasured = false; + + Future connectAndesfitBloodSugarDevice(BluetoothDevice device) async { + device.connectionState.listen((BluetoothConnectionState state) async { + if (state == BluetoothConnectionState.disconnected) { + isAndesfitDeviceConnected = false; + notifyListeners(); + } + if (state == BluetoothConnectionState.connected) { + isAndesfitDeviceConnected = true; + notifyListeners(); + bleDevicesStream.cancel(); + List services = await device.discoverServices(); + services.forEach((service) { + if (service.serviceUuid.toString().toLowerCase() == BLEUtils.BLOOD_GLUCOSE_SERVICE) { + service.characteristics.forEach((characteristic) async { + if (characteristic.characteristicUuid.toString().toLowerCase() == BLEUtils.BLOOD_GLUCOSE_CHARACTERISTIC) { + print(characteristic.characteristicUuid); + bloodSugarValuesStream = characteristic.onValueReceived.listen((event) { + print("onValueReceived Stream"); + print(event); + if (!isDataMeasured) { + updateCurrentBloodGlucose(getBloodGlucoseMeasurements(event)[0]); + } + }); + await characteristic.setNotifyValue(true); + } + }); + return true; + } + }); + } + }); + await device.connect(timeout: Duration(seconds: 35)); + } + + List getBloodGlucoseMeasurements(List byteArray) { + List results = []; + + //[85, 6, 2, 0, 4, 99] + //[85, 12, 3, 23, 9, 12, 12, 25, 0, 75, 0, 2] + + if (byteArray.length == 6) { + int secsRemaining = 0; + secsRemaining = byteArray[4]; + results.add("Measuring: $secsRemaining"); + } else { + num measuredValue = byteArray[9]; + results.add("Measured: $measuredValue"); + isDataMeasured = true; + bloodSugarValuesStream.cancel(); + } + + return results; + } + + BluetoothCharacteristic spirometerReadCharacteristicFf0a; + BluetoothCharacteristic spirometerWriteCharacteristicFf0b; + + String valuePEF; + + String valueFEV; + + Timer timerForSpirometer; + + bool isHistoryCommandWritten = false; + + Future connectAndesfitSpirometerDevice(BluetoothDevice device) async { + device.connectionState.listen((BluetoothConnectionState state) async { + if (state == BluetoothConnectionState.disconnected) { + isAndesfitDeviceConnected = false; + notifyListeners(); + } + if (state == BluetoothConnectionState.connected) { + isAndesfitDeviceConnected = true; + notifyListeners(); + bleDevicesStream.cancel(); + List services = await device.discoverServices(timeout: 5); + services.forEach( + (service) { + if (service.serviceUuid.toString().toLowerCase() == BLEUtils.BLE_TO_MSA100_SERVICE) { + print(service.serviceUuid); + service.characteristics.forEach( + (characteristic) async { + if (characteristic.characteristicUuid.toString().toLowerCase() == BLEUtils.BLE_TO_MSA100_READ_CHARACTERISTIC) { + spirometerReadCharacteristicFf0a = characteristic; + print(spirometerReadCharacteristicFf0a.characteristicUuid); + print(spirometerReadCharacteristicFf0a.properties.toString()); + spirometerReadCharacteristicFf0a.onValueReceived.listen((event) async { + print("onValueReceived ff0a Stream"); + print(event); + + //response received + if (event[0] == 221) { + convertIntListToHexForSpirometer(event); + } + + if (event[0] == 170) { + timerForSpirometer.cancel(); + + // Write get history command to ff0b + if (!isHistoryCommandWritten) { + await Future.delayed(Duration(milliseconds: 1000)).then((value) async { + spirometerWriteCharacteristicFf0b.write([0x55, 0x01], withoutResponse: false).then((value) { + print("----ff0b get history command data written----"); + isHistoryCommandWritten = true; + }); + }); + + // Write get 1st history command to ff0b + await Future.delayed(Duration(milliseconds: 1000)).then((value) async { + spirometerWriteCharacteristicFf0b.write([0x55, 0x02, 0x01, 0x00], withoutResponse: false).then((value) async { + print("----ff0b get 1st history command data written----"); + isHistoryCommandWritten = true; + + // Write delete history command to ff0b + await Future.delayed(Duration(milliseconds: 1000)).then((value) async { + spirometerWriteCharacteristicFf0b.write([0x55, 0x03], withoutResponse: false).then((value) async { + print("----ff0b delete history command data written----"); + isHistoryCommandWritten = true; + await Future.delayed(Duration(milliseconds: 500)).then((value) async { + isHistoryCommandWritten = false; + // Write ACK command to ff0b every 500 ms + startACKCommandToMSA100(); + }); + }); + }); + }); + }); + } + } + }); + + await Future.delayed(Duration(milliseconds: 1000)).then((value) async { + print("Delayed ff0a done"); + if (!spirometerReadCharacteristicFf0a.isNotifying) await spirometerReadCharacteristicFf0a.setNotifyValue(true).catchError((err) {}); + }); + } + + if (characteristic.characteristicUuid.toString().toLowerCase() == BLEUtils.BLE_TO_MSA100_WRITE_CHARACTERISTIC) { + spirometerWriteCharacteristicFf0b = characteristic; + print(spirometerWriteCharacteristicFf0b.characteristicUuid); + print(spirometerWriteCharacteristicFf0b.properties.toString()); + + // Write ACK command to ff0b every 500 ms + startACKCommandToMSA100(); + } + }, + ); + } + }, + ); + } + }); + await device.connect(timeout: Duration(seconds: 35)); + } + + void convertIntListToHexForSpirometer(List byteArray) { + String b = (byteArray.map((x) { + return x.toRadixString(16); + })).join(";"); + List hexString = b.split(";"); + print("HexString: $hexString"); + String returnStringPEF = hexString[8] + hexString[7]; + String returnStringFEV = hexString[6] + hexString[5]; + print("Temp Hex String PEF: $returnStringPEF"); + print("Temp Hex String FEV: $returnStringFEV"); + + final numberPEF = int.parse(returnStringPEF, radix: 16); + final numberFEV = int.parse(returnStringFEV, radix: 16); + + print("Temp Number FEV: ${numberFEV * 0.01}"); + print("Temp Number PEF: $numberPEF"); + + valueFEV = (numberFEV * 0.01).toStringAsFixed(2); + valuePEF = numberPEF.toString(); + notifyListeners(); + } + + void startACKCommandToMSA100() async { + // Write ACK command to ff0b every 500 ms + await Future.delayed(Duration(milliseconds: 1000)).then((value) async { + timerForSpirometer = Timer.periodic(Duration(milliseconds: 500), (Timer t) { + spirometerWriteCharacteristicFf0b.write([0x55, 0x06], withoutResponse: false).then((value) { + print("----ff0b ACK command data written----"); + }); + }); + }); + } + + bool isDeviceSelected = false; + + Future connectAndesfitAllInOneDevice(BluetoothDevice device) async { + log("deviceToConnect: ${device.toString()}"); + } + +//// --> CHECK ME PRO + + Future scanForCheckMe() async { + await BleChannel.scanCheckMeProNative(); + } + + Future startSearchingForCheckMePro() async { + log("selectedTracker in startSearchingForCheckMePro: ${currentSelectedTrackerType.name}"); + await checkBLEPermissions(); + eventChannel.receiveBroadcastStream().listen((event) { + print('Received event---: $event'); + print(event['type']); + if (event['type'] == kDevicesListCheckMe) { + if (Platform.isAndroid) { + List list = [json.decode(event['data'])]; + parsesDevicesList(list, currentSelectedTrackerType); + } else { + parsesDevicesList(json.decode(event['data']), currentSelectedTrackerType); + } + } + if (event['type'] == kCheckMeUsersList) { + if (Platform.isIOS) { + updateCheckMeUserInfoModel(json.decode(event['data'])); + } else { + updateCheckMeUserInfoModel(event['data']); + } + } + if (event['type'] == kTemperatureCheckMePro) { + updateCheckMeTemperatureInfoModel(json.decode(event['data'])); + } + if (event['type'] == kECGCheckMePro) { + updateCheckMeEcgInfoModel(json.decode(event['data'])); + } + if (event['type'] == kECGWaveCheckMePro) { + updateCheckMeEcgWaveInfoModel(json.decode(event['data'])); + } + if (event['type'] == kBPCheckMePro) { + updateCheckMeBPInfoModel(json.decode(event['data'])); + } + if (event['type'] == kOxiCheckMePro) { + updateCheckMeOxiInfoModel(json.decode(event['data'])); + } + if (event['type'] == kGlucoseCheckMePro) { + updateCheckMeGlucoseInfoModel(json.decode(event['data'])); + } + }); + + await scanForCheckMe(); + } + + List checkMeProUsersList; + + void updateCheckMeUserInfoModel(List returnData) { + if (checkMeProUsersList != null) { + checkMeProUsersList.clear(); + } + if (Platform.isIOS) { + checkMeProUsersList = List.generate(returnData.length, (index) => CheckMeUserInfoModel.fromJson(returnData[index])); + } else { + checkMeProUsersList = List.generate(returnData.length, (index) => CheckMeUserInfoModel.fromJson(jsonDecode(returnData[index]))); + } + + notifyListeners(); + } + + getCheckMeUsersListDialog(TrackerTypeEnum trackerTypeEnum, BuildContext context) { + return showDialog( + context: context, + builder: (context) => Container( + decoration: BoxDecoration(borderRadius: BorderRadius.all(Radius.circular(15)), color: Colors.white), + padding: const EdgeInsets.all(20), + margin: const EdgeInsets.symmetric(vertical: 35, horizontal: 30), + child: SingleChildScrollView( + child: Consumer(builder: (BuildContext context, MyTrackersViewModel myTrackersVm, Widget child) { + return Column( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Material( + child: Center( + child: Text( + "Users List", + style: TextStyle( + fontSize: SizeConfig.textMultiplier * 1.8, + fontWeight: FontWeight.w600, + letterSpacing: -0.3, + height: 13 / 10, + ), + ), + ), + ), + if (myTrackersVm.filesLoader) ...[ + Center(child: CircularProgressIndicator()) + ] else if (myTrackersVm.checkMeProUsersList != null && myTrackersVm.checkMeProUsersList.isNotEmpty) ...[ + Material( + child: SizedBox( + height: MediaQuery.of(context).size.height * 0.7, + child: ListView.separated( + itemCount: checkMeProUsersList.length, + separatorBuilder: (context, index) => SizedBox(height: 14), + shrinkWrap: true, + reverse: false, + itemBuilder: (context, index) { + CheckMeUserInfoModel userModel = checkMeProUsersList[index]; + return InkWell( + onTap: () { + if (trackerTypeEnum == TrackerTypeEnum.BloodSugarTracker) { + myTrackersVm.getGlucoseDataFromCheckMePro(userModel.id); + } else if (trackerTypeEnum == TrackerTypeEnum.BloodPressureTracker) { + myTrackersVm.getBPDataFromCheckMePro(userModel.id); + } + Navigator.of(context).pushReplacement(FadePage(page: CheckMeAllInOneInfoScreen(trackerTypeEnum))); + }, + child: Container( + decoration: cardRadius(12), + padding: EdgeInsets.all(12.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text("${userModel.id}. ${userModel.name}"), + mHeight(4.0), + Text("Gender: ${userModel.sex == 1 ? "Female" : "Male"}"), + mHeight(4.0), + Text("Height: ${userModel.height}"), + mHeight(4.0), + Text("Weight: ${userModel.height}"), + ], + ), + ), + ); + }, + ), + ), + ) + ] else ...[ + getNoDataWidget(context), + ] + ], + ); + }), + ), + ), + ); + } + + Future getTempDataFromCheckMePro() async { + if (checkMeTemperatures != null) { + checkMeTemperatures.clear(); + } + await BleChannel.getTempDataFromCheckMePro(); + } + + List checkMeTemperatures; + + void updateCheckMeTemperatureInfoModel(List returnData) { + if (checkMeTemperatures != null) { + checkMeTemperatures.clear(); + } + checkMeTemperatures = List.generate(returnData.length, (index) => CheckMeTemperatureModel.fromJson(returnData[index])); + notifyListeners(); + } + + Future getOxiDataFromCheckMePro() async { + if (checkMeOxi != null) { + checkMeOxi.clear(); + } + await BleChannel.getOxiDataFromCheckMePro(); + } + + List checkMeOxi; + + void updateCheckMeOxiInfoModel(List returnData) { + if (checkMeOxi != null) { + checkMeOxi.clear(); + } + checkMeOxi = List.generate(returnData.length, (index) => CheckMeOxiModel.fromJson(returnData[index])); + notifyListeners(); + } + + Future getECGDataFromCheckMePro() async { + if (checkMeEcg != null) { + checkMeEcg.clear(); + } + await BleChannel.getEcgDataFromCheckMePro(); + } + + List checkMeEcg; + + void updateCheckMeEcgInfoModel(List returnData) { + if (checkMeEcg != null) { + checkMeEcg.clear(); + } + checkMeEcg = List.generate(returnData.length, (index) => CheckMeECGModel.fromJson(returnData[index])); + notifyListeners(); + } + + Future getEcgWaveDataFromCheckMePro(String timeString) async { + if (checkMeECGWave != null) { + checkMeECGWave = null; + } + await BleChannel.getEcgWaveDataFromCheckMePro(timeString); + } + + CheckMeECGWaveModel checkMeECGWave; + + void updateCheckMeEcgWaveInfoModel(returnData) { + if (checkMeECGWave != null) { + checkMeECGWave = null; + } + checkMeECGWave = CheckMeECGWaveModel.fromJson(returnData); + notifyListeners(); + } + + Future getBPDataFromCheckMePro(String userId) async { + if (checkMeBP != null) { + checkMeBP.clear(); + } + await BleChannel.getBPDataFromCheckMePro(userId); + } + + List checkMeBP; + + void updateCheckMeBPInfoModel(List returnData) { + if (checkMeBP != null) { + checkMeBP.clear(); + } + checkMeBP = List.generate(returnData.length, (index) => CheckMeBPModel.fromJson(returnData[index])); + notifyListeners(); + } + + Future getGlucoseDataFromCheckMePro(String userId) async { + if (checkMeGlucose != null) { + checkMeGlucose.clear(); + } + await BleChannel.getGlucoseDataFromCheckMePro(userId); + } + + List checkMeGlucose; + + void updateCheckMeGlucoseInfoModel(List returnData) { + if (checkMeGlucose != null) { + checkMeGlucose.clear(); + } + checkMeGlucose = List.generate(returnData.length, (index) => CheckMeGlucoseModel.fromJson(returnData[index])); + notifyListeners(); + } + + // SMART RING + + Future scanForSmartRing() async { + await BleChannel.scanSmartRingNative(); + } + + Future startSearchingForSmartRing() async { + log("selectedTracker in startSearchingForSmartRing: ${currentSelectedTrackerType.name}"); + await checkBLEPermissions(); + eventChannel.receiveBroadcastStream().listen((event) { + print('Received event---: $event'); + print(event['type']); + if (event['type'] == kDevicesListSmartRing) { + if (Platform.isAndroid) { + List list = [json.decode(event['data'])]; + parsesDevicesList(list, currentSelectedTrackerType); + } else {} + } + + if (event['type'] == kBatteryLevelSmartRing) { + updateSmartRingBatteryLevelModel(json.decode(event['data'])); + } + + if (event['type'] == kTemperatureSmartRing) { + logger.i(event['data']); + updateSmartRingTemperatureHistory(json.decode(event['data'])); + } + + if (event['type'] == kBloodOxygenSmartRing) { + logger.i(event['data']); + updateSmartRingBloodOxygenHistory(json.decode(event['data'])); + } + + if (event['type'] == kHrvSmartRing) { + logger.i(event['data']); + updateSmartRingHrvHistory(json.decode(event['data'])); + } + + if (event['type'] == kStaticHeartRateSmartRing) { + logger.i(event['data']); + updateSmartRingHeartRateStaticHistory(json.decode(event['data'])); + } + + if (event['type'] == kDynamicHeartRateSmartRing) { + logger.i(event['data']); + updateSmartRingHeartRateDynamicHistory(json.decode(event['data'])); + } + + if (event['type'] == kTotalStepCountSmartRing) { + logger.i(event['data']); + updateSmartRingStepCountHistory(json.decode(event['data'])); + } + + if (event['type'] == kRTSportDataSmartRing) { + logger.i(event['data']); + updateRtSportDataSmartRingModel(json.decode(event['data'])); + } + }); + + await scanForSmartRing(); + } + + Future connectSmartRingDevice(BluetoothDevice device) async { + device.connectionState.listen((BluetoothConnectionState state) async { + if (state == BluetoothConnectionState.disconnected) { + isAndesfitDeviceConnected = false; + notifyListeners(); + } + if (state == BluetoothConnectionState.connected) { + isAndesfitDeviceConnected = true; + notifyListeners(); + bleDevicesStream.cancel(); + List services = await device.discoverServices(); + services.forEach((service) { + if (service.serviceUuid.toString().toLowerCase() == BLEUtils.TEMPERATURE_SERVICE) { + print(service.serviceUuid); + service.characteristics.forEach((characteristic) async { + if (characteristic.characteristicUuid.toString().toLowerCase() == BLEUtils.TEMPERATURE_CHARACTERISTIC) { + print(characteristic.characteristicUuid); + characteristic.onValueReceived.listen((event) { + print("onValueReceived Stream"); + print(event); + updateCurrentTempInCelsius(convertIntListToHexTemp(event)); + String currentTempInFahrenheit = ((num.parse(currentTempInCelsius) * 1.8) + 32).toStringAsFixed(1); + String tempCurrentTempInCelsius = currentTempInCelsius + "\u2103" + " / " + currentTempInFahrenheit + "\u2109"; + updateCurrentTempInCelsius(tempCurrentTempInCelsius); + }); + await characteristic.setNotifyValue(true); + } + }); + return true; + } + }); + } + }); + await device.connect(timeout: Duration(seconds: 35)); + } + + Future getTemperatureSmartRing() async { + if (smartRingTemperatureHistory != null) { + smartRingTemperatureHistory.clear(); + smartRingTemperatureHistory = null; + } + String data = await BleChannel.getTemperatureSmartRing(); + logger.i(jsonEncode(data.toString())); + } + + Future getBatteryLevelSmartRing() async { + String data = await BleChannel.getBatteryLevelSmartRing(); + logger.i(jsonEncode(data.toString())); + } + + Future getDynamicHeartRateSmartRing() async { + String data = await BleChannel.getDynamicHeartRateSmartRing(); + logger.i(jsonEncode(data.toString())); + } + + Future getStaticHeartRateSmartRing() async { + String data = await BleChannel.getStaticHeartRateSmartRing(); + logger.i(jsonEncode(data.toString())); + } + + Future getHRVSmartRing() async { + String data = await BleChannel.getHRVSmartRing(); + logger.i(jsonEncode(data.toString())); + } + + Future getBloodOxygenSmartRing() async { + String data = await BleChannel.getBloodOxygenSmartRing(); + logger.i(jsonEncode(data.toString())); + } + + Future getTotalStepCountSmartRing() async { + String data = await BleChannel.getTotalStepCountSmartRing(); + logger.i(jsonEncode(data.toString())); + } + + Future getSportsModeDataSmartRing() async { + String data = await BleChannel.startSportsModeDataSmartRing(); + logger.i(jsonEncode(data.toString())); + } + + Future stopSportsModeDataSmartRing() async { + String data = await BleChannel.stopSportsModeDataSmartRing(); + logger.i(jsonEncode(data.toString())); + } + + String smartRingBatteryLevel; + + void updateSmartRingBatteryLevelModel(dynamic returnData) { + if (smartRingBatteryLevel != null) { + return; + } + Map data = returnData; + smartRingBatteryLevel = (data["dicData"]["batteryLevel"]).toString(); + notifyListeners(); + } + + //TEMPERATURE + + List smartRingTemperatureHistory; + + void updateSmartRingTemperatureHistory(dynamic returnData) { + Map data = returnData; + List response = (data["dicData"]) as List; + + List list = List.generate(response.length, (index) => SmartRingGenericModel.fromJson(response[index], SmartRingOperationsEnum.Temperature)); + if (smartRingTemperatureHistory == null) { + smartRingTemperatureHistory = list; + } else { + smartRingTemperatureHistory.addAll(list); + } + notifyListeners(); + } + + //BLOOD OXYGEN + + List smartRingBloodOxygenHistory; + + void updateSmartRingBloodOxygenHistory(dynamic returnData) { + Map data = returnData; + List response = (data["dicData"]) as List; + + List list = List.generate(response.length, (index) => SmartRingGenericModel.fromJson(response[index], SmartRingOperationsEnum.BloodOxygen)); + if (smartRingBloodOxygenHistory == null) { + smartRingBloodOxygenHistory = list; + } else { + smartRingBloodOxygenHistory.addAll(list); + } + notifyListeners(); + } + + //HEART RATE DYNAMIC + + List smartRingHeartRateDynamicHistory; + + void updateSmartRingHeartRateDynamicHistory(dynamic returnData) { + Map data = returnData; + List response = (data["dicData"]) as List; + + List list = List.generate(response.length, (index) => SmartRingGenericModel.fromJson(response[index], SmartRingOperationsEnum.RealTimeHeartRate)); + if (smartRingHeartRateDynamicHistory == null) { + smartRingHeartRateDynamicHistory = list; + } else { + smartRingHeartRateDynamicHistory.addAll(list); + } + log("smartRingHeartRateDynamicHistory: ${smartRingHeartRateDynamicHistory.length}"); + notifyListeners(); + } + + List smartRingHeartRateStaticHistory; + + void updateSmartRingHeartRateStaticHistory(dynamic returnData) { + Map data = returnData; + List response = (data["dicData"]) as List; + + List list = List.generate(response.length, (index) => SmartRingGenericModel.fromJson(response[index], SmartRingOperationsEnum.HeartRate)); + if (smartRingHeartRateStaticHistory == null) { + smartRingHeartRateStaticHistory = list; + } else { + smartRingHeartRateStaticHistory.addAll(list); + } + log("smartRingHeartRateStaticHistory: ${smartRingHeartRateStaticHistory.length}"); + notifyListeners(); + } + + List smartRingHrvHistory; + + void updateSmartRingHrvHistory(dynamic returnData) { + Map data = returnData; + List response = (data["dicData"]) as List; + + List list = List.generate(response.length, (index) => SmartRingHrvModel.fromJson(response[index])); + if (smartRingHrvHistory == null) { + smartRingHrvHistory = list; + } else { + smartRingHrvHistory.addAll(list); + } + log("smartRingHrvHistory: ${smartRingHrvHistory.length}"); + notifyListeners(); + } + + List smartRingStepCountHistory; + + void updateSmartRingStepCountHistory(dynamic returnData) { + Map data = returnData; + List response = (data["dicData"]) as List; + + List list = List.generate(response.length, (index) => SmartRingStepCountModel.fromJson(response[index])); + if (smartRingStepCountHistory == null) { + smartRingStepCountHistory = list; + } else { + smartRingStepCountHistory.addAll(list); + } + log("smartRingStepCountHistory: ${smartRingStepCountHistory.length}"); + notifyListeners(); + } + + RtSportDataSmartRingModel rtSportDataSmartRingModel; + + void updateRtSportDataSmartRingModel(dynamic returnData) { + Map data = returnData; + rtSportDataSmartRingModel = RtSportDataSmartRingModel.fromJson(data["dicData"] as Map); + log("rtSportDataSmartRingModel: ${data.toString()}"); + notifyListeners(); + } +} diff --git a/lib/pages/medical/my_trackers/spirometer.dart b/lib/pages/medical/my_trackers/spirometer.dart new file mode 100644 index 00000000..f621051b --- /dev/null +++ b/lib/pages/medical/my_trackers/spirometer.dart @@ -0,0 +1,307 @@ +import 'dart:async'; +import 'dart:io'; + +import 'package:diplomaticquarterapp/theme/colors.dart'; +import 'package:diplomaticquarterapp/uitl/ble_utils.dart'; +import 'package:diplomaticquarterapp/uitl/translations_delegate_base.dart'; +import 'package:diplomaticquarterapp/widgets/buttons/defaultButton.dart'; +import 'package:diplomaticquarterapp/widgets/others/app_scaffold_widget.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_blue_plus/flutter_blue_plus.dart'; +import 'package:permission_handler/permission_handler.dart'; + +class SpirometerBLE extends StatefulWidget { + @override + State createState() => _SpirometerBLEState(); +} + +class _SpirometerBLEState extends State { + String connectionStatus = "disconnected"; + BluetoothDevice currentConnectedDevice; + + StreamSubscription bleDevicesStream; + StreamSubscription bleDeviceConnectionStream; + + BluetoothCharacteristic spirometerReadCharacteristicFf0a; + BluetoothCharacteristic spirometerWriteCharacteristicFf0b; + + String valuePEF = "0"; + String valueFEV = "0.0"; + + final bleConnectionStatus = ValueNotifier("Disconnected"); + + Timer _timer; + + Timer _timerRead; + + bool isHistoryCommandWritten = false; + + @override + void dispose() { + super.dispose(); + if (currentConnectedDevice != null) currentConnectedDevice.disconnect(); + if (_timer != null && _timer.isActive) _timer.cancel(); + if (_timerRead != null && _timerRead.isActive) _timerRead.cancel(); + bleConnectionStatus.dispose(); + if (bleDevicesStream != null) bleDevicesStream.cancel(); + if (bleDeviceConnectionStream != null) bleDeviceConnectionStream.cancel(); + } + + @override + Widget build(BuildContext context) { + return AppScaffold( + appBarTitle: TranslationBase.of(context).spirometer, + showNewAppBar: true, + isShowDecPage: false, + showNewAppBarTitle: true, + backgroundColor: Color(0xffF8F8F8), + body: SingleChildScrollView( + child: StreamBuilder( + stream: FlutterBluePlus.adapterState, + initialData: BluetoothAdapterState.unknown, + builder: (c, snapshot) { + final adapterState = snapshot.data; + if (adapterState == BluetoothAdapterState.on) { + return Container( + margin: EdgeInsets.only(top: 200.0, left: 50.0, right: 50.0), + child: Column( + children: [ + Center( + child: DefaultButton( + TranslationBase.of(context).start.toUpperCase(), + () { + checkBLEPermissions(); + }, + color: CustomColors.green, + ), + ), + SizedBox( + height: 50.0, + ), + ValueListenableBuilder( + valueListenable: bleConnectionStatus, + builder: (context, value, _) { + return Text("Connection state: $value"); + }, + ), + SizedBox( + height: 50.0, + ), + Text("Current PEF: $valuePEF L/min"), + Text("Current FEV: $valueFEV mL"), + ], + ), + ); + } else { + FlutterBluePlus.stopScan(); + return SizedBox(height: 300.0, child: BluetoothOffScreen(adapterState: adapterState)); + } + }), + ), + ); + } + + void checkBLEPermissions() { + [Permission.location, Permission.storage, Permission.bluetooth, Permission.bluetoothConnect, Permission.bluetoothScan].request().then((status) { + startBLEConnection(); + }); + } + + void startBLEConnection() { + if (FlutterBluePlus.isScanningNow == false) { + bleConnectionStatus.value = "Connecting..."; + + bleDevicesStream = FlutterBluePlus.scanResults.listen( + (results) { + List blueToothDevices = results; + blueToothDevices.forEach( + (element) { + if (element.device.localName.isNotEmpty) { + if (element.device.localName.toLowerCase() == "ble-msa") { + bleDevicesStream.cancel(); + element.device.connect(timeout: Duration(seconds: 5), autoConnect: false).then((value) async { + bleConnectionStatus.value = "Connected..."; + print("Device Connected-------"); + currentConnectedDevice = element.device; + bleDeviceConnectionStream = currentConnectedDevice.connectionState.listen((event) { + if (event == BluetoothConnectionState.disconnected) { + bleConnectionStatus.value = "Disconnected..."; + print("Device Disconnected-------"); + if (_timer.isActive) _timer.cancel(); + } + }); + FlutterBluePlus.stopScan(); + List services = await element.device.discoverServices(timeout: 5); + services.forEach( + (service) { + if (service.serviceUuid.toString().toLowerCase() == BLEUtils.BLE_TO_MSA100_SERVICE) { + print(service.serviceUuid); + service.characteristics.forEach( + (characteristic) async { + if (characteristic.characteristicUuid.toString().toLowerCase() == BLEUtils.BLE_TO_MSA100_READ_CHARACTERISTIC) { + spirometerReadCharacteristicFf0a = characteristic; + print(spirometerReadCharacteristicFf0a.characteristicUuid); + print(spirometerReadCharacteristicFf0a.properties.toString()); + spirometerReadCharacteristicFf0a.onValueReceived.listen((event) async { + print("onValueReceived ff0a Stream"); + print(event); + + //response received + if (event[0] == 221) { + convertIntListToHex(event); + } + + if (event[0] == 170) { + _timer.cancel(); + + // Write get history command to ff0b + if (!isHistoryCommandWritten) { + await Future.delayed(Duration(milliseconds: 1000)).then((value) async { + spirometerWriteCharacteristicFf0b.write([0x55, 0x01], withoutResponse: false).then((value) { + print("----ff0b get history command data written----"); + isHistoryCommandWritten = true; + }); + }); + + // Write get 1st history command to ff0b + await Future.delayed(Duration(milliseconds: 1000)).then((value) async { + spirometerWriteCharacteristicFf0b.write([0x55, 0x02, 0x01, 0x00], withoutResponse: false).then((value) async { + print("----ff0b get 1st history command data written----"); + isHistoryCommandWritten = true; + + // Write delete history command to ff0b + await Future.delayed(Duration(milliseconds: 1000)).then((value) async { + spirometerWriteCharacteristicFf0b.write([0x55, 0x03], withoutResponse: false).then((value) async { + print("----ff0b delete history command data written----"); + isHistoryCommandWritten = true; + await Future.delayed(Duration(milliseconds: 500)).then((value) async { + isHistoryCommandWritten = false; + // Write ACK command to ff0b every 500 ms + startACKCommandToMSA100(); + }); + }); + }); + }); + }); + } + } + }); + + await Future.delayed(Duration(milliseconds: 1000)).then((value) async { + print("Delayed ff0a done"); + if (!spirometerReadCharacteristicFf0a.isNotifying) await spirometerReadCharacteristicFf0a.setNotifyValue(true).catchError((err) {}); + }); + } + + if (characteristic.characteristicUuid.toString().toLowerCase() == BLEUtils.BLE_TO_MSA100_WRITE_CHARACTERISTIC) { + spirometerWriteCharacteristicFf0b = characteristic; + print(spirometerWriteCharacteristicFf0b.characteristicUuid); + print(spirometerWriteCharacteristicFf0b.properties.toString()); + + // Write ACK command to ff0b every 500 ms + startACKCommandToMSA100(); + + // await Future.delayed(Duration(milliseconds: 1000)).then((value) async { + // _timer = Timer.periodic(Duration(milliseconds: 500), (Timer t) { + // spirometerWriteCharacteristicFf0b.write([0x55, 0x06], withoutResponse: false).then((value) { + // print("----ff0b ACK command data written----"); + // }); + // }); + // }); + } + }, + ); + } + }, + ); + }); + } + } + }, + ); + }, + // onError(e) => print(e); + ); + FlutterBluePlus.startScan(timeout: const Duration(seconds: 5), androidUsesFineLocation: false); + } + } + + void startACKCommandToMSA100() async { + // Write ACK command to ff0b every 500 ms + await Future.delayed(Duration(milliseconds: 1000)).then((value) async { + _timer = Timer.periodic(Duration(milliseconds: 500), (Timer t) { + spirometerWriteCharacteristicFf0b.write([0x55, 0x06], withoutResponse: false).then((value) { + print("----ff0b ACK command data written----"); + }); + }); + }); + } + + String convertIntListToHex(List byteArray) { + String b = (byteArray.map((x) { + return x.toRadixString(16); + })).join(";"); + List hexString = b.split(";"); + print("HexString: $hexString"); + String returnStringPEF = hexString[8] + hexString[7]; + String returnStringFEV = hexString[6] + hexString[5]; + print("Temp Hex String PEF: $returnStringPEF"); + print("Temp Hex String FEV: $returnStringFEV"); + + final numberPEF = int.parse(returnStringPEF, radix: 16); + final numberFEV = int.parse(returnStringFEV, radix: 16); + + print("Temp Number FEV: ${numberFEV * 0.01}"); + print("Temp Number PEF: $numberPEF"); + + setState(() { + valueFEV = (numberFEV * 0.01).toStringAsFixed(2); + valuePEF = numberPEF.toString(); + }); + + return (numberFEV * 0.01).toStringAsFixed(1); + } +} + +class BluetoothOffScreen extends StatelessWidget { + const BluetoothOffScreen({Key key, this.adapterState}) : super(key: key); + + final BluetoothAdapterState adapterState; + + @override + Widget build(BuildContext context) { + return ScaffoldMessenger( + child: Scaffold( + backgroundColor: Colors.lightBlue, + body: Center( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + const Icon( + Icons.bluetooth_disabled, + size: 200.0, + color: Colors.white54, + ), + Text( + 'Bluetooth Adapter is ${adapterState != null ? adapterState.toString().split(".").last + ", Please turn on your bluetooth to continue." : 'not available'}.', + textAlign: TextAlign.center, + style: Theme.of(context).primaryTextTheme.titleSmall?.copyWith(color: Colors.white), + ), + if (Platform.isAndroid) + ElevatedButton( + child: const Text('TURN ON'), + onPressed: () async { + try { + if (Platform.isAndroid) { + await FlutterBluePlus.turnOn(); + } + } catch (e) {} + }, + ), + ], + ), + ), + ), + ); + } +} diff --git a/lib/pages/medical/my_trackers/temperature.dart b/lib/pages/medical/my_trackers/temperature.dart new file mode 100644 index 00000000..f8c77790 --- /dev/null +++ b/lib/pages/medical/my_trackers/temperature.dart @@ -0,0 +1,152 @@ +import 'dart:io'; + +import 'package:diplomaticquarterapp/theme/colors.dart'; +import 'package:diplomaticquarterapp/uitl/ble_utils.dart'; +import 'package:diplomaticquarterapp/uitl/bluetooth_off.dart'; +import 'package:diplomaticquarterapp/uitl/translations_delegate_base.dart'; +import 'package:diplomaticquarterapp/widgets/buttons/defaultButton.dart'; +import 'package:diplomaticquarterapp/widgets/others/app_scaffold_widget.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_blue_plus/flutter_blue_plus.dart'; +import 'package:permission_handler/permission_handler.dart'; + +class TemperatureHomePage extends StatefulWidget { + @override + State createState() => _TemperatureHomePageState(); +} + +class _TemperatureHomePageState extends State { + String connectionStatus = "disconnected"; + String currentTempInCelsius = "0.0"; + + BluetoothDevice currentConnectedDevice; + + @override + void dispose() { + super.dispose(); + currentConnectedDevice.disconnect(timeout: 15); + } + + @override + Widget build(BuildContext context) { + return AppScaffold( + appBarTitle: TranslationBase.of(context).temperature, + showNewAppBar: true, + isShowDecPage: false, + showNewAppBarTitle: true, + backgroundColor: Color(0xffF8F8F8), + body: SingleChildScrollView( + child: StreamBuilder( + stream: FlutterBluePlus.adapterState, + initialData: BluetoothAdapterState.unknown, + builder: (c, snapshot) { + final adapterState = snapshot.data; + if (adapterState == BluetoothAdapterState.on) { + return Container( + margin: EdgeInsets.only(top: 200.0, left: 50.0, right: 50.0), + child: Column( + children: [ + Center( + child: DefaultButton( + TranslationBase.of(context).start.toUpperCase(), + () { + checkBLEPermissions(); + }, + color: CustomColors.green, + ), + ), + SizedBox( + height: 50.0, + ), + Text("Connection state: $connectionStatus"), + SizedBox( + height: 50.0, + ), + Text("Current Temp: $currentTempInCelsius"), + ], + ), + ); + } else { + FlutterBluePlus.stopScan(); + return SizedBox(height: 300.0, child: BluetoothOffScreen(adapterState: adapterState)); + } + }), + ), + ); + } + + void checkBLEPermissions() { + [Permission.location, Permission.storage, Permission.bluetooth, Permission.bluetoothConnect, Permission.bluetoothScan].request().then((status) { + startBLEConnection(); + }); + } + + void startBLEConnection() { + if (FlutterBluePlus.isScanningNow == false) { + setState(() { + connectionStatus = "Connecting..."; + }); + + FlutterBluePlus.startScan(timeout: const Duration(seconds: 5), androidUsesFineLocation: false).then((value) { + List blueToothDevices = []; + // List blueToothDevices = value; + blueToothDevices.forEach((element) async { + if (element.device.localName.isNotEmpty) { + if (element.device.localName.toLowerCase() == "temp") { + element.device.connectionState.listen((BluetoothConnectionState state) async { + setState(() { + connectionStatus = state.toString(); + }); + if (state == BluetoothConnectionState.disconnected) { + // typically, start a periodic timer that tries to periodically reconnect. + // Note: you must always re-discover services after disconnection! + } + if (state == BluetoothConnectionState.connected) { + currentConnectedDevice = element.device; + List services = await element.device.discoverServices(); + services.forEach((service) { + if (service.serviceUuid.toString().toLowerCase() == BLEUtils.TEMPERATURE_SERVICE) { + print(service.serviceUuid); + service.characteristics.forEach((characteristic) async { + if (characteristic.characteristicUuid.toString().toLowerCase() == BLEUtils.TEMPERATURE_CHARACTERISTIC) { + print(characteristic.characteristicUuid); + characteristic.onValueReceived.listen((event) { + print("onValueReceived Stream"); + print(event); + setState(() { + currentTempInCelsius = convertIntListToHex(event); + String currentTempInFahrenheit = ((num.parse(currentTempInCelsius) * 1.8) + 32).toStringAsFixed(1); + currentTempInCelsius = currentTempInCelsius + "\u2103" + " / " + currentTempInFahrenheit + "\u2109"; + }); + }); + await characteristic.setNotifyValue(true); + } + }); + return true; + } + }); + } + }); + await element.device.connect(timeout: Duration(seconds: 35)); + return true; + } + } + }); + }); + } + } + + String convertIntListToHex(List byteArray) { + String b = (byteArray.map((x) { + return x.toRadixString(16); + })).join(";"); + List hexString = b.split(";"); + print("HexString: $hexString"); + String returnString = hexString[3] + hexString[2] + hexString[1]; + print("Temp Hex String: $returnString"); + final number = int.parse(returnString, radix: 16); + print("Temp Number: ${number * 0.01}"); + + return (number * 0.01).toStringAsFixed(1); + } +} diff --git a/lib/pages/medical/my_trackers/viatom_devices/bp_tracker_ble.dart b/lib/pages/medical/my_trackers/viatom_devices/bp_tracker_ble.dart new file mode 100644 index 00000000..58bb3e83 --- /dev/null +++ b/lib/pages/medical/my_trackers/viatom_devices/bp_tracker_ble.dart @@ -0,0 +1,268 @@ + +import 'dart:convert'; + +import 'package:diplomaticquarterapp/pages/medical/my_trackers/ble_models/viatom_devices/bp_rt_model.dart'; +import 'package:diplomaticquarterapp/uitl/utils_new.dart'; +import 'package:diplomaticquarterapp/pages/medical/my_trackers/ble_helpers/ble_connect_helper.dart'; +import 'package:diplomaticquarterapp/widgets/buttons/defaultButton.dart'; +import 'package:diplomaticquarterapp/widgets/others/app_scaffold_widget.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:permission_handler/permission_handler.dart'; + +class BpTrackerBLE extends StatefulWidget { + @override + State createState() => _BpTrackerBLEState(); +} + +class _BpTrackerBLEState extends State { + EventChannel eventChannel = EventChannel('BLE-Platform-Bridge-Event'); + + final bpDataNotifierMeasuring = ValueNotifier(BpRtMeasuringModel()); + final bpDataNotifierResult = ValueNotifier(BpRtResultModel()); + final bpStatusNotifier = ValueNotifier("status"); + final bpDeviceDataNotifier = ValueNotifier(""); + String deviceName = "BP2"; + + @override + void dispose() { + bpDataNotifierMeasuring.dispose(); + bpDataNotifierResult.dispose(); + bpStatusNotifier.dispose(); + super.dispose(); + // BleChannel.disconnect(); + } + + @override + void initState() { + // TODO: implement initState + super.initState(); + } + + @override + Widget build(BuildContext context) { + return AppScaffold( + appBarTitle: "BP Tracker", + showNewAppBar: true, + isShowDecPage: false, + showNewAppBarTitle: true, + backgroundColor: Color(0xffF8F8F8), + body: Padding( + padding: const EdgeInsets.all(24.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Expanded( + child: ListView( + children: [ + ValueListenableBuilder( + valueListenable: bpDeviceDataNotifier, + builder: (context, value, _) { + return Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + Text( + value == null || value.isEmpty ? "" : "Connected", + style: TextStyle(fontSize: 18), + ), + Text( + "$value", + style: TextStyle(fontSize: 18), + ), + ], + ); + }, + ), + ValueListenableBuilder( + valueListenable: bpStatusNotifier, + builder: (context, value, _) { + if (value == "RealTimeDataBP2Measuring") { + return ValueListenableBuilder( + valueListenable: bpDataNotifierMeasuring, + builder: (context, BpRtMeasuringModel bpRtDataMeasuringModel, _) { + return Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + mHeight(24.0), + Column( + children: [ + Text(bpRtDataMeasuringModel.pressure.toString(), style: TextStyle(fontSize: 100, fontWeight: FontWeight.bold)), + Text("Pressure", style: TextStyle(fontSize: 20)), + ], + ), + mHeight(24.0), + ], + ); + }, + ); + } else if (value == "RealTimeDataBP2Result") { + return ValueListenableBuilder( + valueListenable: bpDataNotifierResult, + builder: (context, BpRtResultModel bpRtDataResultModel, _) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + mHeight(24.0), + Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + Column( + children: [ + Text("Dia", style: TextStyle(fontSize: 20)), + Text(bpRtDataResultModel.dia.toString(), style: TextStyle(fontSize: 50, fontWeight: FontWeight.bold)), + Text("mmHg", style: TextStyle(fontSize: 10)), + ], + ), + Column( + children: [ + Text("Sys", style: TextStyle(fontSize: 20)), + Text(bpRtDataResultModel.sys.toString(), style: TextStyle(fontSize: 50, fontWeight: FontWeight.bold)), + Text("mmHg", style: TextStyle(fontSize: 10)), + ], + ), + Column( + children: [ + Text("♥︎", style: TextStyle(fontSize: 20)), + Text(bpRtDataResultModel.pr.toString(), style: TextStyle(fontSize: 50, fontWeight: FontWeight.bold)), + Text("/min", style: TextStyle(fontSize: 10)), + ], + ), + ], + ), + mHeight(24.0), + ], + ); + }, + ); + } + + return Padding( + padding: const EdgeInsets.all(24.0), + child: Center( + child: Text( + "Some animation with the instruction", + style: TextStyle(fontSize: 9.0), + ), + ), + ); + }, + ), + ], + ), + ), + ValueListenableBuilder( + valueListenable: bpDeviceDataNotifier, + builder: (context, value, _) { + if (value == null || value.isEmpty) { + return Row( + children: [ + Expanded( + child: DefaultButton( + "Start Scan", + () async { + checkBLEPermissions(); + }, + textColor: Colors.white, + color: Colors.green, + ), + ), + ], + ); + } + return Row( + children: [ + Expanded( + child: DefaultButton( + "Disconnect with $value", + () async { + bpDataNotifierMeasuring.dispose(); + bpDataNotifierResult.dispose(); + + bpStatusNotifier.dispose(); + BleChannel.disconnect(); + }, + textColor: Colors.white, + ), + ), + ], + ); + return Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + Text( + value != null || value.isEmpty ? "" : "Connected", + style: TextStyle(fontSize: 18), + ), + Text( + "$value", + style: TextStyle(fontSize: 18), + ), + ], + ); + }, + ), + ], + ), + ), + ); + } + + Widget getFilesListWidget() { + return SizedBox(); + // return ListView.separated( + // shrinkWrap: true, + // physics: ScrollPhysics(), + // reverse: true, + // itemBuilder: (context, index) { + // return InkWell( + // onTap: () { + // showEKGFileDetails(ekgFileDetailResponseModelList[index]); + // }, + // child: getECGFileCard(ekgFileDetailResponseModelList[index])); + // // child: Text(ekgFileDetailResponseModelList[index].fileName)); + // }, + // itemCount: ekgFileDetailResponseModelList.length, + // separatorBuilder: (context, index) => SizedBox(height: 14), + // ); + } + + void checkBLEPermissions() async { + [Permission.location, Permission.storage, Permission.bluetooth, Permission.bluetoothConnect, Permission.bluetoothScan].request().then((status) async { + // startBLEConnection(); + + eventChannel.receiveBroadcastStream().listen((event) { + print('Received event---: $event'); + print(event['type']); + if (event['type'] == "infoData") { + bpStatusNotifier.value = "infoData"; + bpDeviceDataNotifier.value = event['deviceName']; + } + if (event['type'] == "fileList") { + // parseEKGFilesList(event['data']); + } + if (event['type'] == 'fileDetail') { + print("Received file data ---:"); + print(event['data']); + } + if (event['type'] == "RealTimeDataBP2Measuring") { + parseBpRtMeasuringObject(event['data']); + bpStatusNotifier.value = "RealTimeDataBP2Measuring"; + } + if (event['type'] == "RealTimeDataBP2Result") { + parseBpRtResultObject(event['data']); + bpStatusNotifier.value = "RealTimeDataBP2Result"; + } + }); + await BleChannel.scanResultsNative(["bloodpressure", "BP2"]); + }); + } + + void parseBpRtMeasuringObject(dynamic returnData) { + bpDataNotifierMeasuring.value = BpRtMeasuringModel.fromJson(json.decode(returnData)); + } + + void parseBpRtResultObject(dynamic returnData) { + bpDataNotifierResult.value = BpRtResultModel.fromJson(json.decode(returnData)); + } +} diff --git a/lib/pages/medical/my_trackers/viatom_devices/ekg_chart_view.dart b/lib/pages/medical/my_trackers/viatom_devices/ekg_chart_view.dart new file mode 100644 index 00000000..425abbfb --- /dev/null +++ b/lib/pages/medical/my_trackers/viatom_devices/ekg_chart_view.dart @@ -0,0 +1,110 @@ +import 'package:diplomaticquarterapp/pages/medical/my_trackers/ble_models/viatom_devices/ecg_file_detail_model.dart'; +import 'package:diplomaticquarterapp/widgets/others/app_scaffold_widget.dart'; +import 'package:fl_chart/fl_chart.dart'; +import 'package:flutter/material.dart'; + +class EKGChartView extends StatefulWidget { + ECGFileDetailModel ekgFileDetailResponseModel; + + EKGChartView({@required this.ekgFileDetailResponseModel}); + + @override + State createState() => _EKGChartViewState(); +} + +class _EKGChartViewState extends State { + @override + void initState() { + super.initState(); + + print(widget.ekgFileDetailResponseModel.fileName); + } + + @override + Widget build(BuildContext context) { + List list = widget.ekgFileDetailResponseModel.shortData; + List> mainList = chunkIntList(list, 1250); + + return AppScaffold( + appBarTitle: "ECG", + showNewAppBar: true, + isShowDecPage: false, + showNewAppBarTitle: true, + backgroundColor: Color(0xffF8F8F8), + body: ListView.separated( + shrinkWrap: true, + physics: ScrollPhysics(), + itemBuilder: (context, index) { + return Padding( + padding: EdgeInsets.only(right: MediaQuery.of(context).size.width - (MediaQuery.of(context).size.width * (mainList[index].length / 1250))), + child: SizedBox( + height: 120.0, + child: LineChart( + LineChartData( + lineTouchData: LineTouchData(handleBuiltInTouches: false), + gridData: FlGridData( + show: true, + verticalInterval: 30, + horizontalInterval: 30, + getDrawingVerticalLine: (value) { + return FlLine( + color: Colors.red[300], + strokeWidth: 0.4, + ); + }, + getDrawingHorizontalLine: (value) { + return FlLine( + color: Colors.red[300], + strokeWidth: 0.4, + ); + }, + ), + titlesData: FlTitlesData(show: false), + borderData: FlBorderData( + show: false, + border: Border.all(color: const Color(0xff37434d), width: 1), + ), + minX: 0, + maxX: (mainList[index].length.toDouble() - 1), + minY: list.reduce((value, element) => value < element ? value : element).toDouble(), + maxY: list.reduce((value, element) => value > element ? value : element).toDouble(), + lineBarsData: [ + LineChartBarData( + isCurved: false, + preventCurveOverShooting: true, + barWidth: 0.5, + dotData: FlDotData(show: false), + spots: getDataList(mainList[index]), + colors: [Colors.grey[800]], + isStrokeCapRound: true, + belowBarData: BarAreaData(show: false), + ), + ], + ), + ), + ), + ); + }, + itemCount: mainList.length, + separatorBuilder: (context, index) => SizedBox(height: 14), + ), + ); + } + + List> chunkIntList(List list, int chunkSize) { + List> chunks = []; + for (int i = 0; i < list.length; i += chunkSize) { + int end = i + chunkSize; + chunks.add(list.sublist(i, end > list.length ? list.length : end)); + } + return chunks; + } + + List getDataList(List list) { + List spotsList = []; + for (int i = 0; i < list.length; i++) { + spotsList.add(FlSpot(i.toDouble(), list[i].toDouble())); + } + return spotsList; + } +} diff --git a/lib/pages/medical/my_trackers/viatom_devices/ekg_tracker_ble.dart b/lib/pages/medical/my_trackers/viatom_devices/ekg_tracker_ble.dart new file mode 100644 index 00000000..cd4697cb --- /dev/null +++ b/lib/pages/medical/my_trackers/viatom_devices/ekg_tracker_ble.dart @@ -0,0 +1,293 @@ +import 'dart:async'; +import 'dart:convert'; +import 'package:diplomaticquarterapp/pages/medical/my_trackers/ble_models/viatom_devices/ecg_file_detail_model.dart'; +import 'package:diplomaticquarterapp/pages/medical/my_trackers/ble_models/viatom_devices/ecg_rt_model.dart'; +import 'package:diplomaticquarterapp/pages/medical/my_trackers/viatom_devices/ekg_chart_view.dart'; +import 'package:diplomaticquarterapp/uitl/date_uitl.dart'; +import 'package:diplomaticquarterapp/uitl/utils_new.dart'; +import 'package:diplomaticquarterapp/pages/medical/my_trackers/ble_helpers/ble_connect_helper.dart'; +import 'package:diplomaticquarterapp/widgets/buttons/defaultButton.dart'; +import 'package:diplomaticquarterapp/widgets/others/app_scaffold_widget.dart'; +import 'package:diplomaticquarterapp/widgets/transitions/fade_page.dart'; +import 'package:fl_chart/fl_chart.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; + +import 'package:syncfusion_flutter_charts/charts.dart'; +import 'dart:math' as math; + +class EKG_BLE extends StatefulWidget { + const EKG_BLE(); + + @override + State createState() => _EKG_BLEState(); +} + +class _EKG_BLEState extends State { + EventChannel eventChannel = EventChannel('BLE-Platform-Bridge-Event'); + + // EventChannel eventChannelIOSEKG = EventChannel('BLE-Platform-Bridge-IOS-EKG'); + + String receivedData = ''; + + final ekgValueNotifier = ValueNotifier("start"); + + List ekgFilesList = []; + + List ekgFileDetailResponseModelList = []; + + ECGRtModel ekgRealTimeDataResponseModel; + + List ecgBytesAllDataList = []; + + Timer timer; + List<_ChartData> chartData; + int count; + ChartSeriesController _chartSeriesController; + + @override + void dispose() { + ekgValueNotifier.dispose(); + super.dispose(); + BleChannel.disconnect(); + timer?.cancel(); + chartData.clear(); + _chartSeriesController = null; + } + + @override + void initState() { + // TODO: implement initState + + count = 19; + chartData = <_ChartData>[ + _ChartData(0, 42), + _ChartData(1, 47), + _ChartData(2, 33), + _ChartData(3, 49), + _ChartData(4, 54), + _ChartData(5, 41), + _ChartData(6, 58), + _ChartData(7, 51), + _ChartData(8, 98), + _ChartData(9, 41), + _ChartData(10, 53), + _ChartData(11, 72), + _ChartData(12, 86), + _ChartData(13, 52), + _ChartData(14, 94), + _ChartData(15, 92), + _ChartData(16, 86), + _ChartData(17, 72), + _ChartData(18, 94), + ]; + timer = Timer.periodic(const Duration(milliseconds: 1000), _updateDataSource); + super.initState(); + } + + SfCartesianChart _buildLiveLineChart() { + return SfCartesianChart( + plotAreaBorderWidth: 0, + primaryXAxis: NumericAxis(majorGridLines: const MajorGridLines(width: 0)), + primaryYAxis: NumericAxis(axisLine: const AxisLine(width: 0), majorTickLines: const MajorTickLines(size: 0)), + series: >[ + LineSeries<_ChartData, int>( + onRendererCreated: (ChartSeriesController controller) { + _chartSeriesController = controller; + }, + dataSource: chartData, + color: const Color.fromRGBO(192, 108, 132, 1), + xValueMapper: (_ChartData sales, _) => sales.country, + yValueMapper: (_ChartData sales, _) => sales.sales, + animationDuration: 2000.0) + ]); + } + + void _updateDataSource(Timer timer) { + // if (isCardView != null) { + chartData.add(_ChartData(count, _getRandomInt(10, 100))); + if (chartData.length == 20) { + chartData.removeAt(0); + _chartSeriesController?.updateDataSource( + addedDataIndexes: [chartData.length - 1], + removedDataIndexes: [0], + ); + } else { + _chartSeriesController?.updateDataSource( + addedDataIndexes: [chartData.length - 1], + ); + } + count = count + 1; + // } + } + + ///Get the random data + int _getRandomInt(int min, int max) { + final math.Random random = math.Random(); + return min + random.nextInt(max - min); + } + + @override + Widget build(BuildContext context) { + return AppScaffold( + appBarTitle: "ECG Tracker", + showNewAppBar: true, + isShowDecPage: false, + showNewAppBarTitle: true, + backgroundColor: Color(0xffF8F8F8), + body: SingleChildScrollView( + child: Padding( + padding: const EdgeInsets.all(24.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + Expanded( + child: DefaultButton( + "Get Info", + () async { + eventChannel.receiveBroadcastStream().listen((event) { + print('Received event---: $event'); + print(event['type']); + if (event['type'] == "infoData") { + ekgValueNotifier.value = event['data']; + } + if (event['type'] == "fileList") { + parseEKGFilesList(event['data']); + } + if (event['type'] == 'fileDetail') { + print("Received file data ---:"); + print(event['data']); + parseEKGFileDetailObject(event['data']); + } + if (event['type'] == "realtimeDataECG") { + ekgValueNotifier.value = event['data']; + parseEKGRealTimeDataObject(event['data']); + } + }); + // eventChannelIOSEKG.receiveBroadcastStream().listen((event) { + // print('Received eventChannelIOSEKG event---: $event'); + // }); + await BleChannel.scanResultsNative(["ekg", "DuoEK"]); + }, + textColor: Colors.white, + ), + ), + mWidth(16.0), + Expanded( + child: DefaultButton( + "Get Files List", + () async { + // await BleChannel.getECGFilesList(); + }, + textColor: Colors.white, + ), + ), + ], + ), + mHeight(20.0), + // _buildLiveLineChart() + ValueListenableBuilder( + valueListenable: ekgValueNotifier, + builder: (context, value, _) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + value, + style: TextStyle(fontSize: 9.0), + ), + mHeight(24.0), + getFilesListWidget(), + mHeight(20.0), + ], + ); + }, + ), + ], + ), + ), + ), + ); + } + + void parseEKGFilesList(List filesListArray) { + ekgFilesList.clear(); + ekgFilesList = filesListArray.toString().replaceAll("[", "").replaceAll("]", "").split(","); + ekgFilesList.removeWhere((element) => element.contains("a")); // removing analysis files from the list + ekgValueNotifier.value = ekgFilesList.toString(); + } + + List getDataList(List list) { + ecgBytesAllDataList.add(list[0]); + + List spotsList = []; + for (int i = 0; i < ecgBytesAllDataList.length; i++) { + spotsList.add(FlSpot(i.toDouble(), ecgBytesAllDataList[i].toDouble())); + } + return spotsList; + } + + Widget getFilesListWidget() { + return ListView.separated( + shrinkWrap: true, + physics: ScrollPhysics(), + reverse: true, + itemBuilder: (context, index) { + return InkWell( + onTap: () { + showEKGFileDetails(ekgFileDetailResponseModelList[index]); + }, + child: getECGFileCard(ekgFileDetailResponseModelList[index])); + // child: Text(ekgFileDetailResponseModelList[index].fileName)); + }, + itemCount: ekgFileDetailResponseModelList.length, + separatorBuilder: (context, index) => SizedBox(height: 14), + ); + } + + Widget getECGFileCard(ECGFileDetailModel ekgFileDetailResponseModel) { + return Container( + decoration: cardRadius(12), + padding: EdgeInsets.all(12.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text(ekgFileDetailResponseModel.fileName), + mHeight(6.0), + Text("Duration: ${ekgFileDetailResponseModel.duration} seconds"), + mHeight(6.0), + Text("Date: ${DateUtil.getDayMonthYearHourMinuteDateFormatted(DateTime.fromMillisecondsSinceEpoch((ekgFileDetailResponseModel.startTime * 1000)))}"), + ], + ), + ); + } + + void showEKGFileDetails(ECGFileDetailModel ekgFileDetailResponseModel) async { + print("received file name: ${ekgFileDetailResponseModel.fileName}"); + Navigator.push(context, FadePage(page: EKGChartView(ekgFileDetailResponseModel: ekgFileDetailResponseModel))); + // await BleChannel.getEKGFileDetails(fileName); + } + + void parseEKGFileDetailObject(dynamic returnData) { + ekgFileDetailResponseModelList.clear(); + json.decode(returnData).forEach((v) { + ekgFileDetailResponseModelList.add(new ECGFileDetailModel.fromJson(v)); + }); + print(ekgFileDetailResponseModelList.length); + ekgValueNotifier.value = "Files Received: ${ekgFileDetailResponseModelList.length}"; + } + + String parseEKGRealTimeDataObject(dynamic returnData) { + ekgRealTimeDataResponseModel = ECGRtModel.fromJson(json.decode(returnData)); + return "HeartRate: ${ekgRealTimeDataResponseModel.param.hr} - Current State: ${ekgRealTimeDataResponseModel.param.curStatus}"; + } +} + +class _ChartData { + _ChartData(this.country, this.sales); + + final int country; + final num sales; +} diff --git a/lib/pages/medical/my_trackers/viatom_devices/oxymeter_ble.dart b/lib/pages/medical/my_trackers/viatom_devices/oxymeter_ble.dart new file mode 100644 index 00000000..4518d68b --- /dev/null +++ b/lib/pages/medical/my_trackers/viatom_devices/oxymeter_ble.dart @@ -0,0 +1,151 @@ +import 'dart:io'; + +import 'package:diplomaticquarterapp/pages/medical/my_trackers/ble_helpers/ble_connect_helper.dart'; +import 'package:diplomaticquarterapp/widgets/others/app_scaffold_widget.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:permission_handler/permission_handler.dart'; + +class Oxymeter_BLE extends StatefulWidget { + @override + State createState() => _Oxymeter_BLEState(); +} + +class _Oxymeter_BLEState extends State { + EventChannel eventChannel = EventChannel('BLE-Platform-Bridge-Event'); + String receivedData = ''; + + final oxymeterValueNotifier = ValueNotifier("start"); + + String deviceName = "CheckMeO2"; + // String deviceName = "POD"; + + @override + void dispose() { + oxymeterValueNotifier.dispose(); + super.dispose(); + BleChannel.disconnect(); + } + + @override + void initState() { + // TODO: implement initState + super.initState(); + } + + @override + Widget build(BuildContext context) { + return AppScaffold( + appBarTitle: "Oxymeter", + showNewAppBar: true, + isShowDecPage: false, + showNewAppBarTitle: true, + backgroundColor: Color(0xffF8F8F8), + body: SingleChildScrollView( + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Center( + child: GestureDetector( + onTap: () { + // await BleChannel.getScanningResult(); + checkBLEPermissions(); + }, + child: Container( + width: 50, + height: 50, + color: Colors.blueAccent, + ), + ), + ), + ValueListenableBuilder( + valueListenable: oxymeterValueNotifier, + builder: (context, value, _) { + return value != "start" + ? Platform.isAndroid + ? Column( + children: [ + Text( + getSPO2( + value.toString().replaceAll("RtParam", ""), + ), + ), + Text( + getPR( + value.toString().replaceAll("RtParam", ""), + ), + ), + // Text( + // getPI( + // value.toString().replaceAll("RtParam", ""), + // ), + // ), + ], + ) + : Column( + children: [ + Text(getSPO2iOS(value.toString())), + Text(getPRiOS(value.toString())), + ], + ) + : Text(value); + }, + ), + ], + ), + ), + ); + } + + void checkBLEPermissions() async { + [Permission.location, Permission.storage, Permission.bluetooth, Permission.bluetoothConnect, Permission.bluetoothScan].request().then((status) async { + // startBLEConnection(); + + eventChannel.receiveBroadcastStream().listen((event) { + print('Received event---: $event'); + oxymeterValueNotifier.value = event; + + // O2M response: RtParam{spo2=0, pr=0, steps=0, battery=72, batteryState=0, vector=0, pi=0.0, state=0, countDown=0, invalidIvState=0, spo2IvState=0, hrIvState=0, vectorIvState=0} + // POD-1W response: RtParam{spo2=98, pr=104, pi=5.8, isProbeOff=false, isPulseSearching=false} + }); + + await BleChannel.scanResultsNative(["oximeter", deviceName]); + }); + } + + String getSPO2(String value) { + return "SpO2: " + value.split(",")[0].replaceAll("{spo2=", ""); + } + + String getPR(String value) { + return "Pulse Rate: " + value.split(",")[1].replaceAll("pr=", ""); + } + + String getPI(String value) { + return "Perfusion Index: " + value.split(",")[2].replaceAll("pi=", "") + "%"; + } + + String getSPO2iOS(String value) { + return "SpO2: " + value.split(",")[0]; + } + + String getPRiOS(String value) { + return "Pulse Rate: " + value.split(",")[1]; + } + +// List setResult(String value) { +// List values = value.split(","); +// +// print(values[0].replaceAll("{spo2=", "")); +// print(values[1].replaceAll("pr=", "")); +// print(values[2].replaceAll("pi=", "")); +// +// values.clear(); +// values.add(values[0].replaceAll("{spo2=", "")); +// values.add(values[1].replaceAll("pr=", "")); +// values.add(values[2].replaceAll("pi=", "")); +// +// return values; +// } +} diff --git a/lib/pages/medical/my_trackers/viatom_devices/sp20_pulse_tracker.dart b/lib/pages/medical/my_trackers/viatom_devices/sp20_pulse_tracker.dart new file mode 100644 index 00000000..c94ff8c4 --- /dev/null +++ b/lib/pages/medical/my_trackers/viatom_devices/sp20_pulse_tracker.dart @@ -0,0 +1,200 @@ +import 'dart:convert'; +import 'package:diplomaticquarterapp/pages/medical/my_trackers/ble_models/viatom_devices/oxy_rt_model.dart'; +import 'package:diplomaticquarterapp/uitl/utils_new.dart'; +import 'package:diplomaticquarterapp/pages/medical/my_trackers/ble_helpers/ble_connect_helper.dart'; +import 'package:diplomaticquarterapp/widgets/buttons/defaultButton.dart'; +import 'package:diplomaticquarterapp/widgets/others/app_scaffold_widget.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:permission_handler/permission_handler.dart'; + +class SP20PulseTracker extends StatefulWidget { + @override + State createState() => _SP20PulseTrackerState(); +} + +class _SP20PulseTrackerState extends State { + EventChannel eventChannel = EventChannel('BLE-Platform-Bridge-Event'); + + // final bpDataNotifierMeasuring = ValueNotifier(BpRtDataMeasuringModel()); + final sp20RtParamNotifier = ValueNotifier(OxyRtModel()); + final sp20StatusNotifier = ValueNotifier(""); + final bpDeviceDataNotifier = ValueNotifier(""); + String deviceName = "SP20"; + + @override + void dispose() { + // bpDataNotifierMeasuring.dispose(); + sp20RtParamNotifier.dispose(); + sp20StatusNotifier.dispose(); + super.dispose(); + BleChannel.disconnect(); + } + + @override + void initState() { + // TODO: implement initState + super.initState(); + } + + @override + Widget build(BuildContext context) { + return AppScaffold( + appBarTitle: "Pulse Tracker", + showNewAppBar: true, + isShowDecPage: false, + showNewAppBarTitle: true, + backgroundColor: Color(0xffF8F8F8), + body: Padding( + padding: const EdgeInsets.all(24.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Expanded( + child: ListView( + children: [ + ValueListenableBuilder( + valueListenable: bpDeviceDataNotifier, + builder: (context, value, _) { + return Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + Text( + value == null || value.isEmpty ? "" : "Connected", + style: TextStyle(fontSize: 18), + ), + Text( + "$value", + style: TextStyle(fontSize: 18), + ), + ], + ); + }, + ), + ValueListenableBuilder( + valueListenable: sp20StatusNotifier, + builder: (context, value, _) { + if (value == "EventSp20RtParam") { + return ValueListenableBuilder( + valueListenable: sp20RtParamNotifier, + builder: (context, OxyRtModel sp20RtModel, _) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + mHeight(24.0), + Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + if (sp20RtModel.isProbeOff) ...[ + Text("Check Probe", style: TextStyle(fontSize: 50, fontWeight: FontWeight.bold)), + ] else ...[ + Column( + children: [ + Text("SPO₂", style: TextStyle(fontSize: 20)), + Text(sp20RtModel.spo2.toString(), style: TextStyle(fontSize: 50, fontWeight: FontWeight.bold)), + Text("%", style: TextStyle(fontSize: 10)), + ], + ), + Column( + children: [ + Text("Pulse", style: TextStyle(fontSize: 20)), + Text(sp20RtModel.pr.toString(), style: TextStyle(fontSize: 50, fontWeight: FontWeight.bold)), + Text("bpm", style: TextStyle(fontSize: 10)), + ], + ), + Column( + children: [ + Text("PI", style: TextStyle(fontSize: 20)), + Text(sp20RtModel.pi.toString(), style: TextStyle(fontSize: 50, fontWeight: FontWeight.bold)), + Text("%", style: TextStyle(fontSize: 10)), + ], + ), + ], + ], + ), + mHeight(24.0), + ], + ); + }, + ); + } + + return Padding( + padding: const EdgeInsets.all(24.0), + child: Center( + child: Text( + "Some animation with the instruction", + style: TextStyle(fontSize: 9.0), + ), + ), + ); + }, + ), + ], + ), + ), + ValueListenableBuilder( + valueListenable: sp20StatusNotifier, + builder: (context, value, _) { + if (value == null || value.isEmpty) { + return Row( + children: [ + Expanded( + child: DefaultButton( + "Start Scan", + () async { + checkBLEPermissions(); + }, + textColor: Colors.white, + color: Colors.green, + ), + ), + ], + ); + } + return Row( + children: [ + Expanded( + child: DefaultButton( + "Disconnect with $deviceName", + () async { + sp20RtParamNotifier.dispose(); + sp20StatusNotifier.dispose(); + + BleChannel.disconnect(); + }, + textColor: Colors.white, + ), + ), + ], + ); + }, + ), + ], + ), + ), + ); + } + + void checkBLEPermissions() async { + [Permission.location, Permission.storage, Permission.bluetooth, Permission.bluetoothConnect, Permission.bluetoothScan].request().then((status) async { + // startBLEConnection(); + + eventChannel.receiveBroadcastStream().listen((event) { + print('Received event---: $event'); + print(event['type']); + if (event['type'] == "infoData") { + sp20StatusNotifier.value = "infoData"; + bpDeviceDataNotifier.value = event['deviceName']; + } + + if (event['type'] == "EventSp20RtParam") { + sp20RtParamNotifier.value = OxyRtModel.fromJson(json.decode(event['data'])); + sp20StatusNotifier.value = "EventSp20RtParam"; + } + }); + await BleChannel.scanResultsNative(["Pulse Oxymeter", "SP-20"]); + }); + } +} diff --git a/lib/pages/medical/my_trackers/weight_scale_ble.dart b/lib/pages/medical/my_trackers/weight_scale_ble.dart new file mode 100644 index 00000000..3fb8594b --- /dev/null +++ b/lib/pages/medical/my_trackers/weight_scale_ble.dart @@ -0,0 +1,279 @@ +import 'dart:async'; +import 'package:diplomaticquarterapp/pages/medical/my_trackers/ble_models/andesfit_devices/weight_data_model.dart'; +import 'package:diplomaticquarterapp/theme/colors.dart'; +import 'package:diplomaticquarterapp/uitl/app_toast.dart'; +import 'package:diplomaticquarterapp/uitl/ble_utils.dart'; +import 'package:diplomaticquarterapp/uitl/bluetooth_off.dart'; +import 'package:diplomaticquarterapp/uitl/translations_delegate_base.dart'; +import 'package:diplomaticquarterapp/widgets/buttons/defaultButton.dart'; +import 'package:diplomaticquarterapp/widgets/others/app_scaffold_widget.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_blue_plus/flutter_blue_plus.dart'; +import 'package:permission_handler/permission_handler.dart'; + +class WeightScaleBLE extends StatefulWidget { + @override + State createState() => _WeightScaleBLEState(); +} + +class _WeightScaleBLEState extends State { + String connectionStatus = "disconnected"; + String currentWeight = "0.0"; + + BluetoothDevice currentConnectedDevice; + + StreamSubscription bleDevicesStream; + StreamSubscription weightValuesStream; + + WeightScaleData weightScaleData = WeightScaleData(); + + final bleConnectionStatus = ValueNotifier("Disconnected"); + + String resultSet1 = ""; + String resultSet2 = ""; + + @override + void dispose() { + super.dispose(); + bleConnectionStatus.dispose(); + bleDevicesStream.cancel(); + weightValuesStream.cancel(); + currentConnectedDevice.disconnect(); + } + + @override + Widget build(BuildContext context) { + return AppScaffold( + appBarTitle: TranslationBase.of(context).weight, + showNewAppBar: true, + isShowDecPage: false, + showNewAppBarTitle: true, + backgroundColor: Color(0xffF8F8F8), + body: SingleChildScrollView( + child: StreamBuilder( + stream: FlutterBluePlus.adapterState, + initialData: BluetoothAdapterState.unknown, + builder: (c, snapshot) { + final adapterState = snapshot.data; + if (adapterState == BluetoothAdapterState.on) { + return Container( + margin: EdgeInsets.only(top: 200.0, left: 50.0, right: 50.0), + child: Column( + children: [ + Center( + child: DefaultButton( + TranslationBase.of(context).start.toUpperCase(), + () { + checkBLEPermissions(); + // parseResultData(); + }, + color: CustomColors.green, + ), + ), + SizedBox( + height: 50.0, + ), + ValueListenableBuilder( + valueListenable: bleConnectionStatus, + builder: (context, value, _) { + return Text("Connection state: $value"); + }, + ), + SizedBox( + height: 50.0, + ), + Column( + children: [ + Text("Current Weight: ${weightScaleData.weight}" + " kg"), + Text("Current BMI: ${weightScaleData.bmi}"), + Text("Current Body Fat: ${weightScaleData.fat}" + "%"), + Text("Current Bone Weight: ${weightScaleData.bone}" + " kg"), + Text("Current Water: ${weightScaleData.water}" + "%"), + Text("Current Muscle: ${weightScaleData.muscle}" + "%"), + Text("Current BMR: ${weightScaleData.bmr}" + " calories/day"), + ], + ), + // }) + ], + ), + ); + } else { + FlutterBluePlus.stopScan(); + return SizedBox(height: 300.0, child: BluetoothOffScreen(adapterState: adapterState)); + } + }), + ), + ); + } + + void checkBLEPermissions() { + [Permission.location, Permission.storage, Permission.bluetooth, Permission.bluetoothConnect, Permission.bluetoothScan].request().then((status) { + startBLEConnection(); + }); + } + + void startBLEConnection() { + if (FlutterBluePlus.isScanningNow == false) { + bleConnectionStatus.value = "Connecting..."; + + bleDevicesStream = FlutterBluePlus.scanResults.listen( + (results) { + List blueToothDevices = results; + blueToothDevices.forEach((element) async { + if (element.device.localName.isNotEmpty) { + if (element.device.localName.toLowerCase() == "sdic") { + bleDevicesStream.cancel(); + element.device.connect(timeout: Duration(seconds: 5), autoConnect: false).then((value) async { + bleConnectionStatus.value = "Connected..."; + print("Device Connected-------"); + currentConnectedDevice = element.device; + FlutterBluePlus.stopScan(); + // await currentConnectedDevice.clearGattCache(); + List services = await element.device.discoverServices(timeout: 5); + services.forEach((service) { + if (service.serviceUuid.toString().toLowerCase() == BLEUtils.WEIGHT_MEASUREMENT_SERVICE) { + print(service.serviceUuid); + service.characteristics.forEach((characteristic) async { + if (characteristic.characteristicUuid.toString().toLowerCase() == BLEUtils.WEIGHT_MEASUREMENT_CHARACTERISTIC_FFF1) { + print(characteristic.characteristicUuid); + print(characteristic.properties.toString()); + characteristic.onValueReceived.listen((event) { + print("onValueReceived fff1 Stream"); + print(event); + if (event[0] == 129) { + // Issue result ack command + Future.delayed(Duration(milliseconds: 1000)).then((value) async { + characteristic.write([0x01]).then((value) { + print("----fff1 result ack data written----"); + }); + }); + } + // Error + else if (event[0] == 65) { + AppToast.showErrorToast(message: "Measurement Error Occurred!"); + } else { + //Sample Results + // ----------Result Set 1----------: 42;0;1;0;0;0;0;65;9;b0;d2;0;7f;3;44;0;ac;1;cc;0 + // ----------Result Set 2----------: 42;1;32;0;2c;0;1;3;84;9;1b;0;29;2;9c;0;10;0;e9 + + if (event[1] == 0) { + resultSet1 = convertResultSetToString(event); + print("----------Result Set 1----------: $resultSet1"); + print("----------Result Set 2----------: $resultSet2"); + } else { + resultSet2 = convertResultSetToString(event); + print("----------Result Set 1----------: $resultSet1"); + print("----------Result Set 2----------: $resultSet2"); + parseResultData(); + } + } + }); + await Future.delayed(Duration(milliseconds: 500)).then((value) async { + print("Delayed fff1 done"); + if (!characteristic.isNotifying) await characteristic.setNotifyValue(true).catchError((err) {}); + }); + } + + if (characteristic.characteristicUuid.toString().toLowerCase() == BLEUtils.WEIGHT_MEASUREMENT_CHARACTERISTIC_FFF2) { + print(characteristic.characteristicUuid); + print(characteristic.properties.toString()); + characteristic.onValueReceived.listen((event) { + print("onValueReceived fff2 Stream"); + print(event); + }); + + await Future.delayed(Duration(milliseconds: 1000)).then((value) async { + print("Delayed fff2 done"); + if (!characteristic.isNotifying) await characteristic.setNotifyValue(true).catchError((err) {}); + }); + + // Write user data to fff2 + await Future.delayed(Duration(milliseconds: 1000)).then((value) async { + characteristic.write([0x81, 0x00, 0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0xB2, 0x19, 0x01, 0x00, 0x00], withoutResponse: true).then((value) { + print("----fff2 user data written----"); + }); + + // Issue get result command + await Future.delayed(Duration(milliseconds: 1000)).then((value) async { + characteristic.write([0x41, 0x00, 0x82, 0x00, 0x00, 0x00, 0x00], withoutResponse: true).then((value) { + print("----fff2 get result data written----"); + }); + }); + }); + } + }); + return true; + } + }); + }); + return true; + } + } + }); + }, + // onError(e) => print(e); + ); + + FlutterBluePlus.startScan(timeout: const Duration(seconds: 5), androidUsesFineLocation: false); + } + } + + String adjustHexString(String value) { + return value.length == 1 ? "0" + value : value; + } + + void parseResultData() { + List hexStringResSet1 = resultSet1.split(";"); + List hexStringResSet2 = resultSet2.split(";"); + + String hexStringWeight = adjustHexString(hexStringResSet1[13]) + adjustHexString(hexStringResSet1[14]); + String hexStringBMI = adjustHexString(hexStringResSet1[15]) + adjustHexString(hexStringResSet1[16]); + String hexStringFat = adjustHexString(hexStringResSet2[3]) + adjustHexString(hexStringResSet2[4]); + String hexStringBone = adjustHexString(hexStringResSet2[11]) + adjustHexString(hexStringResSet2[12]); + String hexStringWater = adjustHexString(hexStringResSet2[13]) + adjustHexString(hexStringResSet2[14]); + String hexStringMuscle = adjustHexString(hexStringResSet2[7]) + adjustHexString(hexStringResSet2[8]); + String hexStringBMR = adjustHexString(hexStringResSet2[9]) + adjustHexString(hexStringResSet2[10]); + + print((int.parse(hexStringWeight, radix: 16) * 0.1).toStringAsFixed(1)); + print((int.parse(hexStringBMI, radix: 16) * 0.1).toStringAsFixed(1)); + print((int.parse(hexStringFat, radix: 16) * 0.1).toStringAsFixed(1)); + print((int.parse(hexStringMuscle, radix: 16) * 0.1).toStringAsFixed(1)); + print((int.parse(hexStringBMR, radix: 16))); + print((int.parse(hexStringBone, radix: 16) * 0.1).toStringAsFixed(1)); + print((int.parse(hexStringWater, radix: 16) * 0.1).toStringAsFixed(1)); + + setState(() { + weightScaleData.weight = (int.parse(hexStringWeight, radix: 16) * 0.1).toStringAsFixed(1); + weightScaleData.bmi = (int.parse(hexStringBMI, radix: 16) * 0.1).toStringAsFixed(1); + weightScaleData.fat = (int.parse(hexStringFat, radix: 16) * 0.1).toStringAsFixed(1); + weightScaleData.muscle = (int.parse(hexStringMuscle, radix: 16) * 0.1).toStringAsFixed(1); + weightScaleData.bmr = (int.parse(hexStringBMR, radix: 16)).toString(); + weightScaleData.bone = (int.parse(hexStringBone, radix: 16) * 0.1).toStringAsFixed(1); + weightScaleData.water = (int.parse(hexStringWater, radix: 16) * 0.1).toStringAsFixed(1); + }); + } + + String convertResultSetToString(List byteArray) { + String resSet; + resSet = (byteArray.map( + (x) { + return x.toRadixString(16); + }, + )).join(";"); + return resSet; + } + + String convertIntListToHex(List byteArray) { + String b = (byteArray.map((x) { + return x.toRadixString(16); + })).join(";"); + List hexString = b.split(";"); + print("HexString: $hexString"); + String returnString = hexString[2] + hexString[3]; + print("Temp Hex String: $returnString"); + final number = int.parse(returnString, radix: 16); + print("Temp Number: ${number * 0.1}"); + + return (number * 0.1).toStringAsFixed(1); + } +} diff --git a/lib/routes.dart b/lib/routes.dart index 6e8aca9e..116313da 100644 --- a/lib/routes.dart +++ b/lib/routes.dart @@ -6,6 +6,7 @@ import 'package:diplomaticquarterapp/pages/DrawerPages/family/my-family.dart'; import 'package:diplomaticquarterapp/pages/TestPage.dart'; import 'package:diplomaticquarterapp/pages/appUpdatePage/app_update_page.dart'; import 'package:diplomaticquarterapp/pages/conference/zoom/call_screen.dart'; +import 'package:diplomaticquarterapp/pages/ble_screen.dart'; import 'package:diplomaticquarterapp/pages/landing/landing_page.dart'; import 'package:diplomaticquarterapp/pages/livecare/incoming_call.dart'; import 'package:diplomaticquarterapp/pages/livecare/livecare_home.dart'; @@ -55,6 +56,7 @@ const String OPENTOK_CALL_PAGE = 'OPENTOK_CALL_PAGE'; const String CART_ORDER_PAGE = 'cart-order-page'; const String CALL_PAGE = 'CALL_PAGE'; const String INCOMING_CALL_PAGE = 'INCOMING_CALL_PAGE'; +const String BLE_Page = 'BLE_PAGE'; const String HEALTH_WEATHER = 'health-weather'; const APP_UPDATE = 'app-update'; @@ -91,6 +93,7 @@ var routes = { ZOOM_CALL_PAGE: (_) => CallScreen(), + BLE_Page: (_) => BLEScreen(), OPENTOK_CALL_PAGE: (_) => OpenTokConnectCallPage( apiKey: OPENTOK_API_KEY, sessionId: '1_MX40NjIwOTk2Mn5-MTY0NzE3MzcwNjA0MH55RUJoZnd0ZGh2U3BPc01ENVZBelQvT1Z-fg', diff --git a/lib/uitl/ble_utils.dart b/lib/uitl/ble_utils.dart new file mode 100644 index 00000000..8e824199 --- /dev/null +++ b/lib/uitl/ble_utils.dart @@ -0,0 +1,41 @@ +class BLEUtils { + //Blood Pressure + static const String BLOOD_PRESSURE_SERVICE = "0000fff0-0000-1000-8000-00805f9b34fb"; + static const String BLOOD_PRESSURE_CHARACTERISTIC = "0000fff4-0000-1000-8000-00805f9b34fb"; + + //Temperature + static const String TEMPERATURE_SERVICE = "00001809-0000-1000-8000-00805f9b34fb"; + static const String TEMPERATURE_CHARACTERISTIC = "00002a1c-0000-1000-8000-00805f9b34fb"; + + //Spirometer + static const String BLE_TO_MSA100_SERVICE = "0000fff0-0000-1000-8000-00805f9b34fb"; + static const String BLE_TO_MSA100_WRITE_CHARACTERISTIC = "0000ff0b-0000-1000-8000-00805f9b34fb"; + static const String BLE_TO_MSA100_READ_CHARACTERISTIC = "0000ff0a-0000-1000-8000-00805f9b34fb"; + + static const String MSA100_TO_BLE_SERVICE = "0000fff0-0000-1000-8000-00805f9b34fb"; + static const String MSA100_TO_BLE_CHARACTERISTIC = "0000ff0a-0000-1000-8000-00805f9b34fb"; + + //ECG + static const String ECG_SERVICE = "49535343-fe7d-4ae5-8fa9-9fafd205e455"; // + static const String ECG_READ_CHARACTERISTIC = "49535343-1e4d-4bd9-ba61-23c647249616"; //49535343-1e4d-4bd9-ba61-23c647249616 + static const String ECG_WRITE_CHARACTERISTIC = "49535343-8841-43f4-a8d4-ecbe34729bb3"; //49535343-8841-43f4-a8d4-ecbe34729bb3 + + //Blood Glucose + static const String BLOOD_GLUCOSE_SERVICE = "0000fff0-0000-1000-8000-00805f9b34fb"; + static const String BLOOD_GLUCOSE_CHARACTERISTIC = "0000fff4-0000-1000-8000-00805f9b34fb"; + + //Weight Scale + static const String WEIGHT_MEASUREMENT_CHARACTERISTIC_FFF1 = "0000fff1-0000-1000-8000-00805f9b34fb"; + static const String WEIGHT_MEASUREMENT_CHARACTERISTIC_FFF2 = "0000fff2-0000-1000-8000-00805f9b34fb"; + static const String WEIGHT_MEASUREMENT_CHARACTERISTIC_FFF3 = "0000fff3-0000-1000-8000-00805f9b34fb"; + static const String WEIGHT_MEASUREMENT_SERVICE = "0000fff0-0000-1000-8000-00805f9b34fb"; + + //CheckMe Pro AllInOne + static const String ALL_IN_ONE_SERVICE = "14839ac4-7d7e-415c-9a42-167340cf2339"; // + static const String ALL_IN_ONE_READ_CHARACTERISTIC = "0734594A-A8E7-4B1A-A6B1-CD5243059A57"; //49535343-1e4d-4bd9-ba61-23c647249616 + static const String ALL_IN_ONE_WRITE_CHARACTERISTIC = "8B00ACE7-EB0B-49B0-BBE9-9AEE0A26E1A3"; //49535343-8841-43f4-a8d4-ecbe34729bb3 + +// 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"); +} diff --git a/lib/uitl/bluetooth_off.dart b/lib/uitl/bluetooth_off.dart new file mode 100644 index 00000000..f0a7f6b7 --- /dev/null +++ b/lib/uitl/bluetooth_off.dart @@ -0,0 +1,47 @@ +import 'dart:io'; + +import 'package:flutter/material.dart'; +import 'package:flutter_blue_plus/flutter_blue_plus.dart'; + +class BluetoothOffScreen extends StatelessWidget { + const BluetoothOffScreen({Key key, this.adapterState}) : super(key: key); + + final BluetoothAdapterState adapterState; + + @override + Widget build(BuildContext context) { + return ScaffoldMessenger( + child: Scaffold( + backgroundColor: Colors.lightBlue, + body: Center( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + const Icon( + Icons.bluetooth_disabled, + size: 200.0, + color: Colors.white54, + ), + Text( + 'Bluetooth Adapter is ${adapterState != null ? adapterState.toString().split(".").last + ", Please turn on your bluetooth to continue." : 'not available'}.', + textAlign: TextAlign.center, + style: Theme.of(context).primaryTextTheme.titleSmall?.copyWith(color: Colors.white), + ), + if (Platform.isAndroid) + ElevatedButton( + child: const Text('TURN ON'), + onPressed: () async { + try { + if (Platform.isAndroid) { + await FlutterBluePlus.turnOn(); + } + } catch (e) {} + }, + ), + ], + ), + ), + ), + ); + } +} \ No newline at end of file diff --git a/lib/uitl/translations_delegate_base.dart b/lib/uitl/translations_delegate_base.dart index 46f4807c..340d84c2 100644 --- a/lib/uitl/translations_delegate_base.dart +++ b/lib/uitl/translations_delegate_base.dart @@ -2945,6 +2945,7 @@ class TranslationBase { String get receive => localizedValues["receive"][locale.languageCode]; String get PRO => localizedValues["PRO"][locale.languageCode]; String get patientRelationOffice => localizedValues["patientRelationOffice"][locale.languageCode]; + String get spirometer => localizedValues["spirometer"][locale.languageCode]; String get invalidEligibility => localizedValues["invalidEligibility"][locale.languageCode]; String get invalidInsurance => localizedValues["invalidInsurance"][locale.languageCode]; String get continueCash => localizedValues["continueCash"][locale.languageCode]; diff --git a/lib/uitl/utils.dart b/lib/uitl/utils.dart index 9b59e200..06ba2c2b 100644 --- a/lib/uitl/utils.dart +++ b/lib/uitl/utils.dart @@ -30,6 +30,7 @@ import 'package:diplomaticquarterapp/pages/medical/doctor/doctor_home_page.dart' import 'package:diplomaticquarterapp/pages/medical/eye/EyeMeasurementsPage.dart'; import 'package:diplomaticquarterapp/pages/medical/labs/labs_home_page.dart'; import 'package:diplomaticquarterapp/pages/medical/my_invoices/my_invoice_page.dart'; +import 'package:diplomaticquarterapp/pages/medical/my_trackers/ble_device_type_screens/select_tracker_type.dart'; import 'package:diplomaticquarterapp/pages/medical/my_trackers/my_trackers.dart'; import 'package:diplomaticquarterapp/pages/medical/patient_sick_leave_page.dart'; import 'package:diplomaticquarterapp/pages/medical/prescriptions/prescriptions_home_page.dart'; @@ -539,6 +540,27 @@ class Utils { ), )); + // Oxymeter + // BloodPressure + // Blood Sugar + // ECG + // Holter + // All in one Monitor + + medical.add(InkWell( + onTap: () { + if (true) { + Navigator.push(context, FadePage(page: SelectTrackerType())); + } + }, + child: MedicalProfileItem( + title: "Connect", + imagePath: 'tracker.svg', + subTitle: "BLE", + isEnable: true, + ), + )); + medical.add(InkWell( onTap: () => projectViewModel.havePrivilege(30) ? Navigator.push(context, FadePage(page: SmartWatchInstructions())) : null, child: MedicalProfileItem( @@ -909,6 +931,7 @@ class Utils { } //static String generateSignature() {} + static String generateSignature() {} } Widget applyShadow({Color color = Colors.grey, double shadowOpacity = 0.5, double spreadRadius = 2, double blurRadius = 7, Offset offset = const Offset(2, 2), required Widget child}) { diff --git a/pubspec.yaml b/pubspec.yaml index 9b6839cb..2713357b 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -99,6 +99,9 @@ dependencies: # Calendar syncfusion_flutter_calendar: ^23.1.42 + #Charts for Live ECG View + syncfusion_flutter_charts: ^19.3.55 + # SVG Images flutter_svg: ^2.0.8 @@ -170,10 +173,18 @@ dependencies: amazon_payfort: ^1.1.0 logger: ^2.0.2+1 network_info_plus: any + flutter_blue_plus: 1.15.7 + flutter_toggle_tab: + # flutter_callkit_incoming: ^1.0.3+3 + # firebase_core: 1.12.0 flutter_zoom_videosdk: ^1.10.11 dart_jsonwebtoken: ^2.14.0 + +# flutter_amazonpaymentservices: 0.0.6 +# crypto: + dependency_overrides: dev_dependencies: