diff --git a/app/build.gradle b/app/build.gradle
index 06f039e..419fccc 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -16,15 +16,16 @@ android {
// prod - production, preprod - preproduction, quality - qc, dev - development
defaultConfig {
- applicationId "in.sminnovations.hpostesting.quality"
+ applicationId "in.sminnovations.hpostesting.dev"
minSdk 21
targetSdk 34
- versionCode 97
- versionName "2.1.97"
+ versionCode 98
+ versionName "2.1.98"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
+
buildTypes {
release {
minifyEnabled false
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index a91079a..a5b66b6 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -69,6 +69,15 @@
android:parentActivityName="com.example.hpostesting.presentation.MainActivity"
android:theme="@style/Theme.HPOS.NoActionBar"
android:windowSoftInputMode="adjustPan" />
+
+
+
when (deviceType) {
- Constants.DEVICE_TYPE_HOMOCUBE -> {
+ Constants.DEVICE_TYPE_HEMOCUBE -> {
val i = Intent(applicationContext, HemocubeActivity::class.java)
startActivity(i)
}
@@ -194,6 +194,11 @@ class KitScanActivity : AppCompatActivity() {
val i = Intent(applicationContext, TestRightActivity::class.java)
startActivity(i)
}
+
+ Constants.DEVICE_TYPE_TRUEHEME -> {
+ val i = Intent(applicationContext, TrueHemeActivity::class.java)
+ startActivity(i)
+ }
}
}
}
diff --git a/app/src/main/java/com/example/hpostesting/presentation/MainActivity.kt b/app/src/main/java/com/example/hpostesting/presentation/MainActivity.kt
index 5afbabc..dfd8997 100644
--- a/app/src/main/java/com/example/hpostesting/presentation/MainActivity.kt
+++ b/app/src/main/java/com/example/hpostesting/presentation/MainActivity.kt
@@ -1,8 +1,10 @@
package com.example.hpostesting.presentation
-import com.example.hpostesting.presentation.dashboard.DashboardActivity
import android.Manifest
-import android.content.*
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.Intent
+import android.content.IntentFilter
import android.content.pm.PackageManager
import android.hardware.usb.UsbManager
import android.location.Location
@@ -20,7 +22,12 @@ import com.example.hpostesting.data.constant.Constants
import com.example.hpostesting.data.constant.LanguageManager
import com.example.hpostesting.data.model.patient.UserData
import com.example.hpostesting.data.model.test.TestType
-import com.google.android.gms.location.*
+import com.example.hpostesting.presentation.dashboard.DashboardActivity
+import com.google.android.gms.location.FusedLocationProviderClient
+import com.google.android.gms.location.LocationCallback
+import com.google.android.gms.location.LocationRequest
+import com.google.android.gms.location.LocationResult
+import com.google.android.gms.location.LocationServices
import com.google.android.material.snackbar.Snackbar
import com.google.firebase.crashlytics.FirebaseCrashlytics
import com.hoho.android.usbserial.driver.UsbSerialProber
@@ -81,7 +88,7 @@ class MainActivity : AppCompatActivity() {
DataHolder.deviceType.observe(this) { deviceType ->
with(binding) {
- if (deviceType == Constants.DEVICE_TYPE_HOMOCUBE) {
+ if (deviceType == Constants.DEVICE_TYPE_HEMOCUBE) {
cvItem1.visibility = View.VISIBLE
cvItem3.visibility = View.VISIBLE
cvItem2.visibility = View.GONE
@@ -111,8 +118,8 @@ class MainActivity : AppCompatActivity() {
binding.cvItem3.visibility = View.VISIBLE
binding.cvItem2.visibility = View.GONE
binding.cvItem4.visibility = View.GONE
- DataHolder.deviceType.postValue(Constants.DEVICE_TYPE_HOMOCUBE)
- Constants.DEVICE_TYPE_HOMOCUBE
+ DataHolder.deviceType.postValue(Constants.DEVICE_TYPE_HEMOCUBE)
+ Constants.DEVICE_TYPE_HEMOCUBE
}
device.productId == Constants.HEMO_CUBE_V2_PRODUCT_ID && device.vendorId == Constants.HEMO_CUBE_V2_VENDOR_ID -> {
@@ -120,8 +127,8 @@ class MainActivity : AppCompatActivity() {
binding.cvItem3.visibility = View.VISIBLE
binding.cvItem2.visibility = View.GONE
binding.cvItem4.visibility = View.GONE
- DataHolder.deviceType.postValue(Constants.DEVICE_TYPE_HOMOCUBE)
- Constants.DEVICE_TYPE_HOMOCUBE
+ DataHolder.deviceType.postValue(Constants.DEVICE_TYPE_HEMOCUBE)
+ Constants.DEVICE_TYPE_HEMOCUBE
}
device.productId == 24577 && device.vendorId == 1027 -> {
@@ -129,8 +136,8 @@ class MainActivity : AppCompatActivity() {
binding.cvItem3.visibility = View.VISIBLE
binding.cvItem2.visibility = View.GONE
binding.cvItem4.visibility = View.GONE
- DataHolder.deviceType.postValue(Constants.DEVICE_TYPE_HOMOCUBE)
- Constants.DEVICE_TYPE_HOMOCUBE
+ DataHolder.deviceType.postValue(Constants.DEVICE_TYPE_TRUEHEME)
+ Constants.DEVICE_TYPE_TRUEHEME
}
device.productId == 8963 && device.vendorId == 1659 -> {
@@ -138,8 +145,8 @@ class MainActivity : AppCompatActivity() {
binding.cvItem3.visibility = View.VISIBLE
binding.cvItem2.visibility = View.GONE
binding.cvItem4.visibility = View.GONE
- DataHolder.deviceType.postValue(Constants.DEVICE_TYPE_HOMOCUBE)
- Constants.DEVICE_TYPE_HOMOCUBE
+ DataHolder.deviceType.postValue(Constants.DEVICE_TYPE_HEMOCUBE)
+ Constants.DEVICE_TYPE_HEMOCUBE
}
device.productId == Constants.DEVICE_PRODUCT_ID && device.vendorId == Constants.DEVICE_VENDOR_ID -> {
@@ -278,7 +285,6 @@ class MainActivity : AppCompatActivity() {
// exception.printStackTrace()
// }
-
fusedLocationClient.lastLocation.addOnSuccessListener { location: Location? ->
location?.let {
if (DataHolder.selectedTest != null) {
@@ -302,9 +308,6 @@ class MainActivity : AppCompatActivity() {
val rootView = findViewById(android.R.id.content)
Snackbar.make(rootView, errorMessage, Snackbar.LENGTH_LONG).show()
}
-
-
-
}
private fun requestLocationUpdates() {
diff --git a/app/src/main/java/com/example/hpostesting/presentation/hemocube/HemoCubeFragment.kt b/app/src/main/java/com/example/hpostesting/presentation/hemocube/HemoCubeFragment.kt
index 1868e98..f1c20b0 100644
--- a/app/src/main/java/com/example/hpostesting/presentation/hemocube/HemoCubeFragment.kt
+++ b/app/src/main/java/com/example/hpostesting/presentation/hemocube/HemoCubeFragment.kt
@@ -123,7 +123,7 @@ class HemoCubeFragment : Fragment() {
}
binding.btnPlacebuffer.setOnClickListener {
- if (isBufferValueAvailable()) {
+ if (isBufferValueAvailable() || !Constants.BLANK_EVERY_TEST) {
showBufferAlertDialog()
} else {
activity?.runOnUiThread {
@@ -188,23 +188,6 @@ class HemoCubeFragment : Fragment() {
binding.progressBar.visibility = View.GONE
}
-// hemoCubeViewModel.fireBaseUpload.observe(viewLifecycleOwner) { result ->
-// if (result == "Success") {
-// showToast(R.string.test_upload)
-// handleReadingFinish()
-// }
-// if (result == "Local") {
-// showToast(R.string.internt_not_local)
-// handleReadingFinish()
-// }
-// if (result == "Error") {
-// showToast(R.string.error_local)
-// startActivity(Intent(requireActivity(), DashboardActivity::class.java))
-// }
-//
-// binding.progressBar.visibility = View.GONE
-// }
-
hemoCubeViewModel.getDeviceData(sharedPreferences.getString(Constants.USER_ID, ""))
hemoCubeViewModel.deviceData.observe(viewLifecycleOwner) {
@@ -387,6 +370,19 @@ class HemoCubeFragment : Fragment() {
processV1HardwareId(resultData)
}
+ (resultData.contains("#LS") && this.testStatusCode < TestStatus.FIRST_EMPTY_AIR_READING_STARTED.code) -> {
+ // air reading 1
+ this.testStatusCode = TestStatus.FIRST_EMPTY_AIR_READING_STARTED.code
+ hemoCubeViewModel.messages.postValue("Air reading started")
+ }
+
+ (resultData.contains("#LC") && this.testStatusCode < TestStatus.FIRST_EMPTY_AIR_READING_COMPLETED.code) -> {
+ // air reading 1, send command to print
+ this.testStatusCode = TestStatus.FIRST_EMPTY_AIR_READING_COMPLETED.code
+ hemoCubeViewModel.messages.postValue("Air reading completed")
+ fetchResult()
+ }
+
resultData.contains("#BS") && this.testStatusCode < TestStatus.BUFFER_STARTED.code -> {
hemoCubeViewModel.messages.postValue(getString(R.string.buffer_started))
this.testStatusCode = TestStatus.BUFFER_STARTED.code
@@ -455,10 +451,59 @@ class HemoCubeFragment : Fragment() {
fetchResult()
}
+ (resultData.contains("#LS") && this.testStatusCode < TestStatus.SECOND_EMPTY_AIR_READING_STARTED.code) -> {
+ // air reading 2
+ this.testStatusCode = TestStatus.SECOND_EMPTY_AIR_READING_STARTED.code
+ hemoCubeViewModel.messages.postValue("Air reading started")
+ }
+
+ (resultData.contains("#LC") && this.testStatusCode < TestStatus.SECOND_EMPTY_AIR_READING_COMPLETED.code) -> {
+ // air reading 2, print values
+ this.testStatusCode = TestStatus.SECOND_EMPTY_AIR_READING_COMPLETED.code
+ hemoCubeViewModel.messages.postValue("Air reading completed")
+ fetchResult()
+ }
+
resultData.contains("REND") && this.testStatusCode < TestStatus.SAMPLE_PRINT_COMPLETED.code -> {
handleSampleCompleted()
}
+ currentResultData.contains("REND")
+ && this.testStatusCode >= TestStatus.FIRST_EMPTY_AIR_READING_COMPLETED.code
+ && this.testStatusCode < TestStatus.FIRST_EMPTY_AIR_READING_PRINT_COMPLETED.code -> {
+ this.testStatusCode = TestStatus.FIRST_GAIN_PRINT_COMPLETED.code
+ hemoCubeViewModel.messages.postValue("First air reading completed")
+
+ val resultLines = currentResultData.split("\\s+(?=LB|LS)".toRegex())
+ DataHolder.hemoCubeTestData?.apply {
+ led1Air1 = resultLines[5].split(' ')[1].trim().toDoubleOrNull()!!
+ led2Air1 = resultLines[6].split(' ')[1].trim().toDoubleOrNull()!!
+ led3Air1 = resultLines[7].split(' ')[1].trim().toDoubleOrNull()!!
+ led4Air1 = resultLines[8].split(' ')[1].split('\r')[0].trim().toDoubleOrNull()!!
+ }
+
+ startBufferProcess()
+ currentResultData = ""
+ }
+
+ currentResultData.contains("REND")
+ && this.testStatusCode >= TestStatus.SECOND_EMPTY_AIR_READING_COMPLETED.code
+ && this.testStatusCode < TestStatus.SECOND_EMPTY_AIR_PRINT_COMPLETED.code -> {
+ this.testStatusCode = TestStatus.SECOND_EMPTY_AIR_PRINT_COMPLETED.code
+ hemoCubeViewModel.messages.postValue("Second air reading completed")
+
+ val resultLines = currentResultData.split("\\s+(?=LB|LS)".toRegex())
+ DataHolder.hemoCubeTestData?.apply {
+ led1Air2 = resultLines[5].split(' ')[1].trim().toDoubleOrNull()!!
+ led2Air2 = resultLines[6].split(' ')[1].trim().toDoubleOrNull()!!
+ led3Air2 = resultLines[7].split(' ')[1].trim().toDoubleOrNull()!!
+ led4Air2 = resultLines[8].split(' ')[1].split('\r')[0].trim().toDoubleOrNull()!!
+ }
+
+ sendSecondGainCommand()
+ currentResultData = ""
+ }
+
currentResultData.contains("REND")
&& this.testStatusCode >= TestStatus.FIRST_GAIN_COMPLETED.code
&& this.testStatusCode < TestStatus.FIRST_GAIN_PRINT_COMPLETED.code -> {
diff --git a/app/src/main/java/com/example/hpostesting/presentation/trueheme/TrueHemeActivity.kt b/app/src/main/java/com/example/hpostesting/presentation/trueheme/TrueHemeActivity.kt
new file mode 100644
index 0000000..465abb7
--- /dev/null
+++ b/app/src/main/java/com/example/hpostesting/presentation/trueheme/TrueHemeActivity.kt
@@ -0,0 +1,199 @@
+package com.example.hpostesting.presentation.trueheme
+
+import android.annotation.SuppressLint
+import android.app.PendingIntent
+import android.content.BroadcastReceiver
+import android.content.ComponentName
+import android.content.Context
+import android.content.Intent
+import android.content.IntentFilter
+import android.content.ServiceConnection
+import android.hardware.usb.UsbDevice
+import android.hardware.usb.UsbDeviceConnection
+import android.hardware.usb.UsbManager
+import android.os.Bundle
+import android.os.IBinder
+import android.util.Log
+import android.view.Menu
+import android.widget.Toast
+import androidx.activity.viewModels
+import androidx.appcompat.app.AppCompatActivity
+import androidx.core.content.ContextCompat
+import androidx.core.view.get
+import com.example.hpostesting.data.DataHolder
+import com.example.hpostesting.data.constant.Constants
+import com.example.hpostesting.data.constant.LanguageManager
+import com.example.hpostesting.presentation.hemocube.HemoCubeFragment
+import com.example.hpostesting.presentation.hemocube.HemoCubeViewModel
+import com.example.hpostesting.presentation.testRight.UsbService
+import com.hoho.android.usbserial.driver.UsbSerialDriver
+import com.hoho.android.usbserial.driver.UsbSerialProber
+import dagger.hilt.android.AndroidEntryPoint
+import `in`.sminnovations.hpostesting.R
+import `in`.sminnovations.hpostesting.databinding.ActivityHemocubeBinding
+
+@AndroidEntryPoint
+class TrueHemeActivity : AppCompatActivity() {
+ private lateinit var binding: ActivityHemocubeBinding
+ private val viewModel by viewModels()
+ private var myMenu: Menu? = null
+
+ private lateinit var mDriver: UsbSerialDriver
+ private var mConnection: UsbDeviceConnection? = null
+ lateinit var mService: UsbService
+
+ private val TAG = "HemoCube"
+
+ private val broadcastReceiver = object : BroadcastReceiver() {
+ override fun onReceive(context: Context, intent: Intent) {
+
+ synchronized(this) {
+ val device: UsbDevice? = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE)
+
+ if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) {
+ device?.apply {
+ connectUsb(true)
+ DataHolder.usbConnected.postValue(true)
+ }
+ } else {
+ onErrorReported("permission denied for device")
+ DataHolder.usbConnected.postValue(true)
+ }
+ }
+
+ }
+ }
+ private val connection = object : ServiceConnection {
+ override fun onServiceConnected(className: ComponentName, service: IBinder) {
+ val binder = service as UsbService.UsbServiceBinder
+ mService = binder.getService()
+ viewModel.isServiceConnected = true
+ mConnection.let { mService.connect(mDriver, mConnection!!) }
+ moveToNext()
+ }
+
+ override fun onServiceDisconnected(arg0: ComponentName) {
+ viewModel.isServiceConnected = false
+ }
+ }
+ override fun attachBaseContext(newBase: Context?) {
+ val languageCode = LanguageManager.getSavedLanguage(newBase!!)
+ LanguageManager.setLocale(newBase, languageCode)
+ super.attachBaseContext(newBase)
+ }
+
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ binding = ActivityHemocubeBinding.inflate(layoutInflater)
+ setContentView(binding.root)
+ setSupportActionBar(binding.myToolbar)
+ supportActionBar?.setDisplayHomeAsUpEnabled(true)
+ setupListener()
+ connectUsb(false)
+ }
+
+ private fun setupListener() {
+ DataHolder.usbConnected.observe(this) {
+ Log.d("USB OBSERVE", "HemoCube called -> $it")
+ if (it) {
+ myMenu?.get(0)?.icon =
+ ContextCompat.getDrawable(this, R.drawable.ic_baseline_usb_24)
+ } else {
+ myMenu?.get(0)?.icon =
+ ContextCompat.getDrawable(this, R.drawable.ic_baseline_usb_off_24)
+ Toast.makeText(this, "Device is Disconnected", Toast.LENGTH_SHORT).show()
+ }
+ }
+ }
+
+ open fun connectUsb(permissionGranted: Boolean) {
+ Log.d(TAG, "connectUsb() called, permission variable = $permissionGranted")
+ val manager = getSystemService(Context.USB_SERVICE) as UsbManager
+ val availableDrivers = UsbSerialProber.getDefaultProber().findAllDrivers(manager)
+
+ if (availableDrivers.isEmpty()) {
+ onErrorReported("No Device is Connected")
+ } else {
+ mDriver = availableDrivers[0]
+ mConnection = manager.openDevice(mDriver.device)
+
+ if (mConnection == null) {
+ requestUserPermission(manager, mDriver.device)
+ } else {
+ setupService()
+ }
+ }
+ }
+
+
+ @SuppressLint("MutableImplicitPendingIntent")
+ private fun requestUserPermission(manager: UsbManager, device: UsbDevice) {
+ val mPendingIntent: PendingIntent
+ if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.S) {
+ mPendingIntent = PendingIntent.getBroadcast(
+ this, 0, Intent(Constants.HOMOCUBE_USB_PERMISSION), PendingIntent.FLAG_MUTABLE
+ )
+ } else {
+ mPendingIntent = PendingIntent.getBroadcast(
+ this,
+ 0,
+ Intent(Constants.HOMOCUBE_USB_PERMISSION),
+ PendingIntent.FLAG_ONE_SHOT or PendingIntent.FLAG_IMMUTABLE
+ )
+ }
+
+ val filter = IntentFilter(Constants.HOMOCUBE_USB_PERMISSION)
+ registerReceiver(broadcastReceiver, filter)
+ manager.requestPermission(device, mPendingIntent)
+ }
+
+ fun setupService() {
+ val intent = Intent(this, UsbService::class.java)
+ bindService(intent, connection, Context.BIND_AUTO_CREATE)
+ }
+
+ open fun reconnectDevice() {
+ mService.disconnect()
+ unbindService(connection)
+ viewModel.isServiceConnected = false
+ connectUsb(true)
+ }
+
+ private fun moveToNext() {
+ if (supportFragmentManager.isDestroyed) return
+
+ supportFragmentManager.beginTransaction().replace(binding.fghemocube.id, HemoCubeFragment())
+ .commit()
+ }
+
+
+ override fun onBackPressed() {
+ val fragment = supportFragmentManager.findFragmentById(R.id.fghemocube)
+ if (fragment is HemoCubeFragment) {
+ fragment.handleBackButtonPress()
+ } else {
+ super.onBackPressed()
+ }
+ }
+
+
+ fun onErrorReported(msg: String) {
+ Toast.makeText(this, msg, Toast.LENGTH_SHORT).show()
+ if (!isFinishing) onBackPressed()
+ }
+
+ override fun onCreateOptionsMenu(menu: Menu?): Boolean {
+ menuInflater.inflate(R.menu.my_menu, menu)
+ return true
+ }
+
+ override fun onDestroy() {
+ super.onDestroy()
+ if (viewModel.isServiceConnected) {
+ mService.disconnect()
+ unbindService(connection)
+ viewModel.isServiceConnected = false
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/hpostesting/presentation/trueheme/TrueHemeFragment.kt b/app/src/main/java/com/example/hpostesting/presentation/trueheme/TrueHemeFragment.kt
new file mode 100644
index 0000000..be89edf
--- /dev/null
+++ b/app/src/main/java/com/example/hpostesting/presentation/trueheme/TrueHemeFragment.kt
@@ -0,0 +1,1144 @@
+package com.example.hpostesting.presentation.trueheme
+
+import android.content.Context
+import android.content.Intent
+import android.content.SharedPreferences
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.Toast
+import androidx.core.content.ContextCompat
+import androidx.fragment.app.Fragment
+import androidx.fragment.app.activityViewModels
+import androidx.lifecycle.MutableLiveData
+import com.example.hpostesting.data.DataHolder
+import com.example.hpostesting.data.Result
+import com.example.hpostesting.data.constant.Constants
+import com.example.hpostesting.data.constant.HemoCubeCommands
+import com.example.hpostesting.data.constant.TestStatus
+import com.example.hpostesting.data.model.TestState
+import com.example.hpostesting.data.model.patient.DeviceData
+import com.example.hpostesting.data.model.patient.toHemoCubeTestData
+import com.example.hpostesting.presentation.UsbServiceListener
+import com.example.hpostesting.presentation.dashboard.DashboardActivity
+import com.example.hpostesting.presentation.hemocube.HemocubeActivity
+import com.example.hpostesting.presentation.utils.MyDialogListener
+import com.example.hpostesting.presentation.utils.UIUtils
+import com.google.firebase.crashlytics.ktx.crashlytics
+import com.google.firebase.ktx.Firebase
+import `in`.sminnovations.hpostesting.R
+import `in`.sminnovations.hpostesting.databinding.FragmentTruehemeSampleBinding
+import kotlin.math.abs
+import kotlin.math.log10
+
+@Suppress("MemberVisibilityCanBePrivate")
+class TrueHemeFragment : Fragment() {
+
+ private lateinit var binding: FragmentTruehemeSampleBinding
+ private val hemoCubeViewModel: TrueHemeViewModel by activityViewModels()
+ private lateinit var sharedPreferences: SharedPreferences
+ private val testDetails = DataHolder.selectedTest?.toHemoCubeTestData()
+ private var isOnline = false
+ private var currentDeviceData: DeviceData? = null
+ private var resultData: String = ""
+ private var currentResultData: String = ""
+ private var isUsingExistingBuffer = false
+ private var isTestOngoing = false
+ private var startListening = MutableLiveData(false)
+ private var led1BufferForDevice = 0.0
+ private var led2BufferForDevice = 0.0
+ private var led3BufferForDevice = 0.0
+ private var led4BufferForDevice = 0.0
+ private var led1SampleForDevice = 0.0
+ private var led2SampleForDevice = 0.0
+ private var led3SampleForDevice = 0.0
+ private var led4SampleForDevice = 0.0
+ private var fittedAbs1 = 0.0
+ private var fittedAbs2 = 0.0
+ private var fittedAbs3 = 0.0
+ private var fittedAbs4 = 0.0
+ private var calculatedPredictedDenovixRatio = 0.0
+ private var validationError = false
+ private var deviceHardwareId = ""
+ private var allErrorMessages = ""
+ private var testStatusCode = 0.0
+ private var repeatReadingCount = 0
+ private var readingsPerSample = Constants.READINGS_PER_SAMPLE
+ private var uploadedToCloud = false
+ private var uploadedToMolbio = false
+ lateinit var testState: TestState
+
+ override fun onCreateView(
+ inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?,
+ ): View {
+ binding = FragmentTruehemeSampleBinding.inflate(inflater, container, false)
+ sharedPreferences =
+ requireContext().getSharedPreferences("HEMOCUBE", Context.MODE_PRIVATE)
+
+ testState = TestState(
+ testDetails = DataHolder.selectedTest?.toHemoCubeTestData(),
+ )
+
+ return binding.root
+ }
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
+ initViews()
+ listenToHemoCube()
+ getDeviceInfo()
+ observeViewModel()
+ }
+
+ private fun initViews() {
+ binding.btnSubmit.setOnClickListener {
+ activity?.runOnUiThread {
+ binding.progressBar.visibility = View.VISIBLE
+ binding.btnSubmit.visibility = View.GONE
+ }
+ hemoCubeViewModel.uploadHemoCubeResultToDatabase(
+ isOnline, true, sharedPreferences.getString(Constants.KIT_NUMBER, "")
+ )
+ }
+
+ binding.tvTitle2.visibility = View.GONE
+ binding.etAbhaId.visibility = View.GONE
+ binding.nameEditText.visibility = View.GONE
+ binding.tvTitle.visibility = View.GONE
+ binding.btnGo.visibility = View.GONE
+// binding.btnSubmit.isEnabled = false
+// binding.btnSubmit.isClickable = false
+ binding.btnPlacebuffer.visibility = View.GONE
+
+ binding.tvName.text = "Name: ${testDetails?.name}\n ID: ${testDetails?._id}"
+ binding.tvSubtitle4.text = "Config"
+
+ binding.btnSamplestart.setOnClickListener {
+ activity?.runOnUiThread {
+ binding.tvSubtitle4.visibility = View.VISIBLE
+// binding.tvSubtitle4.text = "Sample Started"
+ }
+ startSampleProcess()
+ it.visibility = View.GONE
+ }
+
+ binding.btnPlacebuffer.setOnClickListener {
+ if (isBufferValueAvailable() || !Constants.BLANK_EVERY_TEST) {
+ showBufferAlertDialog()
+ } else {
+ activity?.runOnUiThread {
+ binding.tvSubtitle4.visibility = View.VISIBLE
+// binding.tvSubtitle4.text = "Buffer Started"
+ }
+
+ checkAndStartProcess()
+ it.visibility = View.GONE
+ }
+
+ }
+
+ if (sharedPreferences.getString(Constants.USER_ID, "").toString() == "ADMIN") {
+ activity?.runOnUiThread {
+ binding.tvDeviceMessages.visibility = View.VISIBLE
+ }
+ }
+ }
+
+ private fun observeViewModel() {
+ hemoCubeViewModel.fireBaseUpload.observe(viewLifecycleOwner) { result ->
+ if (result == "Success") {
+ uploadedToCloud = true
+ showToast(R.string.test_upload)
+ hemoCubeViewModel.resultUpload.observe(viewLifecycleOwner) {
+ when (it) {
+ is Result.Success -> {
+ uploadedToMolbio = true
+ if (Constants.MOLBIO_INTERGATION) {
+ it.data.data?.get(0)?.rawData?.let { it1 ->
+ hemoCubeViewModel.updateMolbioFlag(
+ it1._id
+ )
+ }
+ }
+ handleReadingFinish()
+ }
+ is Result.Error -> {
+ binding.btnSubmit.visibility = View.VISIBLE
+ //Remove this line of code while deploying to IOCL
+ it.exception.let { message ->
+ Toast.makeText(activity, "An error occurred: $message", Toast.LENGTH_LONG)
+ .show()
+ }
+ handleReadingFinish()
+ }
+
+ else -> {}
+ }
+ }
+ }
+ if (result == "Local") {
+ showToast(R.string.internt_not_local)
+ startActivity(Intent(requireActivity(), DashboardActivity::class.java))
+ }
+ if (result == "Error") {
+ showToast(R.string.error_local)
+ startActivity(Intent(requireActivity(), DashboardActivity::class.java))
+ }
+
+ binding.progressBar.visibility = View.GONE
+ }
+
+ hemoCubeViewModel.getDeviceData(sharedPreferences.getString(Constants.USER_ID, ""))
+
+ hemoCubeViewModel.deviceData.observe(viewLifecycleOwner) {
+ currentDeviceData = it
+ }
+
+ hemoCubeViewModel.networkStatusLiveData.observe(viewLifecycleOwner) { isNetworkAvailable ->
+ apply {
+ DataHolder.hemoCubeTestData?.let {
+ currentDeviceData?.coefficients?.let { coefficients ->
+// val coefficient1 = coefficients[0]
+// val coefficient2 = coefficients[1]
+// val result = coefficient1 * coefficient2
+ }
+ }
+ isOnline = isNetworkAvailable
+ }
+ }
+
+ hemoCubeViewModel.messages.observe(viewLifecycleOwner) {
+ binding.tvSubtitle4.text = it
+ }
+
+ hemoCubeViewModel.deviceMessages.observe(viewLifecycleOwner) {
+ binding.tvDeviceMessages.text = it
+ }
+ }
+
+ private fun handleReadingFinish() {
+ if (allReadingsComplete(repeatReadingCount, readingsPerSample) && uploadedToCloud) {
+ activity?.runOnUiThread {
+ binding.btnSubmit.visibility = View.GONE
+ val i = Intent(
+ requireContext().applicationContext, DashboardActivity::class.java
+ )
+ startActivity(i)
+ }
+ } else {
+ hemoCubeViewModel.messages.postValue("Reading $repeatReadingCount completed")
+ resetTest()
+ startSampleProcess()
+ }
+ }
+
+ fun allReadingsComplete(repeatReadingCount: Int, readingsPerSample: Int): Boolean {
+ return repeatReadingCount >= readingsPerSample
+ }
+
+ private fun resetTest() {
+ testStatusCode = 0.0
+ allErrorMessages = ""
+ validationError = false
+ resultData = ""
+ currentResultData = ""
+ }
+
+ private fun checkAndStartProcess() {
+ startBufferProcess()
+ }
+
+ private fun isBufferValueAvailable(): Boolean {
+ return try {
+ if (sharedPreferences.getString(
+ Constants.BUFFER_VALUE_1, ""
+ ) != "" && sharedPreferences.getString(Constants.BUFFER_VALUE_2, "") != ""
+ && sharedPreferences.getString(Constants.BUFFER_VALUE_3, "") != ""
+ && sharedPreferences.getString(Constants.BUFFER_VALUE_4, "") != ""
+ ) {
+ sharedPreferences.getString(Constants.BUFFER_VALUE_1, "")
+ ?.toDouble()!! > 0.0 && sharedPreferences.getString(
+ Constants.BUFFER_VALUE_2,
+ ""
+ )
+ ?.toDouble()!! > 0.0 && sharedPreferences.getString(
+ Constants.BUFFER_VALUE_3,
+ ""
+ )
+ ?.toDouble()!! > 0.0 && sharedPreferences.getString(
+ Constants.BUFFER_VALUE_4,
+ ""
+ )
+ ?.toDouble()!! > 0.0
+ } else {
+ false
+ }
+ } catch (e: Exception) {
+ showToast(R.string.error_exist)
+ false
+ }
+ }
+
+ private fun showBufferAlertDialog() {
+ val title = "WARNING"
+ val message = getString(R.string.do_exist)
+ val negativeText = getString(R.string.no)
+ val positiveText = getString(R.string.yes)
+
+ UIUtils.createAlertDialog(requireContext(),
+ title,
+ message,
+ negativeText,
+ positiveText,
+ object : MyDialogListener {
+ override fun onClickNegativeButton() {
+ startBufferProcess()
+ }
+
+ override fun onClickPositiveButton() {
+ activity?.runOnUiThread {
+ binding.btnPlacebuffer.visibility = View.GONE
+ binding.btnSamplestart.visibility = View.VISIBLE
+ binding.tvSubtitle4.text = getString(R.string.place_sample)
+ }
+ isUsingExistingBuffer = true
+ }
+ })
+ }
+
+ private fun listenToHemoCube() {
+ DataHolder.hemoCubeTestData = DataHolder.selectedTest?.toHemoCubeTestData()
+ hemoCubeViewModel.progressBar.postValue(true)
+
+ val fullReadOutput = StringBuilder()
+ startListening.postValue(true)
+
+ try {
+ (activity as HemocubeActivity).mService.listenToHemoCube(object : UsbServiceListener {
+ override fun onUsbRead(data: ByteArray?) {
+ data?.let {
+ val stringData = String(it)
+ fullReadOutput.append(stringData)
+ handleUsbData(stringData)
+ }
+ }
+
+ override fun onUsbError(e: Exception?) {
+ hemoCubeViewModel.progressBar.postValue(false)
+ }
+ })
+ } catch (e: Exception) {
+ showToast(R.string.test_ongoing)
+ Firebase.crashlytics.recordException(e)
+ }
+ }
+
+ private fun getDeviceInfo() {
+ hemoCubeViewModel.progressBar.postValue(true)
+ (activity as HemocubeActivity).mService.sendAndListenToHemoCube(
+ HemoCubeCommands.DEVICE_CONFIGURATION_COMMAND,
+ object : UsbServiceListener {
+ override fun onUsbRead(data: ByteArray?) {
+ data?.let {
+ val stringData = String(it)
+ hemoCubeViewModel.messages.postValue(stringData)
+ binding.tvSubtitle4.text = stringData
+ }
+ }
+
+ override fun onUsbError(e: Exception?) {
+ hemoCubeViewModel.progressBar.postValue(false)
+ }
+ })
+ }
+
+ fun handleUsbData(stringData: String) {
+ if (stringData.contains("#")) {
+ isTestOngoing = true
+ }
+
+ resultData += stringData
+ currentResultData += stringData
+ hemoCubeViewModel.deviceMessages.postValue(currentResultData)
+
+ when {
+ resultData.contains("SNE") && this.testStatusCode < TestStatus.CONFIG_COMPLETED.code -> {
+ processV2HardwareId(resultData)
+ }
+
+ (resultData.contains("SN") && !resultData.contains("SNS") && !resultData.contains("SNE") && resultData.length >= 15) && this.testStatusCode < TestStatus.CONFIG_COMPLETED.code -> {
+ processV1HardwareId(resultData)
+ }
+
+ (resultData.contains("#LS") && this.testStatusCode < TestStatus.FIRST_EMPTY_AIR_READING_STARTED.code) -> {
+ // air reading 1
+ this.testStatusCode = TestStatus.FIRST_EMPTY_AIR_READING_STARTED.code
+ hemoCubeViewModel.messages.postValue("Air reading started")
+ }
+
+ (resultData.contains("#LC") && this.testStatusCode < TestStatus.FIRST_EMPTY_AIR_READING_COMPLETED.code) -> {
+ // air reading 1, send command to print
+ this.testStatusCode = TestStatus.FIRST_EMPTY_AIR_READING_COMPLETED.code
+ hemoCubeViewModel.messages.postValue("Air reading completed")
+ fetchResult()
+ }
+
+ resultData.contains("#BS") && this.testStatusCode < TestStatus.BUFFER_STARTED.code -> {
+ hemoCubeViewModel.messages.postValue(getString(R.string.buffer_started))
+ this.testStatusCode = TestStatus.BUFFER_STARTED.code
+ }
+
+ resultData.contains("#BC") && this.testStatusCode < TestStatus.BUFFER_COMPLETED.code -> {
+ this.testStatusCode = TestStatus.BUFFER_COMPLETED.code
+ activity?.runOnUiThread {
+ binding.tvSubtitle4.text = getString(R.string.buffer_completed)
+ binding.btnSamplestart.visibility = View.VISIBLE
+ }
+ }
+
+ (resultData.contains("#SS") || resultData.contains("#SS1")) && this.testStatusCode < TestStatus.SAMPLE_STARTED.code -> {
+ this.testStatusCode = TestStatus.SAMPLE_STARTED.code
+ activity?.runOnUiThread {
+ binding.tvSubtitle4.text = getString(R.string.sample_started)
+ binding.btnSamplestart.visibility = View.GONE
+ }
+ }
+
+ (resultData.contains("#SC") || resultData.contains("#SC1")) && this.testStatusCode < TestStatus.SAMPLE_COMPLETED.code -> {
+ this.testStatusCode = TestStatus.SAMPLE_COMPLETED.code
+ hemoCubeViewModel.messages.postValue(
+ getString(R.string.sample_completed) + "\n" + getString(R.string.gathering_data))
+ fetchResult()
+ currentResultData = ""
+ }
+
+ resultData.contains("ovf") -> {
+ hemoCubeViewModel.messages.postValue(
+ getString(R.string.power_bank)
+ )
+ }
+
+ currentResultData.contains("#SS2") && this.testStatusCode < TestStatus.FIRST_GAIN_STARTED.code -> {
+ this.testStatusCode = TestStatus.FIRST_GAIN_STARTED.code
+ hemoCubeViewModel.messages.postValue("1.3X Gain Started")
+ }
+
+ currentResultData.contains("#SC2") && this.testStatusCode < TestStatus.FIRST_GAIN_COMPLETED.code -> {
+ this.testStatusCode = TestStatus.FIRST_GAIN_COMPLETED.code
+ hemoCubeViewModel.messages.postValue("1.3X Gain Completed")
+ fetchResult()
+ }
+
+ currentResultData.contains("#SS3") && this.testStatusCode < TestStatus.SECOND_GAIN_STARTED.code -> {
+ this.testStatusCode = TestStatus.SECOND_GAIN_STARTED.code
+ hemoCubeViewModel.messages.postValue("2X Gain Started")
+ }
+
+ currentResultData.contains("#SC3") && this.testStatusCode < TestStatus.SECOND_GAIN_COMPLETED.code -> {
+ this.testStatusCode = TestStatus.SECOND_GAIN_COMPLETED.code
+ hemoCubeViewModel.messages.postValue("2X Gain Completed")
+ fetchResult()
+ }
+
+ currentResultData.contains("#SS5") && this.testStatusCode < TestStatus.FORTH_GAIN_STARTED.code -> {
+ this.testStatusCode = TestStatus.FORTH_GAIN_STARTED.code
+ hemoCubeViewModel.messages.postValue("7.6X Gain Started")
+ }
+
+ currentResultData.contains("#SC5") && this.testStatusCode < TestStatus.FORTH_GAIN_COMPLETED.code -> {
+ this.testStatusCode = TestStatus.FORTH_GAIN_COMPLETED.code
+ hemoCubeViewModel.messages.postValue("7.6X Gain Completed")
+ fetchResult()
+ }
+
+ (resultData.contains("#LS") && this.testStatusCode < TestStatus.SECOND_EMPTY_AIR_READING_STARTED.code) -> {
+ // air reading 2
+ this.testStatusCode = TestStatus.SECOND_EMPTY_AIR_READING_STARTED.code
+ hemoCubeViewModel.messages.postValue("Air reading started")
+ }
+
+ (resultData.contains("#LC") && this.testStatusCode < TestStatus.SECOND_EMPTY_AIR_READING_COMPLETED.code) -> {
+ // air reading 2, print values
+ this.testStatusCode = TestStatus.SECOND_EMPTY_AIR_READING_COMPLETED.code
+ hemoCubeViewModel.messages.postValue("Air reading completed")
+ fetchResult()
+ }
+
+ resultData.contains("REND") && this.testStatusCode < TestStatus.SAMPLE_PRINT_COMPLETED.code -> {
+ handleSampleCompleted()
+ }
+
+ currentResultData.contains("REND")
+ && this.testStatusCode >= TestStatus.FIRST_EMPTY_AIR_READING_COMPLETED.code
+ && this.testStatusCode < TestStatus.FIRST_EMPTY_AIR_READING_PRINT_COMPLETED.code -> {
+ this.testStatusCode = TestStatus.FIRST_GAIN_PRINT_COMPLETED.code
+ hemoCubeViewModel.messages.postValue("First air reading completed")
+
+ val resultLines = currentResultData.split("\\s+(?=LB|LS)".toRegex())
+ DataHolder.hemoCubeTestData?.apply {
+ led1Air1 = resultLines[5].split(' ')[1].trim().toDoubleOrNull()!!
+ led2Air1 = resultLines[6].split(' ')[1].trim().toDoubleOrNull()!!
+ led3Air1 = resultLines[7].split(' ')[1].trim().toDoubleOrNull()!!
+ led4Air1 = resultLines[8].split(' ')[1].split('\r')[0].trim().toDoubleOrNull()!!
+ }
+
+ startBufferProcess()
+ currentResultData = ""
+ }
+
+ currentResultData.contains("REND")
+ && this.testStatusCode >= TestStatus.SECOND_EMPTY_AIR_READING_COMPLETED.code
+ && this.testStatusCode < TestStatus.SECOND_EMPTY_AIR_PRINT_COMPLETED.code -> {
+ this.testStatusCode = TestStatus.SECOND_EMPTY_AIR_PRINT_COMPLETED.code
+ hemoCubeViewModel.messages.postValue("Second air reading completed")
+
+ val resultLines = currentResultData.split("\\s+(?=LB|LS)".toRegex())
+ DataHolder.hemoCubeTestData?.apply {
+ led1Air2 = resultLines[5].split(' ')[1].trim().toDoubleOrNull()!!
+ led2Air2 = resultLines[6].split(' ')[1].trim().toDoubleOrNull()!!
+ led3Air2 = resultLines[7].split(' ')[1].trim().toDoubleOrNull()!!
+ led4Air2 = resultLines[8].split(' ')[1].split('\r')[0].trim().toDoubleOrNull()!!
+ }
+
+ sendSecondGainCommand()
+ currentResultData = ""
+ }
+
+ currentResultData.contains("REND")
+ && this.testStatusCode >= TestStatus.FIRST_GAIN_COMPLETED.code
+ && this.testStatusCode < TestStatus.FIRST_GAIN_PRINT_COMPLETED.code -> {
+ this.testStatusCode = TestStatus.FIRST_GAIN_PRINT_COMPLETED.code
+ hemoCubeViewModel.messages.postValue("1.3X gain data gathered")
+
+ val resultLines = currentResultData.split("\\s+(?=LB|LS)".toRegex())
+ DataHolder.hemoCubeTestData?.apply {
+ led1Gain1 = resultLines[5].split(' ')[1].trim().toDoubleOrNull()!!
+ led2Gain1 = resultLines[6].split(' ')[1].trim().toDoubleOrNull()!!
+ led3Gain1 = resultLines[7].split(' ')[1].trim().toDoubleOrNull()!!
+ led4Gain1 = resultLines[8].split(' ')[1].split('\r')[0].trim().toDoubleOrNull()!!
+ }
+
+ sendSecondGainCommand()
+ currentResultData = ""
+ }
+
+ currentResultData.contains("REND")
+ && this.testStatusCode >= TestStatus.FIRST_GAIN_PRINT_COMPLETED.code
+ && this.testStatusCode < TestStatus.SECOND_GAIN_PRINT_COMPLETED.code -> {
+ this.testStatusCode = TestStatus.SECOND_GAIN_PRINT_COMPLETED.code
+ hemoCubeViewModel.messages.postValue("2X gain data gathered")
+
+ val resultLines = currentResultData.split("\\s+(?=LB|LS)".toRegex())
+ DataHolder.hemoCubeTestData?.apply {
+ led1Gain2 = resultLines[5].split(' ')[1].trim().toDoubleOrNull()!!
+ led2Gain2 = resultLines[6].split(' ')[1].trim().toDoubleOrNull()!!
+ led3Gain2 = resultLines[7].split(' ')[1].trim().toDoubleOrNull()!!
+ led4Gain2 = resultLines[8].split(' ')[1].split('\r')[0].trim().toDoubleOrNull()!!
+ }
+
+ sendForthGainCommand()
+ currentResultData = ""
+ }
+
+ currentResultData.contains("REND")
+ && this.testStatusCode >= TestStatus.SECOND_GAIN_PRINT_COMPLETED.code
+ && this.testStatusCode < TestStatus.FORTH_GAIN_PRINT_COMPLETED.code -> {
+ this.testStatusCode = TestStatus.FORTH_GAIN_PRINT_COMPLETED.code
+ hemoCubeViewModel.messages.postValue("7.6X gain data gathered")
+
+ val resultLines = currentResultData.split("\\s+(?=LB|LS)".toRegex())
+ DataHolder.hemoCubeTestData?.apply {
+ led1Gain4 = resultLines[5].split(' ')[1].trim().toDoubleOrNull()!!
+ led2Gain4 = resultLines[6].split(' ')[1].trim().toDoubleOrNull()!!
+ led3Gain4 = resultLines[7].split(' ')[1].trim().toDoubleOrNull()!!
+ led4Gain4 = resultLines[8].split(' ')[1].split('\r')[0].trim().toDoubleOrNull()!!
+ }
+
+ finishReading()
+ }
+ }
+ }
+
+ fun finishReading() {
+ repeatReadingCount += 1
+
+ if (readingsPerSample == 1) {
+ activity?.runOnUiThread {
+ binding.btnSubmit.visibility = View.VISIBLE
+ binding.btnSubmit.isEnabled = true
+ binding.btnSubmit.isClickable = true
+// binding.clParent.setBackgroundColor(Color.parseColor("#edfffd"))
+ binding.ivCheck.visibility = View.VISIBLE
+ }
+ } else {
+ hemoCubeViewModel.uploadHemoCubeResultToDatabase(
+ isOnline, true, sharedPreferences.getString(Constants.KIT_NUMBER, "")
+ )
+ }
+ }
+
+ fun handleSampleCompleted() {
+
+ this.testStatusCode = TestStatus.SAMPLE_PRINT_COMPLETED.code
+ hemoCubeViewModel.messages.postValue(
+ getString(R.string.data_collected_processing_data)
+ )
+
+ val resultLines = resultData.split("\\s+(?=LB|LS)".toRegex())
+ var bufferIntensity = resultLines[1].split(' ')[1].trim()
+ led1BufferForDevice = if (isUsingExistingBuffer) {
+ sharedPreferences.getString(Constants.BUFFER_VALUE_1, "")?.toDoubleOrNull()!!
+ } else {
+ bufferIntensity.toDoubleOrNull()!!
+ }
+ bufferIntensity = resultLines[2].split(' ')[1].trim()
+ led2BufferForDevice = if (isUsingExistingBuffer) {
+ sharedPreferences.getString(Constants.BUFFER_VALUE_2, "")?.toDoubleOrNull()!!
+ } else {
+ bufferIntensity.toDoubleOrNull()!!
+ }
+ bufferIntensity = resultLines[3].split(' ')[1].trim()
+ led3BufferForDevice = if (isUsingExistingBuffer) {
+ sharedPreferences.getString(Constants.BUFFER_VALUE_3, "")?.toDoubleOrNull()!!
+ } else {
+ bufferIntensity.toDoubleOrNull()!!
+ }
+ bufferIntensity = resultLines[4].split(' ')[1].trim()
+ led4BufferForDevice = if (isUsingExistingBuffer) {
+ sharedPreferences.getString(Constants.BUFFER_VALUE_4, "")?.toDoubleOrNull()!!
+ } else {
+ bufferIntensity.toDoubleOrNull()!!
+ }
+
+ led1SampleForDevice = resultLines[5].split(' ')[1].trim().toDoubleOrNull()!!
+ led2SampleForDevice = resultLines[6].split(' ')[1].trim().toDoubleOrNull()!!
+ led3SampleForDevice = resultLines[7].split(' ')[1].trim().toDoubleOrNull()!!
+ led4SampleForDevice =
+ resultLines[8].split(' ')[1].split('\r')[0].trim().toDoubleOrNull()!!
+ processResult()
+ if (Constants.PQ_MODE) {
+ finishReading()
+ } else {
+ sendFirstGainCommand()
+ }
+ currentResultData = ""
+ }
+
+ fun processV1HardwareId(resultData: String) {
+ val hardwareId = extractV1HardwareId(resultData)
+
+ if (!hardwareId.isNullOrBlank()) {
+ updateDeviceId(hardwareId)
+ activity?.runOnUiThread {
+ binding.btnPlacebuffer.visibility = View.VISIBLE
+ }
+ hemoCubeViewModel.messages.postValue(getString(R.string.start))
+ } else {
+ hemoCubeViewModel.messages.postValue("Config error")
+ }
+
+ if (!Constants.DEVICE_CONFIGURATION.containsKey(deviceHardwareId)) {
+ assignDefaultDevice(resultData)
+ testState.allErrorMessages += "Calibration configuration for this device id is not found\n"
+ }
+ if (!Constants.BUFFER_INTENSITY_THRESHOLDS.containsKey(deviceHardwareId)) {
+ testState.allErrorMessages += "ADC thresholds for this device id are not found\n"
+ }
+ }
+
+ fun extractV1HardwareId(input: String): String? {
+ val regex = Regex("SN (\\S+)")
+ val matchResult = regex.find(input)
+
+ return matchResult?.groupValues?.get(1)
+ }
+
+ fun processV2HardwareId(resultData: String) {
+ val hardwareId = extractV2HardwareId(resultData)
+
+ if (!hardwareId.isNullOrBlank()) {
+ updateDeviceId(hardwareId)
+ activity?.runOnUiThread {
+ binding.btnPlacebuffer.visibility = View.VISIBLE
+ }
+ hemoCubeViewModel.messages.postValue(getString(R.string.start))
+ } else {
+ hemoCubeViewModel.messages.postValue("Config error")
+ }
+
+ if (!Constants.DEVICE_CONFIGURATION.containsKey(deviceHardwareId)) {
+ assignDefaultDevice(resultData)
+ testState.allErrorMessages += "Calibration configuration for this device id is not found\n"
+ }
+ if (!Constants.BUFFER_INTENSITY_THRESHOLDS.containsKey(deviceHardwareId)) {
+ testState.allErrorMessages += "ADC thresholds for this device id are not found\n"
+ }
+ }
+
+ fun assignDefaultDevice(configData: String) {
+ deviceHardwareId = "HCV-000-3001"
+ testStatusCode = TestStatus.CONFIG_COMPLETED.code
+ activity?.runOnUiThread {
+ binding.btnPlacebuffer.visibility = View.VISIBLE
+ }
+ hemoCubeViewModel.messages.postValue(getString(R.string.start))
+ }
+
+ fun extractV2HardwareId(input: String): String? {
+ val pattern = Regex("SNS\\s*(.*?)\\s*SNE")
+ val matchResult: MatchResult? = pattern.find(input)
+
+ return matchResult?.groups?.get(1)?.value
+ }
+
+ fun updateDeviceId(hardwareId: String?) {
+ hardwareId?.let {
+ deviceHardwareId += it
+ with(sharedPreferences.edit()) {
+ putString(Constants.DEVICE_ID, deviceHardwareId)
+ apply()
+ }
+ testStatusCode = TestStatus.CONFIG_COMPLETED.code
+ }
+ }
+
+ fun handleBackButtonPress() {
+ if (isTestOngoing) {
+ showToast(R.string.test_go_noback)
+ } else {
+ activity?.onBackPressed()
+ }
+ }
+
+ private fun processResult() {
+ try {
+ hemoCubeViewModel.messages.postValue(getString(R.string.processing_result))
+ val deviceLog = resultData
+
+ val pInfo = requireActivity().packageManager.getPackageInfo(
+ requireActivity().packageName, 0
+ )
+ val version = pInfo.versionName
+
+ val led1Average = log10(led1BufferForDevice.div(led1SampleForDevice))
+ val led2Average = log10(led2BufferForDevice.div(led2SampleForDevice))
+ val led3Average = log10(led3BufferForDevice.div(led3SampleForDevice))
+ val led4Average = log10(led4BufferForDevice.div(led4SampleForDevice))
+ val deviceRatio = led4Average / led1Average
+
+ if (led1BufferForDevice < Constants.BUFFER_INTENSITY_THRESHOLDS[deviceHardwareId]?.get(0)
+ ?.get(0)!!
+ || led2BufferForDevice < Constants.BUFFER_INTENSITY_THRESHOLDS[deviceHardwareId]?.get(
+ 1
+ )?.get(0)!!
+ || led3BufferForDevice < Constants.BUFFER_INTENSITY_THRESHOLDS[deviceHardwareId]?.get(
+ 2
+ )?.get(0)!!
+ || led4BufferForDevice < Constants.BUFFER_INTENSITY_THRESHOLDS[deviceHardwareId]?.get(
+ 3
+ )?.get(0)!!
+ ) {
+ validationError = true
+ testState.allErrorMessages += "Error: Invalid Test. Improper buffer reading (low)"
+ activity?.runOnUiThread {
+ binding.errorMessage.text = getString(R.string.error_improper_buffer_low)
+// binding.errorMessage.visibility = View.VISIBLE
+ }
+ }
+
+ if (led1BufferForDevice > Constants.BUFFER_INTENSITY_THRESHOLDS[deviceHardwareId]?.get(0)
+ ?.get(1)!!
+ || led2BufferForDevice > Constants.BUFFER_INTENSITY_THRESHOLDS[deviceHardwareId]?.get(
+ 1
+ )?.get(1)!!
+ || led3BufferForDevice > Constants.BUFFER_INTENSITY_THRESHOLDS[deviceHardwareId]?.get(
+ 2
+ )?.get(1)!!
+ || led4BufferForDevice > Constants.BUFFER_INTENSITY_THRESHOLDS[deviceHardwareId]?.get(
+ 3
+ )?.get(1)!!
+ ) {
+ validationError = true
+ testState.allErrorMessages += "Error: Invalid Test. Improper buffer reading (high)" + "\n"
+ activity?.runOnUiThread {
+ binding.errorMessage.text =
+ getString(R.string.error_improper_buffer_high)
+ binding.errorMessage.visibility = View.VISIBLE
+ }
+ }
+
+ var gradient = Constants.DEVICE_CONFIGURATION[deviceHardwareId]?.get(0)?.get(0)
+ var constant = Constants.DEVICE_CONFIGURATION[deviceHardwareId]?.get(0)?.get(1)
+ fittedAbs1 = gradient?.times(led1Average)?.plus(constant!!)!!
+
+ gradient = Constants.DEVICE_CONFIGURATION[deviceHardwareId]?.get(1)?.get(0)
+ constant = Constants.DEVICE_CONFIGURATION[deviceHardwareId]?.get(1)?.get(1)
+ fittedAbs2 = gradient?.times(led2Average)?.plus(constant!!)!!
+
+ gradient = Constants.DEVICE_CONFIGURATION[deviceHardwareId]?.get(2)?.get(0)
+ constant = Constants.DEVICE_CONFIGURATION[deviceHardwareId]?.get(2)?.get(1)
+ fittedAbs3 = gradient?.times(led3Average)?.plus(constant!!)!!
+
+ gradient = Constants.DEVICE_CONFIGURATION[deviceHardwareId]?.get(0)?.get(0)
+ constant = Constants.DEVICE_CONFIGURATION[deviceHardwareId]?.get(0)?.get(1)
+ fittedAbs4 = gradient?.times(led4Average)?.plus(constant!!)!!
+
+ val slope1 = Constants.DEVICE_HB_PARAMETERS[deviceHardwareId]?.get(1)?.get(0)
+ val intercept1 = Constants.DEVICE_HB_PARAMETERS[deviceHardwareId]?.get(1)?.get(1)
+ val calculatedHb1 = (led1Average - intercept1!!) / slope1!!
+
+ val slope2 = Constants.DEVICE_HB_PARAMETERS[deviceHardwareId]?.get(3)?.get(0)
+ val intercept = Constants.DEVICE_HB_PARAMETERS[deviceHardwareId]?.get(3)?.get(1)
+ val calculatedHb2 = (led2Average - intercept!!) / slope2!!
+
+ val slope3 = Constants.DEVICE_HB_PARAMETERS[deviceHardwareId]?.get(2)?.get(0)
+ val intercept3 = Constants.DEVICE_HB_PARAMETERS[deviceHardwareId]?.get(2)?.get(1)
+ val calculatedHb3 = (led3Average - intercept3!!) / slope3!!
+
+ val slope4 = Constants.DEVICE_HB_PARAMETERS[deviceHardwareId]?.get(3)?.get(0)
+ val intercept4 = Constants.DEVICE_HB_PARAMETERS[deviceHardwareId]?.get(3)?.get(1)
+ val calculatedHb4 = (led4Average - intercept4!!) / slope4!!
+
+ calculatedPredictedDenovixRatio = fittedAbs3.div(fittedAbs1)
+
+ val slope = (led1Average - led2Average) / (435 - 415)
+ val calculatedSlopeRatio = abs(led3Average / slope)
+ val slopeClass = slopeRatioClassification(calculatedSlopeRatio)
+
+ if (fittedAbs1 <= fittedAbs2) {
+ validationError = true
+ testState.allErrorMessages += "Error: Invalid Test. Problem with de-oxygenation" + "\n"
+ activity?.runOnUiThread {
+ binding.errorMessage.text = getString(R.string.error_invalid_test)
+// binding.errorMessage.visibility = View.VISIBLE
+ }
+ }
+
+ if (fittedAbs1 < 0 || fittedAbs2 < 0 || fittedAbs3 < 0 || fittedAbs4 < 0) {
+ validationError = true
+ activity?.runOnUiThread {
+ binding.errorMessage.text = getString(R.string.error_negative_abs)
+// binding.errorMessage.visibility = View.VISIBLE
+ }
+ }
+
+ if ((led1Average < 0.7 || led2Average < 0.7 || led3Average < 0.7 || led4Average < 0.7) && calculatedSlopeRatio > 35.0) {
+ validationError = true
+ activity?.runOnUiThread {
+ binding.errorMessage.text = "Severely Low Hb. Repeat test with 12 ul in 2 ml Buffer"
+ binding.errorMessage.visibility = View.VISIBLE
+ }
+ }
+
+ if (fittedAbs3 < 0.1) {
+ validationError = true
+ testState.allErrorMessages += "Error: Low Hb. Repeat test" + "\n"
+ activity?.runOnUiThread {
+ binding.errorMessage.text = "Error: Low Hb. Repeat test"
+// binding.errorMessage.visibility = View.VISIBLE
+ }
+ }
+
+ DataHolder.hemoCubeTestData?.apply {
+ deviceId = sharedPreferences.getString(Constants.DEVICE_ID, "").toString()
+ led1Buffer = led1BufferForDevice
+ led2Buffer = led2BufferForDevice
+ led3Buffer = led3BufferForDevice
+ led4Buffer = led4BufferForDevice
+ appVersion = version
+ this.led1Sample = led1SampleForDevice
+ this.led2Sample = led2SampleForDevice
+ this.led3Sample = led3SampleForDevice
+ this.led4Sample = led4SampleForDevice
+ this.led1Average = led1Average
+ this.led2Average = led2Average
+ this.led3Average = led3Average
+ this.led4Average = led4Average
+ this.abs1 = fittedAbs1
+ this.abs2 = fittedAbs2
+ this.abs3 = fittedAbs3
+ this.abs4 = fittedAbs4
+ this.hb3 = calculatedHb3
+ this.hb4 = calculatedHb4
+ this.deviceRatio = deviceRatio
+ this.calculatedRatio = calculateRatio(deviceRatio)
+ this.predictedDenovixRatio = calculatedPredictedDenovixRatio
+ this.slopeRatio = calculatedSlopeRatio
+ this.coefficients = currentDeviceData?.coefficients?.get(0)
+ .toString() + ", " + currentDeviceData?.coefficients?.get(1).toString()
+ this.prdClassification = absorbanceBasedClassification(predictedDenovixRatio)
+ this.deviceRatioClass = deviceRatioClassification(deviceRatio)
+ this.slopeRatioClass = slopeClass
+ this.classificationResult = deviceRatioClass //findResult(calculatedRatio)
+ hemoCubeViewModel.messages.postValue("${this.deviceRatioClass} ${if (led4Average < 0.17) " - Low Hb" else ""}\n")
+ if (DataHolder.hemoCubeTestData?.testType == "HB")
+ hemoCubeViewModel.messages.postValue("Hb: $calculatedHb4")
+ this.errorMessages = testState.allErrorMessages
+ this.resultData = deviceLog
+ this.batteryLevel = hemoCubeViewModel.getBatteryLevel().toString()
+ this.batteryCapacity =
+ hemoCubeViewModel.getBatteryCapacity(requireContext()).toString()
+ this.batteryMaxCapacity =
+ hemoCubeViewModel.getBatteryMaxCapacity(requireContext()).toString()
+ this.batteryTemperature = hemoCubeViewModel.getBatteryTemperature().toString()
+ this.batteryVoltage =
+ hemoCubeViewModel.getBatteryVoltage(requireContext()).toString()
+ }
+
+ if (!isUsingExistingBuffer) {
+ with(sharedPreferences.edit()) {
+ putString(Constants.BUFFER_VALUE_1, led1BufferForDevice.toString())
+ putString(Constants.BUFFER_VALUE_2, led2BufferForDevice.toString())
+ putString(Constants.BUFFER_VALUE_3, led3BufferForDevice.toString())
+ putString(Constants.BUFFER_VALUE_4, led4BufferForDevice.toString())
+ apply()
+ }
+ }
+ } catch (e: Exception) {
+ Toast.makeText(
+ requireContext(),
+ R.string.error_processing_device_data,
+ Toast.LENGTH_SHORT
+ ).show()
+ Firebase.crashlytics.recordException(e)
+ }
+ }
+
+ fun findResult(calculatedRatio: Double?): String {
+ try {
+ hemoCubeViewModel.messages.postValue("result classification")
+ if (calculatedRatio != null) {
+ if (calculatedRatio < 0.05)
+ return getString(R.string.error_repeat_test_higher_volume)
+ if (calculatedRatio in 0.05..0.155) {
+ return getString(R.string.normal)
+ }
+ if (calculatedRatio in 0.155..0.175)
+ return getString(R.string.negative_borderline)
+ if (calculatedRatio in 0.175..0.22)
+ return getString(R.string.sickle_cell_trait)
+ if (calculatedRatio in 0.22..0.25)
+ return getString(R.string.positive_for_sickle_cell)
+ if (calculatedRatio in 0.25..0.35)
+ return getString(R.string.sickle_cell_disease)
+ if (calculatedRatio > 0.35)
+ return getString(R.string.error_repeat_test_lower_volume)
+ } else {
+ return getString(R.string.invalid)
+ }
+ } catch (e: Exception) {
+ showToast(R.string.error_classification)
+ Firebase.crashlytics.recordException(e)
+ return getString(R.string.error)
+ }
+ return getString(R.string.invalid)
+ }
+
+ fun deviceRatioClassification(ratio: Double?): String {
+ try {
+ if (ratio != null) {
+ if (ratio in 0.2..0.29) {
+// setSubtitleTextColor(R.color.green_2)
+ return "Normal"
+ }
+ if (ratio in 0.29..0.32)
+ return "Negative Borderline, Repeat Test"
+ if (ratio in 0.32..0.35)
+ return "Sickle Cell Trait"
+ if (ratio in 0.35..0.38)
+ return "Positive for Sickle Cell. HPLC for Confirmation"
+ if (ratio in 0.38..0.5)
+ return "Sickle Cell Disease"
+ } else {
+ return "Invalid"
+ }
+ } catch (e: Exception) {
+ handleException(e)
+ return "Error"
+ }
+ return "Invalid"
+ }
+
+ fun setSubtitleTextColor(colorResId: Int) {
+ activity?.runOnUiThread {
+ binding.tvSubtitle4.setTextColor(ContextCompat.getColor(requireContext(), colorResId))
+ }
+ }
+
+ fun handleException(e: Exception) {
+ showToast(R.string.error_classification)
+ Firebase.crashlytics.recordException(e)
+ }
+
+ fun slopeRatioClassification(ratio: Double?): String {
+ try {
+ hemoCubeViewModel.messages.postValue("result classification")
+ if (ratio != null) {
+ if (ratio in 0.0..30.0)
+ return getString(R.string.normal)
+ if (ratio in 30.0..35.0)
+ return getString(R.string.negative_borderline)
+ if (ratio in 35.0..50.0)
+ return getString(R.string.sickle_cell_trait)
+ if (ratio in 50.0..60.0)
+ return getString(R.string.positive_for_sickle_cell)
+ if (ratio > 60.0)
+ return getString(R.string.sickle_cell_disease)
+ } else {
+ return getString(R.string.invalid)
+ }
+ } catch (e: Exception) {
+ showToast(R.string.error_classification)
+ Firebase.crashlytics.recordException(e)
+ return getString(R.string.error)
+ }
+ return getString(R.string.invalid)
+ }
+
+ private fun absorbanceBasedClassification(predictedDenovixRatio: Double?): String {
+ try {
+ hemoCubeViewModel.messages.postValue("result classification")
+ if (predictedDenovixRatio != null) {
+ if (predictedDenovixRatio in 0.0..0.16) {
+// activity?.runOnUiThread {
+// binding.tvSubtitle4.setTextColor(
+// ContextCompat.getColor(
+// requireContext(),
+// R.color.brightGreen
+// )
+// )
+// }
+ return getString(R.string.normal)
+ }
+ if (predictedDenovixRatio in 0.16..0.165)
+ return getString(R.string.negative_borderline)
+ if (predictedDenovixRatio in 0.165..0.235)
+ return getString(R.string.sickle_cell_trait)
+ if (predictedDenovixRatio in 0.235..0.24)
+ return getString(R.string.positive_borderline)
+ if (predictedDenovixRatio in 0.24..1.0)
+ return getString(R.string.sickle_cell_disease)
+ } else {
+ return getString(R.string.invalid)
+ }
+ } catch (e: Exception) {
+ showToast(R.string.error_classification)
+ Firebase.crashlytics.recordException(e)
+ return getString(R.string.error)
+ }
+ return getString(R.string.invalid)
+ }
+
+ private fun showToast(messageResId: Int) {
+ Toast.makeText(requireContext(), getString(messageResId), Toast.LENGTH_SHORT).show()
+ }
+
+ private fun startBufferProcess() {
+ hemoCubeViewModel.progressBar.postValue(true)
+
+ activity?.runOnUiThread {
+ binding.btnPlacebuffer.visibility = View.GONE
+ }
+
+ (activity as HemocubeActivity).mService.sendAndListenToHemoCube(
+ HemoCubeCommands.START_BUFFER_COMMAND,
+ object : UsbServiceListener {
+ override fun onUsbRead(data: ByteArray?) {}
+
+ override fun onUsbError(e: Exception?) {
+ hemoCubeViewModel.progressBar.postValue(false)
+ }
+ })
+ }
+
+ private fun startSampleProcess() {
+ hemoCubeViewModel.progressBar.postValue(true)
+
+ (activity as HemocubeActivity).mService.sendAndListenToHemoCube(
+ HemoCubeCommands.START_SAMPLE,
+ object : UsbServiceListener {
+ override fun onUsbRead(data: ByteArray?) {}
+
+ override fun onUsbError(e: Exception?) {
+ hemoCubeViewModel.progressBar.postValue(false)
+ }
+ })
+ }
+
+ private fun sendFirstGainCommand() {
+ hemoCubeViewModel.progressBar.postValue(true)
+
+ (activity as HemocubeActivity).mService.sendAndListenToHemoCube(
+ HemoCubeCommands.FIRST_GAIN_COMMAND,
+ object : UsbServiceListener {
+ override fun onUsbRead(data: ByteArray?) {}
+
+ override fun onUsbError(e: Exception?) {
+ hemoCubeViewModel.progressBar.postValue(false)
+ }
+ })
+ }
+
+ private fun sendSecondGainCommand() {
+ hemoCubeViewModel.progressBar.postValue(true)
+
+ (activity as HemocubeActivity).mService.sendAndListenToHemoCube(
+ HemoCubeCommands.SECOND_GAIN_COMMAND,
+ object : UsbServiceListener {
+ override fun onUsbRead(data: ByteArray?) {}
+
+ override fun onUsbError(e: Exception?) {
+ hemoCubeViewModel.progressBar.postValue(false)
+ }
+ })
+ }
+
+ private fun sendThirdGainCommand() {
+ hemoCubeViewModel.progressBar.postValue(true)
+
+ (activity as HemocubeActivity).mService.sendAndListenToHemoCube(
+ HemoCubeCommands.THIRD_GAIN_COMMAND,
+ object : UsbServiceListener {
+ override fun onUsbRead(data: ByteArray?) {}
+
+ override fun onUsbError(e: Exception?) {
+ hemoCubeViewModel.progressBar.postValue(false)
+ }
+ })
+ }
+
+ private fun sendForthGainCommand() {
+ hemoCubeViewModel.progressBar.postValue(true)
+
+ (activity as HemocubeActivity).mService.sendAndListenToHemoCube(
+ HemoCubeCommands.FORTH_GAIN_COMMAND,
+ object : UsbServiceListener {
+ override fun onUsbRead(data: ByteArray?) {}
+
+ override fun onUsbError(e: Exception?) {
+ hemoCubeViewModel.progressBar.postValue(false)
+ }
+ })
+ }
+
+ private fun fetchResult() {
+ hemoCubeViewModel.progressBar.postValue(true)
+
+ (activity as HemocubeActivity).mService.sendAndListenToHemoCube(
+ HemoCubeCommands.PRINT_COMMAND,
+ object : UsbServiceListener {
+ override fun onUsbRead(data: ByteArray?) {}
+
+ override fun onUsbError(e: Exception?) {
+ hemoCubeViewModel.progressBar.postValue(false)
+ }
+ })
+ }
+
+ private fun calculateRatio(ratio: Double): Double {
+ val coefficient1 = currentDeviceData?.coefficients?.get(0) ?: 0.0
+ val coefficient2 = currentDeviceData?.coefficients?.get(1) ?: 0.0
+ return coefficient1 * ratio + coefficient2
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/hpostesting/presentation/trueheme/TrueHemeViewModel.kt b/app/src/main/java/com/example/hpostesting/presentation/trueheme/TrueHemeViewModel.kt
new file mode 100644
index 0000000..9ebae05
--- /dev/null
+++ b/app/src/main/java/com/example/hpostesting/presentation/trueheme/TrueHemeViewModel.kt
@@ -0,0 +1,533 @@
+package com.example.hpostesting.presentation.trueheme
+
+import android.content.Context
+import android.content.Intent
+import android.content.IntentFilter
+import android.os.BatteryManager
+import android.util.Log
+import androidx.lifecycle.LiveData
+import androidx.lifecycle.MutableLiveData
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.viewModelScope
+import androidx.work.ExistingPeriodicWorkPolicy
+import androidx.work.PeriodicWorkRequestBuilder
+import androidx.work.WorkManager
+import com.example.hpostesting.data.CsvWriter
+import com.example.hpostesting.data.DataHolder
+import com.example.hpostesting.data.NetworkStatusLiveData
+import com.example.hpostesting.data.Result
+import com.example.hpostesting.data.constant.Constants
+import com.example.hpostesting.data.dao.HemoCubeBufferDao
+import com.example.hpostesting.data.dao.HemoCubeDao
+import com.example.hpostesting.data.datasource.LocalFileDataSource
+import com.example.hpostesting.data.model.Response
+import com.example.hpostesting.data.model.log.UploadLogsResponse
+import com.example.hpostesting.data.model.login.LoginRequest
+import com.example.hpostesting.data.model.login.LoginResponse
+import com.example.hpostesting.data.model.molbioresult.MolbioV2Result
+import com.example.hpostesting.data.model.molbioresult.MolbioV2ResultRequest
+import com.example.hpostesting.data.model.molbioresult.MolbioV2ResultResponse
+import com.example.hpostesting.data.model.patient.BufferCheckData
+import com.example.hpostesting.data.model.patient.DeviceData
+import com.example.hpostesting.data.model.patient.HemoCubeTestData
+import com.example.hpostesting.data.model.patient.toHemoCubeTestData
+import com.example.hpostesting.data.model.updates.CheckUpdateRequest
+import com.example.hpostesting.data.model.updates.CheckUpdateResponse
+import com.example.hpostesting.data.model.updates.DeviceUpdateRequest
+import com.example.hpostesting.data.repository.Repository
+import com.example.hpostesting.domain.CheckUpdateWorker
+import com.example.hpostesting.domain.LogFileManager
+import dagger.hilt.android.lifecycle.HiltViewModel
+import kotlinx.coroutines.launch
+import okhttp3.MediaType.Companion.toMediaTypeOrNull
+import okhttp3.MultipartBody
+import okhttp3.RequestBody.Companion.asRequestBody
+import okhttp3.ResponseBody
+import java.text.SimpleDateFormat
+import java.util.Calendar
+import java.util.Locale
+import java.util.concurrent.TimeUnit
+import javax.inject.Inject
+
+@Suppress("MemberVisibilityCanBePrivate")
+@HiltViewModel
+class TrueHemeViewModel @Inject constructor(
+ private val hemoCubeDao: HemoCubeDao,
+ private val hemoCubeBufferDao: HemoCubeBufferDao,
+ private val repository: Repository,
+ private val logFileManager: LogFileManager,
+ private val localFileDataSource: LocalFileDataSource,
+ context: Context,
+) : ViewModel() {
+ var isServiceConnected = false
+ val progressBar = MutableLiveData(false)
+ private val testDetails = DataHolder.selectedTest?.toHemoCubeTestData()
+ val messages = MutableLiveData()
+ private val sharedPreference =
+ context.getSharedPreferences("HEMOCUBE", Context.MODE_PRIVATE)
+
+ private val workManager = WorkManager.getInstance(context)
+
+// init {
+// startPeriodicCheckUpdate()
+// }
+
+ val loginResponse = MutableLiveData>()
+
+ val resultUpload = MutableLiveData>()
+
+ val checkUpdate = MutableLiveData>()
+
+ val deviceUpdate = MutableLiveData>()
+
+ val uploadLogs = MutableLiveData?>()
+
+
+ // Get the device ID of the device you want to retrieve data for (e.g., the first device in the list)
+
+ private val _networkStatusLiveData = NetworkStatusLiveData(context)
+ val allUserData = hemoCubeDao.getAll()
+ val allKitTestData = hemoCubeBufferDao.getAll()
+ val deviceData = MutableLiveData()
+
+
+ val networkStatusLiveData: LiveData
+ get() = _networkStatusLiveData
+ val deviceMessages = MutableLiveData()
+ val fireBaseUpload = MutableLiveData()
+ val fireBaseBulkUpload = MutableLiveData()
+
+ private val batteryStatus: Intent? =
+ IntentFilter(Intent.ACTION_BATTERY_CHANGED).let { ifilter ->
+ context.registerReceiver(null, ifilter)
+ }
+
+ fun uploadHemoCubeResultToDatabase(
+ isOnline: Boolean, testStatus: Boolean, kitSerial: String?,
+ ) = viewModelScope.launch {
+ if (kitSerial != null) {
+ testDetails?.kitSerial = kitSerial
+ }
+ testDetails?.testStatus = testStatus
+
+ try {
+ if (isOnline) {
+ parseData()
+ addResultTestToDb()
+ } else {
+ parseData()
+ testDetails?.testTime = SimpleDateFormat(
+ "yyyy-MM-dd HH:mm:ss", Locale.getDefault()
+ ).format(Calendar.getInstance().time)
+ hemoCubeDao.insertAll(testDetails!!)
+ fireBaseUpload.postValue("Local")
+ }
+ } catch (e: Exception) {
+ Log.e("Testdb", "Upload failed: ${e.message}")
+ }
+ }
+
+ fun login(loginRequest: LoginRequest) = viewModelScope.launch {
+ loginResponse.postValue(Result.Loading())
+ repository.login(loginRequest).let {
+ loginResponse.postValue(it)
+ }
+ }
+
+ fun uploadResult(molbioV2ResultRequest: MolbioV2ResultRequest) = viewModelScope.launch {
+ resultUpload.postValue(Result.Loading())
+ repository.uploadResults(molbioV2ResultRequest).let {
+ resultUpload.postValue(it)
+ }
+ }
+
+ fun checkUpdate(checkUpdateRequest: CheckUpdateRequest) = viewModelScope.launch {
+ checkUpdate.postValue(Result.Loading())
+ repository.checkUpdate(checkUpdateRequest).let {
+ checkUpdate.postValue(it)
+ }
+ }
+
+ fun deviceUpdate(deviceUpdateRequest: DeviceUpdateRequest) = viewModelScope.launch {
+ deviceUpdate.postValue(Result.Loading())
+ repository.deviceUpdate(deviceUpdateRequest).let {
+ deviceUpdate.postValue(it)
+ }
+ }
+
+ fun startPeriodicCheckUpdate() {
+ val periodicRequest = PeriodicWorkRequestBuilder(
+ repeatInterval = 1, repeatIntervalTimeUnit = TimeUnit.MINUTES
+ ).build()
+
+ workManager.enqueueUniquePeriodicWork(
+ "checkUpdateWorker", ExistingPeriodicWorkPolicy.KEEP, periodicRequest
+ )
+ }
+
+ fun uploadLogs() = viewModelScope.launch {
+ uploadLogs.postValue(Result.Loading())
+ val logFile = logFileManager.createLogFile().let { file ->
+ val requestBody = file?.asRequestBody("multipart/form-data".toMediaTypeOrNull())
+ val multipartFile =
+ requestBody?.let { MultipartBody.Part.createFormData("logFile", file?.name, it) }
+ multipartFile?.let { partFile ->
+ repository.uploadLogs(partFile).let { result ->
+ uploadLogs.postValue(result)
+ }
+ }
+ }
+ }
+
+ fun uploadHemoCubeResultToDatabaseForBufferCheck(
+ isOnline: Boolean,
+ bufferCheckData: BufferCheckData,
+ ) =
+ viewModelScope.launch {
+ if (isOnline) {
+ try {
+ when (val response =
+ repository.addTestToDatabaseforBufferCheck(bufferCheckData)) {
+ is Response.Success -> {
+ Log.i("Testdb", "Data uploaded to Firestore successfully")
+ fireBaseUpload.postValue("Success")
+ bufferCheckData.localFlag = true
+ hemoCubeBufferDao.insertAll(bufferCheckData)
+ }
+
+ is Response.Error -> {
+ Log.e("Testdb", "Error uploading data to Firestore: $response")
+ fireBaseUpload.postValue("Error")
+ bufferCheckData.localFlag = true
+ hemoCubeBufferDao.insertAll(bufferCheckData)
+ }
+
+ else -> {}
+ }
+ } catch (e: Exception) {
+ Log.e("Testdb", "Exception during data upload: ${e.message}")
+ fireBaseUpload.postValue("Error")
+ }
+ } else {
+ hemoCubeBufferDao.insertAll(bufferCheckData)
+ fireBaseUpload.postValue("Local")
+ }
+ }
+
+ fun bulkAddResultKitTestToDb(bufferCheckData: BufferCheckData) {
+ viewModelScope.launch {
+ bufferCheckData.reportUploadTime = SimpleDateFormat(
+ "yyyy-MM-dd HH:mm:ss", Locale.getDefault()
+ ).format(Calendar.getInstance().time)
+ when (repository.addTestToDatabaseforBufferCheck(bufferCheckData)) {
+ is Response.Success -> {
+ fireBaseBulkUpload.postValue("Success")
+ updateBufferLocalFlag(bufferCheckData._id)
+ }
+
+ else -> {
+ fireBaseBulkUpload.postValue("Error")
+ }
+ }
+ }
+ }
+
+ fun uploadHemoCubeResultToDatabaseforbuffercheckN(bufferCheckData: BufferCheckData) =
+ viewModelScope.launch {
+ addResultTestToDbforbuffercheck(bufferCheckData)
+ }
+
+ fun getDeviceData(deviceId: String?) = viewModelScope.launch {
+ deviceData.postValue(deviceId?.let { repository.getDeviceDataById(it) })
+ }
+
+ fun parseData() {
+ testDetails?.deviceRatio = DataHolder.hemocubeResult
+ testDetails?.resultData = DataHolder.hemoCubeTestData?.resultData.toString()
+ testDetails?.location = DataHolder.location
+ testDetails?.testTime = SimpleDateFormat(
+ "yyyy-MM-dd HH:mm:ss", Locale.getDefault()
+ ).format(Calendar.getInstance().time)
+ testDetails?.appVersion = DataHolder.hemoCubeTestData?.appVersion
+ testDetails?.deviceId = DataHolder.hemoCubeTestData?.deviceId
+ testDetails?.deviceSerialNumber =
+ sharedPreference.getString(Constants.USER_ID, "").toString()
+ testDetails?.kitSerial = sharedPreference.getString(Constants.KIT_NUMBER, "").toString()
+ testDetails?.led1Buffer = DataHolder.hemoCubeTestData?.led1Buffer
+ testDetails?.led2Buffer = DataHolder.hemoCubeTestData?.led2Buffer
+ testDetails?.led3Buffer = DataHolder.hemoCubeTestData?.led3Buffer
+ testDetails?.led4Buffer = DataHolder.hemoCubeTestData?.led4Buffer
+ testDetails?.led1Sample = DataHolder.hemoCubeTestData?.led1Sample
+ testDetails?.led2Sample = DataHolder.hemoCubeTestData?.led2Sample
+ testDetails?.led3Sample = DataHolder.hemoCubeTestData?.led3Sample
+ testDetails?.led4Sample = DataHolder.hemoCubeTestData?.led4Sample
+ testDetails?.led1Average = DataHolder.hemoCubeTestData?.led1Average
+ testDetails?.led2Average = DataHolder.hemoCubeTestData?.led2Average
+ testDetails?.led3Average = DataHolder.hemoCubeTestData?.led3Average
+ testDetails?.led4Average = DataHolder.hemoCubeTestData?.led4Average
+ testDetails?.abs1 = DataHolder.hemoCubeTestData?.abs1
+ testDetails?.abs2 = DataHolder.hemoCubeTestData?.abs2
+ testDetails?.abs3 = DataHolder.hemoCubeTestData?.abs3
+ testDetails?.abs4 = DataHolder.hemoCubeTestData?.abs4
+ testDetails?.deviceRatio = DataHolder.hemoCubeTestData?.deviceRatio
+ testDetails?.slopeRatio = DataHolder.hemoCubeTestData?.slopeRatio
+ testDetails?.predictedDenovixRatio = DataHolder.hemoCubeTestData?.predictedDenovixRatio
+ testDetails?.calculatedRatio = DataHolder.hemoCubeTestData?.calculatedRatio
+ testDetails?.coefficients = DataHolder.hemoCubeTestData?.coefficients
+ testDetails?.incubationTime = DataHolder.hemoCubeTestData?.incubationTime.toString()
+ testDetails?.name = DataHolder.hemoCubeTestData?.name.toString()
+ testDetails?.birthYear = DataHolder.hemoCubeTestData?.birthYear.toString()
+ testDetails?.userImageURL = DataHolder.hemoCubeTestData?.userImageURL.toString()
+ testDetails?.classificationResult = DataHolder.hemoCubeTestData?.classificationResult!!
+ testDetails?.prdClassification = DataHolder.hemoCubeTestData?.prdClassification.toString()
+ testDetails?.deviceRatioClass = DataHolder.hemoCubeTestData?.deviceRatioClass.toString()
+ testDetails?.slopeRatioClass = DataHolder.hemoCubeTestData?.slopeRatioClass.toString()
+ testDetails?.errorMessages = DataHolder.hemoCubeTestData?.errorMessages.toString()
+ testDetails?.batteryLevel = DataHolder.hemoCubeTestData?.batteryLevel.toString()
+ testDetails?.batteryCapacity = DataHolder.hemoCubeTestData?.batteryCapacity.toString()
+ testDetails?.batteryMaxCapacity = DataHolder.hemoCubeTestData?.batteryMaxCapacity.toString()
+ testDetails?.batteryTemperature = DataHolder.hemoCubeTestData?.batteryTemperature.toString()
+ testDetails?.batteryVoltage = DataHolder.hemoCubeTestData?.batteryVoltage.toString()
+ testDetails?.quickCapture = DataHolder.hemoCubeTestData?.quickCapture!!
+ testDetails?.solution = DataHolder.hemoCubeTestData?.solution
+ testDetails?.concentration = DataHolder.hemoCubeTestData?.concentration
+ testDetails?.volume = DataHolder.hemoCubeTestData?.volume
+ }
+
+ private fun addResultTestToDb() {
+ viewModelScope.launch {
+ try {
+ testDetails!!.reportUploadTime = SimpleDateFormat(
+ "yyyy-MM-dd HH:mm:ss", Locale.getDefault()
+ ).format(Calendar.getInstance().time)
+
+ when (val response = repository.addTestToDatabase(testDetails)) {
+ is Response.Success -> {
+ val kitCount = sharedPreference.getInt(Constants.KIT_COUNT, 0)
+ with(sharedPreference.edit()) {
+ putInt(Constants.KIT_COUNT, kitCount.plus(1))
+ apply()
+ }
+ Log.i("Testdb", "Data uploaded to Firestore successfully")
+ fireBaseUpload.postValue("Success")
+ testDetails.localFlag = true
+ uploadResult(
+ MolbioV2ResultRequest(
+ mutableListOf(
+ MolbioV2Result(
+ rawData = testDetails,
+ analysisId = testDetails._id,
+ analysisDate = testDetails.testTime,
+ analysisStatus = testDetails.classificationResult,
+ thresholds = Constants.BUFFER_INTENSITY_THRESHOLDS[testDetails.deviceId].toString(),
+ interpretation = testDetails.classificationResult,
+ testId = testDetails._id,
+ testTime = testDetails.testTime,
+ collectionTime = testDetails.testTime,
+ expiryTime = testDetails.testTime,
+ )
+ )
+ )
+ )
+ hemoCubeDao.insertAll(testDetails)
+ }
+
+ is Response.Error -> {
+ Log.e("Testdb", "Error uploading data to Firestore: $response")
+ fireBaseUpload.postValue("Error")
+ hemoCubeDao.insertAll(testDetails)
+ }
+
+ else -> {}
+ }
+ } catch (e: Exception) {
+ Log.e("Testdb", "Exception during data upload: ${e.message}")
+ fireBaseUpload.postValue("Error")
+ }
+ }
+ }
+
+ fun bulkAddResultTestToDb(userData: HemoCubeTestData) {
+ viewModelScope.launch {
+ userData.reportUploadTime = SimpleDateFormat(
+ "yyyy-MM-dd HH:mm:ss", Locale.getDefault()
+ ).format(Calendar.getInstance().time)
+ when (repository.addTestToDatabase(userData)) {
+ is Response.Success -> {
+ fireBaseBulkUpload.postValue("Success")
+ updateLocalFlag(userData._id)
+ }
+
+ else -> {
+ fireBaseBulkUpload.postValue("Error")
+ }
+ }
+ }
+ }
+
+ private fun updateLocalFlag(userId: String) = viewModelScope.launch {
+ hemoCubeDao.updateFieldById(id = userId, true)
+ }
+
+ fun updateMolbioFlag(userId: String) = viewModelScope.launch {
+ hemoCubeDao.updateMolbioFlag(id = userId, true)
+ }
+
+ fun addUser(userData: HemoCubeTestData) = viewModelScope.launch {
+ hemoCubeDao.insertAll(userData)
+ }
+
+ fun deleteById(userId: String) = viewModelScope.launch {
+ hemoCubeDao.deleteById(id = userId)
+ }
+
+
+ private fun addResultTestToDbforbuffercheck(bufferCheckData: BufferCheckData) {
+ viewModelScope.launch {
+ try {
+
+ when (val response =
+ repository.addTestToDatabaseforBufferCheck(bufferCheckData)) {
+ is Response.Success -> {
+ Log.i("Testdb", "Data uploaded to Firestore successfully")
+ fireBaseUpload.postValue("Success")
+ }
+
+ is Response.Error -> {
+ Log.e("Testdb", "Error uploading data to Firestore: $response")
+ fireBaseUpload.postValue("Error")
+ }
+ }
+ } catch (e: Exception) {
+ Log.e("Testdb", "Exception during data upload: ${e.message}")
+ fireBaseUpload.postValue("Error")
+ }
+ }
+ }
+
+ private fun updateBufferLocalFlag(bufferId: String) =
+ viewModelScope.launch {
+ hemoCubeBufferDao.updateFieldById(id = bufferId, true)
+ }
+
+ fun getLocalUserDataForCsv(context: Context): Boolean {
+ val localUserDataLiveData: LiveData> = hemoCubeDao.getAll()
+
+ // Observe the LiveData to get the actual data when available
+ localUserDataLiveData.observeForever { localUserData ->
+ localUserData?.let {
+ val csvData = mutableListOf>()
+
+ it.forEach { userData ->
+ csvData.add(
+ arrayOf(
+ userData._id,
+ userData.name,
+ userData.bloodGroup,
+ userData.birthYear,
+ userData.classificationResult,
+ userData.testTime.toString(),
+ userData.userImageURL
+ )
+ )
+ }
+
+ val csvWriter = CsvWriter(context)
+ csvWriter.writeCsv("userData.csv", csvData)
+
+ // Remove the observer to avoid leaks
+ localUserDataLiveData.removeObserver {}
+ }
+ }
+
+ return true // Assuming success, you might want to modify this based on your actual logic
+ }
+
+ fun getBatteryLevel(): Float? {
+ val batteryPct: Float? = batteryStatus?.let { intent ->
+ val level: Int =
+ intent.getIntExtra(
+ BatteryManager.EXTRA_LEVEL,
+ -1
+ )
+ val scale: Int =
+ intent.getIntExtra(
+ BatteryManager.EXTRA_SCALE,
+ -1
+ )
+ level * 100 / scale.toFloat()
+ }
+
+ return batteryPct
+ }
+
+ fun getBatteryTemperature(): Float? {
+ val batteryTemp: Float? = batteryStatus?.let { intent ->
+ val temperature = intent.getIntExtra(
+ BatteryManager.EXTRA_TEMPERATURE,
+ 0
+ )
+ temperature.toFloat() / 10
+ }
+
+ return batteryTemp
+ }
+
+ fun getBatteryVoltage(context: Context): Float {
+ val batteryIntent =
+ context.registerReceiver(
+ null,
+ IntentFilter(Intent.ACTION_BATTERY_CHANGED)
+ )
+ val voltage = batteryIntent?.getIntExtra(
+ BatteryManager.EXTRA_VOLTAGE,
+ 0
+ ) ?: 0
+
+ // milli-volts to volts
+ return voltage.toFloat() / 1000
+ }
+
+
+ fun getBatteryCapacity(context: Context): Int {
+ val batteryManager =
+ context.getSystemService(Context.BATTERY_SERVICE) as BatteryManager
+ val currentCapacity =
+ batteryManager.getIntProperty(BatteryManager.BATTERY_PROPERTY_CHARGE_COUNTER)
+
+ return currentCapacity
+ }
+
+ fun getBatteryMaxCapacity(context: Context): Float {
+ val batteryManager = context.getSystemService(Context.BATTERY_SERVICE) as BatteryManager
+ val designCapacity =
+ batteryManager.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY)
+ val currentCapacity =
+ batteryManager.getIntProperty(BatteryManager.BATTERY_PROPERTY_CHARGE_COUNTER)
+
+ // Calculate the estimated maximum battery capacity in mAh
+ val maxCapacity = currentCapacity.toFloat() / designCapacity.toFloat() * 100
+
+ return maxCapacity
+ }
+
+ fun createCSV(hemoCubeTestData: List, appContext: Context) =
+ viewModelScope.launch {
+ val fileName = "HPOS${getCurrentDate()}.csv"
+ if (localFileDataSource.exportDataToCSV(fileName, hemoCubeTestData)) {
+ hemoCubeTestData.forEach { data ->
+ data.localFlag = true
+ hemoCubeDao.updateCSVFieldById(
+ data._id,
+ true
+ )
+ }
+ }
+ }
+
+ fun getCurrentDate(): String {
+ return SimpleDateFormat(
+ "yyyy-MM-dd HH:mm:ss", Locale.getDefault()
+ ).format(Calendar.getInstance().time)
+ }
+}
diff --git a/app/src/main/res/layout/activity_trueheme.xml b/app/src/main/res/layout/activity_trueheme.xml
new file mode 100644
index 0000000..f959890
--- /dev/null
+++ b/app/src/main/res/layout/activity_trueheme.xml
@@ -0,0 +1,35 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/fragment_trueheme_sample.xml b/app/src/main/res/layout/fragment_trueheme_sample.xml
new file mode 100644
index 0000000..7966a1a
--- /dev/null
+++ b/app/src/main/res/layout/fragment_trueheme_sample.xml
@@ -0,0 +1,264 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+