diff --git a/.idea/misc.xml b/.idea/misc.xml deleted file mode 100644 index e533f05..0000000 --- a/.idea/misc.xml +++ /dev/null @@ -1,43 +0,0 @@ - - - - - - - - - - - \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index 295b626..55ec74c 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -18,8 +18,8 @@ android { applicationId "in.sminnovations.hpostesting" minSdk 21 targetSdk 34 - versionCode 10 - versionName "2.1.10" + versionCode 24 + versionName "2.1.24" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } @@ -60,6 +60,7 @@ dependencies { //firebase implementation platform('com.google.firebase:firebase-bom:32.1.0') + implementation("com.google.firebase:firebase-perf-ktx") implementation("com.google.firebase:firebase-crashlytics-ktx") implementation("com.google.firebase:firebase-analytics-ktx") implementation 'com.google.firebase:firebase-firestore-ktx' @@ -96,17 +97,17 @@ dependencies { implementation 'com.google.android.gms:play-services-code-scanner:16.1.0' //Room - implementation "androidx.room:room-ktx:2.6.0-beta01" - implementation "androidx.room:room-runtime:2.6.0-beta01" - kapt ("androidx.room:room-compiler:2.6.0-beta01") + implementation "androidx.room:room-ktx:2.6.0-rc01" + implementation "androidx.room:room-runtime:2.6.0-rc01" + kapt ("androidx.room:room-compiler:2.6.0-rc01") //image implementation 'com.github.bumptech.glide:glide:4.13.2' annotationProcessor 'com.github.bumptech.glide:compiler:4.13.2' // Navigation Component - implementation "androidx.navigation:navigation-fragment-ktx:2.7.2" - implementation "androidx.navigation:navigation-ui-ktx:2.7.2" + implementation "androidx.navigation:navigation-fragment-ktx:2.7.3" + implementation "androidx.navigation:navigation-ui-ktx:2.7.3" //Dagger - Hilt implementation "com.google.dagger:hilt-android:2.46" diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 11d8e5e..98c18c5 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -13,8 +13,11 @@ + + + + + { return try { val userdata = db.collection("patientData").whereEqualTo("_id", data!!._id).get().await() @@ -52,13 +47,30 @@ class DatabaseRepository : Repository { db.collection("testData").add(data!!).await() Response.Success(data!!._id) } catch (e: Exception) { + Firebase.crashlytics.recordException(e) Response.Error(e) } } + suspend fun addTestToDatabaseforBufferCheck(data: BufferCheckData?): Response { + return try { + db.collection("buffers").add(data!!).await() + Response.Success(data!!.kitno) + } catch (e: Exception) { + Firebase.crashlytics.recordException(e) + Response.Error(e) + } + } - - + suspend fun addDiagnostics(data: DiagnosticsData?): Response { + return try { + db.collection("diagnostics").add(data!!).await() + Response.Success(data!!.deviceId) + } catch (e: Exception) { + Firebase.crashlytics.recordException(e) + Response.Error(e) + } + } suspend fun uploadFileToStorage(patientID: String, filePath: String): Response { try { @@ -67,9 +79,8 @@ class DatabaseRepository : Repository { val riversRef = storage.reference.child("$patientID/${file.lastPathSegment}") riversRef.putFile(file).await() return Response.Success(true) - } catch (e: Exception) { - e.printStackTrace() + Firebase.crashlytics.recordException(e) return Response.Error(e) } } @@ -81,7 +92,7 @@ class DatabaseRepository : Repository { Response.Success(true) } catch (e: Exception) { - e.printStackTrace() + Firebase.crashlytics.recordException(e) Response.Error(e) } } @@ -100,7 +111,7 @@ class DatabaseRepository : Repository { pendingList } catch (e: Exception) { - e.printStackTrace() + Firebase.crashlytics.recordException(e) pendingList } } @@ -111,7 +122,7 @@ class DatabaseRepository : Repository { Response.Success(true) } catch (e: Exception) { - e.printStackTrace() + Firebase.crashlytics.recordException(e) Response.Error(e) } } @@ -120,7 +131,6 @@ class DatabaseRepository : Repository { return db.collection("devices").get().await().toObjects(DeviceData::class.java) } - suspend fun getDeviceDataById(deviceId: String): DeviceData? { val querySnapshot = db.collection("devices").get().await() val allDeviceDataList = querySnapshot.toObjects(DeviceData::class.java) @@ -128,7 +138,4 @@ class DatabaseRepository : Repository { // Find the DeviceData object with the specified deviceId return allDeviceDataList.find { it.deviceId == deviceId } } - - - } \ No newline at end of file diff --git a/app/src/main/java/com/example/hpostesting/presentation/KitScanActivity.kt b/app/src/main/java/com/example/hpostesting/presentation/KitScanActivity.kt index 9edb703..53f8fd3 100644 --- a/app/src/main/java/com/example/hpostesting/presentation/KitScanActivity.kt +++ b/app/src/main/java/com/example/hpostesting/presentation/KitScanActivity.kt @@ -11,6 +11,7 @@ import androidx.appcompat.app.AppCompatActivity import com.example.hpostesting.data.DataHolder import com.example.hpostesting.data.constant.Constants import com.example.hpostesting.presentation.dashboard.DashboardActivity +import com.example.hpostesting.presentation.diagnostics.DiagnosticsActivity import com.example.hpostesting.presentation.hemocube.HemocubeActivity import com.example.hpostesting.presentation.testRight.TestRightActivity import com.google.android.material.snackbar.Snackbar @@ -108,7 +109,33 @@ class KitScanActivity : AppCompatActivity() { binding.btnGo.setOnClickListener { val serialNumber = binding.nameEditText.text.toString().trim() - if (serialNumber.isNotEmpty() && checkDataNotNull() && isSerialValid(serialNumber)) { + if (serialNumber.isNotEmpty() && isSerialValid(serialNumber)) { + if (DataHolder.selectedTest == null) { + with(sharedPreference.edit()) { + putString( + Constants.KIT_NUMBER, binding.nameEditText.text.toString() + ) + putInt(Constants.KIT_COUNT, 1) + apply() + } + Toast.makeText( + applicationContext, + "Kit number is updated, Please Select user before starting test", + Toast.LENGTH_SHORT + ).show() + + val i = Intent(applicationContext, DashboardActivity::class.java) + startActivity(i) + finish() + } + } else { + Toast.makeText(this, "Invalid KIT Number", Toast.LENGTH_LONG).show() + return@setOnClickListener + } + if (serialNumber.isNotEmpty() && checkDataNotNull() && isSerialValid( + serialNumber + ) + ) { DataHolder.kitSerial = binding.nameEditText.text.toString() DataHolder.selectedTest?.kitSerial = binding.nameEditText.text.toString() with(sharedPreference.edit()) { @@ -125,8 +152,9 @@ class KitScanActivity : AppCompatActivity() { private fun checkHemoCubeKitData(): Boolean { return sharedPreference.getString(Constants.KIT_NUMBER, "") - ?.isNotBlank() == true && sharedPreference.getInt(Constants.KIT_COUNT, 0) > 0 - && sharedPreference.getInt(Constants.KIT_COUNT, 0) < 35 + ?.isNotBlank() == true && sharedPreference.getInt( + Constants.KIT_COUNT, 0 + ) > 0 && sharedPreference.getInt(Constants.KIT_COUNT, 0) < 35 } private fun isSerialValid(s: String): Boolean { 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 d578725..dd5d0f6 100644 --- a/app/src/main/java/com/example/hpostesting/presentation/MainActivity.kt +++ b/app/src/main/java/com/example/hpostesting/presentation/MainActivity.kt @@ -90,18 +90,6 @@ class MainActivity : AppCompatActivity() { startActivity(Intent(this, DashboardActivity::class.java)) finish() } - - -// val crashButton = Button(this) -// crashButton.text = "Test Crash" -// crashButton.setOnClickListener { -// throw RuntimeException("Test Crash") // Force a crash -// } -// -// addContentView(crashButton, ViewGroup.LayoutParams( -// ViewGroup.LayoutParams.MATCH_PARENT, -// ViewGroup.LayoutParams.WRAP_CONTENT)) - } private fun checkAndUpdateUsbConnection() { diff --git a/app/src/main/java/com/example/hpostesting/presentation/adapter/UserListAdapter.kt b/app/src/main/java/com/example/hpostesting/presentation/adapter/UserListAdapter.kt index 33da679..cd52b4d 100644 --- a/app/src/main/java/com/example/hpostesting/presentation/adapter/UserListAdapter.kt +++ b/app/src/main/java/com/example/hpostesting/presentation/adapter/UserListAdapter.kt @@ -1,6 +1,10 @@ package com.example.hpostesting.presentation.adapter import android.annotation.SuppressLint +import android.app.AlertDialog +import android.content.Context +import android.content.Context.INPUT_METHOD_SERVICE +import android.view.inputmethod.InputMethodManager import android.view.LayoutInflater import android.view.View import android.view.ViewGroup @@ -10,17 +14,30 @@ import androidx.recyclerview.widget.RecyclerView import com.bumptech.glide.Glide import com.example.hpostesting.data.DataHolder import com.example.hpostesting.data.model.patient.UserData -import com.example.hpostesting.presentation.testRight.TestRightViewModel import com.firebase.ui.firestore.FirestoreRecyclerAdapter import com.firebase.ui.firestore.FirestoreRecyclerOptions import `in`.sminnovations.hpostesting.R import `in`.sminnovations.hpostesting.databinding.UserItemViewBinding +import java.text.SimpleDateFormat +import java.util.Calendar +import java.util.Date +import java.util.Locale +import android.widget.AutoCompleteTextView +import androidx.core.content.ContextCompat.getSystemService +import androidx.core.content.getSystemService +import androidx.fragment.app.FragmentActivity +import com.example.hpostesting.presentation.hemocube.HemoCubeViewModel +import com.google.firebase.firestore.FirebaseFirestore class UserListAdapter( + + private val context: Context, + private val hemoCubeViewModel: HemoCubeViewModel, options: FirestoreRecyclerOptions, - private val view: View -) : - FirestoreRecyclerAdapter(options) { + private val view: View, + private val batLevel: Int, + private val requireActivity: FragmentActivity +) : FirestoreRecyclerAdapter(options) { class OrderItemViewHolder(val binding: UserItemViewBinding) : RecyclerView.ViewHolder(binding.root) @@ -36,6 +53,21 @@ class UserListAdapter( holder.binding.apply { userName.text = "Name: ${model.name}" userId.text = "User ID:${model._id}" + if (model.incubationTime == "") { + btnBlood.visibility = View.VISIBLE + } else { + userId.text = + "User ID:${model._id} \n Time: ${isBetween15And30Minutes(model.incubationTime)} \n Started at: ${ + SimpleDateFormat("HH:mm:ss").format( + SimpleDateFormat( + "yyyy-MM-dd HH:mm:ss", Locale.getDefault() + ).parse(model.incubationTime)!! + ) + }" + btnBlood.visibility = View.GONE + } + + Glide.with(view).load(model.userImageURL).into(userImage) if (model.testStatus!!) { teststatus.text = "Test concluded" @@ -43,7 +75,129 @@ class UserListAdapter( } else { teststatus.text = "Test is Pending" } + + holder.binding.btnBlood.setOnClickListener { + val view: View? = requireActivity.currentFocus + + // on below line checking if view is not null. + if (view != null) { + // on below line we are creating a variable + // for input manager and initializing it. + val inputMethodManager = + context.getSystemService(INPUT_METHOD_SERVICE) as InputMethodManager + + // on below line hiding our keyboard. + inputMethodManager.hideSoftInputFromWindow(view.windowToken, 0) + } + val inflater = LayoutInflater.from(context) + val dialogView = inflater.inflate(R.layout.dialog_custom, null) + + val builder = AlertDialog.Builder(context) + .setView(dialogView) + .setTitle("Add Blood Group") + + val etBloodGroup = dialogView.findViewById(R.id.et_blood_group) + + builder.setPositiveButton("OK") { dialog, which -> + val bloodGroup = etBloodGroup.text.toString() + if (bloodGroup.equals("Select Blood Group") || bloodGroup.isNullOrBlank()) { + etBloodGroup.error = "Please enter your blood group" + Toast.makeText( + context, + "Please enter your blood group", + Toast.LENGTH_SHORT + ).show() + } else { + // Check if hemoCubeViewModel and testDetails are not null + val db: FirebaseFirestore = FirebaseFirestore.getInstance() + + db.collection("patientData") + .whereEqualTo("_id", model._id) + .get() + .addOnSuccessListener { userdata -> + try { + val document = userdata.documents[0] + db.collection("patientData") + .document(document.id) + .update("bloodGroup", bloodGroup, + "incubationTime", SimpleDateFormat( + "yyyy-MM-dd HH:mm:ss", Locale.getDefault() + ).format(Calendar.getInstance().time).toString() + ) + .addOnSuccessListener { + Toast.makeText( + context, + "Blood group updated successfully", + Toast.LENGTH_SHORT + ).show() + btnBlood.visibility = View.GONE + } + .addOnFailureListener { e -> + Toast.makeText( + context, + "Failed to update blood group: ${e.message}", + Toast.LENGTH_SHORT + ).show() + } + } catch (e: IndexOutOfBoundsException) { + // Handle the case where no documents are found for the specified ID + Toast.makeText( + context, + "No user data found for the specified ID", + Toast.LENGTH_SHORT + ).show() + } + + } + .addOnFailureListener { e -> + Toast.makeText( + context, + "Error fetching user data: ${e.message}", + Toast.LENGTH_SHORT + ).show() + } + } + // Handle the selected blood group here + } + + builder.setNegativeButton("Cancel") { dialog, which -> + dialog.dismiss() + } + + val alertDialog = builder.create() + alertDialog.show() + } + + btnStartIncubation.setOnClickListener { +// it.visibility = View.GONE +// Firebase.firestore.collection("patientData").whereEqualTo("_id", model._id).get() +// .addOnSuccessListener { data -> +// if (data.documents.isNotEmpty()) { +// data.documents.forEach { userData -> +// Firebase.firestore.collection("patientData").document(userData.id) +// .update( +// "incubationTime", SimpleDateFormat( +// "yyyy-MM-dd HH:mm:ss", Locale.getDefault() +// ).format(Calendar.getInstance().time).toString() +// ).addOnSuccessListener { +// Toast.makeText( +// view.context, "Incubation started", Toast.LENGTH_SHORT +// ).show() +// startListening() +// } +// } +// } +// } + } userCard.setOnClickListener { + if (batLevel < 40) { + Toast.makeText( + view.context, + "Battery level is low than 40%, please charge the device to continue testing", + Toast.LENGTH_SHORT + ).show() + return@setOnClickListener + } if (model.testStatus != null) { if (model.testStatus!!) { Toast.makeText( @@ -52,11 +206,47 @@ class UserListAdapter( Toast.LENGTH_SHORT ).show() } else { - DataHolder.selectedTest = model - view.findNavController().navigate(R.id.action_nav_home_to_mainActivity) + if (model.incubationTime != "") { + if (isBetween15And30Minutes(model.incubationTime) < 15) { + Toast.makeText( + view.context, + "Incubation has not completed 15 minutes", + Toast.LENGTH_SHORT + ).show() + } else if (isBetween15And30Minutes(model.incubationTime) > 30) { + Toast.makeText( + view.context, + "Incubation crossed 30 minutes, need to repeat the incubation", + Toast.LENGTH_SHORT + ).show() + } else { + DataHolder.selectedTest = model + view.findNavController() + .navigate(R.id.action_nav_home_to_mainActivity) + } + } else { + Toast.makeText( + view.context, + "Incubation not started for this user", + Toast.LENGTH_SHORT + ).show() + } } } } } } + + + + private fun isBetween15And30Minutes(createdAt: String): Long { + val formatter = SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault()) + val createdAtDate: Date = formatter.parse(createdAt)!! + + val currentTime = Calendar.getInstance().time + + val diffMillis = currentTime.time - createdAtDate.time + + return diffMillis / (60 * 1000) + } } \ No newline at end of file diff --git a/app/src/main/java/com/example/hpostesting/presentation/buffercheck/HemoCubeBufferCheckFragment.kt b/app/src/main/java/com/example/hpostesting/presentation/buffercheck/HemoCubeBufferCheckFragment.kt new file mode 100644 index 0000000..deb77e1 --- /dev/null +++ b/app/src/main/java/com/example/hpostesting/presentation/buffercheck/HemoCubeBufferCheckFragment.kt @@ -0,0 +1,354 @@ +package com.example.hpostesting.presentation.buffercheck + +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.view.inputmethod.InputMethodManager +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.constant.Constants +import com.example.hpostesting.data.constant.HemoCubeCommands +import com.example.hpostesting.data.model.patient.BufferCheckData +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.HemoCubeViewModel +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.FragmentHemoCubeReferenceBinding +import kotlin.math.log10 +import java.text.SimpleDateFormat +import java.util.Calendar +import java.util.Locale + +class HemoCubeBufferCheckFragment : Fragment() { + + private lateinit var binding: FragmentHemoCubeReferenceBinding + private val hemoCubeViewModel: HemoCubeViewModel by activityViewModels() + private lateinit var sharedPreferences: SharedPreferences + private var currentDeviceData: DeviceData? = null + private var resultData: String = "" + private var kitno: String = "" + private var isOnline = false + private val messages = MutableLiveData() + private var isTestOngoing = false + private var startListening = MutableLiveData(false) + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?, + ): View { + binding = FragmentHemoCubeReferenceBinding.inflate(inflater, container, false) + sharedPreferences = + requireContext().getSharedPreferences("PREFERENCE_NAME", Context.MODE_PRIVATE) + return binding.root + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + initViews() + observeViewModel() + } + + private fun initViews() { + + binding.nameEditText.setText("SMI/SC/") + binding.tvTitle.visibility = View.GONE + binding.tvName.visibility = View.GONE + + binding.btnGo.setOnClickListener { + val serialNumber = binding.nameEditText.text.toString().trim() + if (serialNumber.isNotEmpty() && isSerialValid(serialNumber)) { + kitno = serialNumber + listenToHemoCube() + startBufferProcess() + } else { + Toast.makeText(context, "Invalid KIT Number", Toast.LENGTH_LONG).show() + return@setOnClickListener + } + activity?.runOnUiThread { + binding.tvSubtitle2.visibility = View.GONE + binding.etAbhaId.visibility = View.GONE + binding.tvTitle.visibility = View.GONE + binding.tvTitle2.visibility = View.GONE + binding.btnGo.visibility = View.GONE + } + } + + + + activity?.runOnUiThread { + binding.btnSubmit.isEnabled = true + binding.btnSubmit.isClickable = true + } + binding.btnSubmit.setOnClickListener { + + } + binding.btnSubmit.isEnabled = false + binding.btnSubmit.isClickable = false + } + + private fun observeViewModel() { + hemoCubeViewModel.deviceData.observe(viewLifecycleOwner) { + currentDeviceData = it + } + + hemoCubeViewModel.networkStatusLiveData.observe(viewLifecycleOwner) { isNetworkAvailable -> + if (isNetworkAvailable) { + hemoCubeViewModel.getDeviceData(sharedPreferences.getString(Constants.USER_ID, "")) + } else { + Toast.makeText( + requireContext(), "No internet connection avaiable", Toast.LENGTH_SHORT + ).show() + } + isOnline = isNetworkAvailable + } + hemoCubeViewModel.fireBaseUpload.observe(viewLifecycleOwner) { result -> + if (result == "Success") { + showToast("Kit result uploaded successfully") + } + if (result == "Local") { + showToast("Kit result uploading failed, note it down manually") + } + + binding.progressBar.visibility = View.GONE + } + + messages.observe(viewLifecycleOwner) { + binding.tvSubtitle4.text = it + } + } + + private fun listenToHemoCube() { + if (DataHolder.hemoCubeTestData == null) { + DataHolder.hemoCubeTestData = DataHolder.selectedTest?.toHemoCubeTestData() + } + + val fullReadOutput = StringBuilder() + startListening.postValue(true) + + try { + (activity as HemocubeBufferCheckActivity).mService.listenToHemoCube(object : + UsbServiceListener { + override fun onUsbRead(data: ByteArray?) { + data?.let { + val stringData = String(it) + fullReadOutput.append(stringData) + handleUsbData(stringData, fullReadOutput) + } + } + + override fun onUsbError(e: Exception?) { + } + }) + } catch (e: Exception) { + showToast("test is going on") + Firebase.crashlytics.recordException(e) + } + } + + private fun handleUsbData(stringData: String, fullReadOutput: StringBuilder) { + if (stringData.contains("#")) { + messages.postValue(stringData) + isTestOngoing = true + } + + resultData += fullReadOutput.toString() + + when { + stringData.contains("#Buffer Completed") -> showStartSampleDialog() + stringData.contains("#Sample Completed") -> { + activity?.runOnUiThread { + binding.btnSubmit.isEnabled = true + binding.btnSubmit.isClickable = true + } + fetchResult() + } + + stringData.contains("RESULT") || resultData.contains("REND") -> { + var validString: String + val results: List + if (stringData.contains("RESULT")) { + validString = isValidResult(stringData) + if (validString.isEmpty()) { + results = resultData.split("\n") + validString = parseResult(results) + } + } else { + results = resultData.split("\n") + validString = parseResult(results) + } + if (validString.isNotEmpty()) { + handleValidResult(validString) + } + } + } + } + + private fun showStartSampleDialog() { + activity?.runOnUiThread { + UIUtils.createAlertDialog(requireContext(), + "Start Sample", + "Do you want to start sample reading?", + getString(R.string.no), + "Yes", + object : MyDialogListener { + override fun onClickNegativeButton() {} + + override fun onClickPositiveButton() { + listenToHemoCube() + startSampleProcess() + } + }) + } + } + + private fun handleValidResult(validString: String) { + try { + val result = validString.split(" ") + if (result.size == 8) { + val deviceId = result[2] + val led1BufferForDevice = result[3].toDoubleOrNull() + val led2BufferForDevice = result[4].toDoubleOrNull() + val led1Sample = result[5].toDoubleOrNull() + val led2Sample = result[6].toDoubleOrNull() + val led1Average = log10(led1BufferForDevice?.div(led1Sample!!) ?: 0.0) + val led2Average = log10(led2BufferForDevice?.div(led2Sample!!) ?: 0.0) + val deviceRatio = led1Average / led2Average + val calculateRatio = calculateRatio(deviceRatio) + val classificationResult = findResult(calculateRatio) + messages.postValue(classificationResult) + val bufferData = BufferCheckData( + deviceId= deviceId, + kitno = kitno, + led1Buffer = led1BufferForDevice, + led2Buffer = led2BufferForDevice, + led1Average = led1Average, + led2Average = led2Average, + led1Sample = led1Sample, + led2Sample = led2Sample, + deviceRatio = deviceRatio, + calculatedRatio = calculateRatio, + coefficients = currentDeviceData?.coefficients?.get(0).toString() + ", " + currentDeviceData?.coefficients?.get(1).toString(), + classificationResult = classificationResult, + deviceSerialNumber = sharedPreferences.getString(Constants.USER_ID, "").toString(), + testTime = SimpleDateFormat( + "yyyy-MM-dd HH:mm:ss", Locale.getDefault() + ).format(Calendar.getInstance().time) + ) + hemoCubeViewModel.uploadHemoCubeResultToDatabaseforbuffercheckN(bufferData) + } + } catch (e: Exception) { + Toast.makeText( + requireContext(), "Error while processing device data", Toast.LENGTH_SHORT + ).show() + Firebase.crashlytics.recordException(e) + } + } + + private fun findResult(calculatedRatio: Double?): String { + try { + if (calculatedRatio != null) { + if (calculatedRatio < 0.085) return "Kit Failed" + if (calculatedRatio in 0.085..0.155) return "Kit Passed" + if (calculatedRatio in 0.155..0.175) return "Kit Failed" + if (calculatedRatio in 0.175..0.22) return "Kit Failed" + if (calculatedRatio in 0.22..0.25) return "Kit Failed" + if (calculatedRatio in 0.25..0.35) return "Kit Failed" + if (calculatedRatio > 0.35) return "Kit Failed" + } else { + return "NULL" + } + } catch (e: Exception) { + showToast("error while performing classification") + Firebase.crashlytics.recordException(e) + return "ERROR" + } + return "NULL" + } + + private fun showToast(message: String) { + Toast.makeText(requireContext(), message, Toast.LENGTH_SHORT).show() + } + + private fun startBufferProcess() { + + (activity as HemocubeBufferCheckActivity).mService.sendAndListenToHemoCube(HemoCubeCommands.startBuffer, + object : UsbServiceListener { + override fun onUsbRead(data: ByteArray?) {} + + override fun onUsbError(e: Exception?) { + } + }) + } + + private fun startSampleProcess() { + + (activity as HemocubeBufferCheckActivity).mService.sendAndListenToHemoCube(HemoCubeCommands.startSample, + object : UsbServiceListener { + override fun onUsbRead(data: ByteArray?) {} + + override fun onUsbError(e: Exception?) { + } + }) + } + + private fun fetchResult() { + + (activity as HemocubeBufferCheckActivity).mService.sendAndListenToHemoCube(HemoCubeCommands.getSample, + object : UsbServiceListener { + override fun onUsbRead(data: ByteArray?) {} + + override fun onUsbError(e: Exception?) { + } + }) + } + + private fun calculateRatio(ratio: Double): Double { + val coefficient1 = currentDeviceData?.coefficients?.get(0) ?: 0.0 + val coefficient2 = currentDeviceData?.coefficients?.get(1) ?: 1.0 + return coefficient1 * ratio + coefficient2 + } + + private fun parseResult(frames: List): String { + val lines = mutableListOf() + for (frame in frames.reversed()) { + if (frame.contains("REND") || frame.contains("RESULT")) lines += frame + if (frame.contains("RESULT")) { + break + } + } + val line = lines.reversed().joinToString("").trim() + if (line.contains("RESULT") && line.split(" ").size == 8) { + return line + } + return "" + } + + private fun isSerialValid(s: String): Boolean { + if (s.length != 17) { + binding.nameEditText.error = getString(R.string.invalid_kit) + return false + } + return true + } + + + private fun isValidResult(line: String): String { + return if (line.contains("RESULT") && line.split(" ").size == 8) { + line + } else { + "" + } + } +} diff --git a/app/src/main/java/com/example/hpostesting/presentation/buffercheck/HemocubeBufferCheckActivity.kt b/app/src/main/java/com/example/hpostesting/presentation/buffercheck/HemocubeBufferCheckActivity.kt new file mode 100644 index 0000000..6375d39 --- /dev/null +++ b/app/src/main/java/com/example/hpostesting/presentation/buffercheck/HemocubeBufferCheckActivity.kt @@ -0,0 +1,164 @@ +package com.example.hpostesting.presentation.buffercheck + +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.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 +open class HemocubeBufferCheckActivity : 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 { + 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 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()) { + Toast.makeText(this, "No device found", Toast.LENGTH_SHORT).show() + } else { + mDriver = availableDrivers[0] + mConnection = manager.openDevice(mDriver.device) + + if (mConnection == null) { + requestUserPermission(manager, mDriver.device) + } else { + setupService() + } + } + } + + + private fun requestUserPermission(manager: UsbManager, device: UsbDevice) { + val mPendingIntent: PendingIntent = if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.S) { + PendingIntent.getBroadcast( + this, 0, Intent(Constants.HOMOCUBE_USB_PERMISSION), PendingIntent.FLAG_MUTABLE + ) + } else { + 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) + } + + private fun setupService() { + val intent = Intent(this, UsbService::class.java) + bindService(intent, connection, Context.BIND_AUTO_CREATE) + } + + private fun moveToNext() { + if (supportFragmentManager.isDestroyed) return + + supportFragmentManager.beginTransaction().replace(binding.fghemocube.id, HemoCubeBufferCheckFragment()) + .commit() + } + + 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/dashboard/DashboardActivity.kt b/app/src/main/java/com/example/hpostesting/presentation/dashboard/DashboardActivity.kt index d557dec..02b1df6 100644 --- a/app/src/main/java/com/example/hpostesting/presentation/dashboard/DashboardActivity.kt +++ b/app/src/main/java/com/example/hpostesting/presentation/dashboard/DashboardActivity.kt @@ -11,6 +11,10 @@ import androidx.navigation.ui.navigateUp import androidx.navigation.ui.setupActionBarWithNavController import androidx.navigation.ui.setupWithNavController import com.google.android.material.navigation.NavigationView +import com.google.firebase.appdistribution.FirebaseAppDistribution +import com.google.firebase.appdistribution.FirebaseAppDistributionException +import com.google.firebase.crashlytics.FirebaseCrashlytics +import com.google.firebase.crashlytics.ktx.crashlytics //import com.google.firebase.appdistribution.FirebaseAppDistribution //import com.google.firebase.appdistribution.FirebaseAppDistributionException import dagger.hilt.android.AndroidEntryPoint @@ -58,28 +62,41 @@ class DashboardActivity : AppCompatActivity() { override fun onResume() { super.onResume() - // Copy and paste this into any part of your app - for example, in your main -// activity's onResume method. -// val firebaseAppDistribution = FirebaseAppDistribution.getInstance() -// firebaseAppDistribution.updateIfNewReleaseAvailable() -// .addOnProgressListener { updateProgress -> -// Toast.makeText(this, "Updating", Toast.LENGTH_SHORT).show() -// } -// .addOnFailureListener { e -> -// // (Optional) Handle errors. -// if (e is FirebaseAppDistributionException) { -// when (e.errorCode) { -// FirebaseAppDistributionException.Status.NOT_IMPLEMENTED -> { -// // SDK did nothing. This is expected when building for Play. -// } -// -// else -> { -// // Handle other errors. -// } -// } -// } -// } + + val firebaseAppDistribution = FirebaseAppDistribution.getInstance() + firebaseAppDistribution.updateIfNewReleaseAvailable() + .addOnProgressListener { updateProgress -> + + if (updateProgress.apkBytesDownloaded > 0) { + Toast.makeText( + this, + "${updateProgress.updateStatus}. ${updateProgress.apkBytesDownloaded / 1048576} /${updateProgress.apkFileTotalBytes / 1048576} MB", + Toast.LENGTH_SHORT + ).show() + } + } + .addOnFailureListener { e -> + // (Optional) Handle errors. + if (e is FirebaseAppDistributionException) { + when (e.errorCode) { + FirebaseAppDistributionException.Status.NOT_IMPLEMENTED -> { + // SDK did nothing. This is expected when building for Play. + } + + else -> { + // Handle other errors. + Toast.makeText( + this, + "AppDistribution error, status: ${e.errorCode}", + Toast.LENGTH_SHORT + ).show() + } + } + FirebaseCrashlytics.getInstance().recordException(e) + } + } + .addOnSuccessListener { +// Toast.makeText(this, "App Update: Success!", Toast.LENGTH_SHORT).show() + } } - - } \ No newline at end of file diff --git a/app/src/main/java/com/example/hpostesting/presentation/dashboard/GalleryFragment.kt b/app/src/main/java/com/example/hpostesting/presentation/dashboard/GalleryFragment.kt index 1911ca0..7234417 100644 --- a/app/src/main/java/com/example/hpostesting/presentation/dashboard/GalleryFragment.kt +++ b/app/src/main/java/com/example/hpostesting/presentation/dashboard/GalleryFragment.kt @@ -1,12 +1,13 @@ package com.example.hpostesting.presentation.dashboard +import android.content.Intent import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.fragment.app.Fragment -import androidx.lifecycle.ViewModelProvider -//import com.example.hpostesting.presentation.dashboard.ui.gallery.GalleryViewModel +import com.example.hpostesting.presentation.buffercheck.HemocubeBufferCheckActivity +import com.example.hpostesting.presentation.diagnostics.DiagnosticsActivity import `in`.sminnovations.hpostesting.databinding.FragmentGalleryBinding class GalleryFragment : Fragment() { @@ -32,6 +33,15 @@ class GalleryFragment : Fragment() { // galleryViewModel.text.observe(viewLifecycleOwner) { //// textView.text = it // } + binding.btnSubmit.setOnClickListener { + startActivity(Intent(requireContext(), HemocubeBufferCheckActivity::class.java)) + } + + binding.btnDiagnostics.setOnClickListener { + startActivity(Intent(requireContext(), DiagnosticsActivity::class.java)) + } + + return root } diff --git a/app/src/main/java/com/example/hpostesting/presentation/dashboard/HomeFragment.kt b/app/src/main/java/com/example/hpostesting/presentation/dashboard/HomeFragment.kt index 689e52b..1a6995f 100644 --- a/app/src/main/java/com/example/hpostesting/presentation/dashboard/HomeFragment.kt +++ b/app/src/main/java/com/example/hpostesting/presentation/dashboard/HomeFragment.kt @@ -2,8 +2,11 @@ package com.example.hpostesting.presentation.dashboard import android.app.AlertDialog import android.content.Context +import android.content.Context.BATTERY_SERVICE import android.content.DialogInterface +import android.content.Intent import android.content.SharedPreferences +import android.os.BatteryManager import android.os.Bundle import android.view.LayoutInflater import android.view.View @@ -16,16 +19,21 @@ import com.example.hpostesting.data.DataHolder import com.example.hpostesting.data.constant.Constants import com.example.hpostesting.data.model.patient.HemoCubeTestData import com.example.hpostesting.data.model.patient.UserData +import com.example.hpostesting.presentation.KitScanActivity import com.example.hpostesting.presentation.adapter.UserListAdapter import com.example.hpostesting.presentation.hemocube.HemoCubeViewModel import com.example.hpostesting.presentation.testRight.TestRightViewModel import com.firebase.ui.firestore.FirestoreRecyclerOptions +import com.google.firebase.crashlytics.ktx.crashlytics import com.google.firebase.firestore.Query import com.google.firebase.firestore.ktx.firestore import com.google.firebase.ktx.Firebase +import com.google.firebase.perf.ktx.performance import dagger.hilt.android.AndroidEntryPoint import `in`.sminnovations.hpostesting.R import `in`.sminnovations.hpostesting.databinding.FragmentHomeBinding +import java.text.SimpleDateFormat +import java.util.Date import java.util.Locale @AndroidEntryPoint @@ -35,6 +43,9 @@ class HomeFragment : Fragment() { private val viewModel: TestRightViewModel by activityViewModels() private val hemoCubeViewModel: HemoCubeViewModel by activityViewModels() private lateinit var rvAdapter: UserListAdapter + private var batLevel: Int = 0 // Initialize with a default value, or obtain the actual battery level + + private lateinit var sharedPreference: SharedPreferences override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? @@ -48,17 +59,16 @@ class HomeFragment : Fragment() { return super.onCreateView(inflater, container, savedInstanceState) } - sharedPreference = requireContext().getSharedPreferences("PREFERENCE_NAME", Context.MODE_PRIVATE) + sharedPreference = + requireContext().getSharedPreferences("PREFERENCE_NAME", Context.MODE_PRIVATE) DataHolder.selectedTest = null return binding.root } + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) viewModel.allUserData.observe(viewLifecycleOwner) { userData -> - if (userData.isNotEmpty()) { - - } deleteIncompleteRegistrations(userData) } hemoCubeViewModel.allUserData.observe(viewLifecycleOwner) { userData -> @@ -77,13 +87,36 @@ class HomeFragment : Fragment() { setUserId() } } + hemoCubeViewModel.fireBaseBulkUpload.observe(viewLifecycleOwner) { result -> + if (result == "Success") { + Toast.makeText( + requireContext(), + "Test Results Uploaded Successfully", + Toast.LENGTH_SHORT + ).show() + } + if (result == "Error") { + Toast.makeText(requireContext(), "Test Results Upload Failed", Toast.LENGTH_SHORT) + .show() + } + } binding.btnLogout.setOnClickListener { logoutUser(requireContext()) } binding.uploadData.setOnClickListener { showUploadDialog(requireContext()) } + binding.btnNewKit.setOnClickListener { + with(sharedPreference.edit()) { + putString(Constants.KIT_NUMBER, "") + putInt(Constants.KIT_COUNT, 0) + apply() + } + startActivity(Intent(requireContext(), KitScanActivity::class.java)) + requireActivity().finish() + } } + private fun setUserId() { binding.btnSubmit.setOnClickListener { val userId = binding.userId.text.toString() @@ -98,29 +131,49 @@ class HomeFragment : Fragment() { } } } + private fun loadUserData() { -// val query = Firebase.firestore.collection("patientData").whereEqualTo("testStatus", false).orderBy("createdAt", Query.Direction.DESCENDING) - val query = Firebase.firestore.collection("patientData").whereEqualTo("testStatus", false) - query.get().addOnSuccessListener { - if (it.documents.isEmpty()) { - binding.pendingTest.visibility = View.VISIBLE - } else { - binding.pendingTest.visibility = View.GONE + try { + val dateFormat = SimpleDateFormat("yyyy-MM-dd") + val currentDate = Date() + val formattedDate = dateFormat.format(currentDate) + val query = Firebase.firestore.collection("patientData") + .whereGreaterThanOrEqualTo("createdAt", formattedDate) + .orderBy("createdAt", Query.Direction.DESCENDING) +// val query = Firebase.firestore.collection("patientData").whereEqualTo("testStatus", false) + query.get().addOnSuccessListener { + if (it.documents.isEmpty()) { + binding.pendingTest.visibility = View.VISIBLE + } else { + binding.pendingTest.visibility = View.GONE + } } + val recyclerViewOptions = + FirestoreRecyclerOptions.Builder().setQuery(query, UserData::class.java) + .build() + + val bm = requireContext().getSystemService(BATTERY_SERVICE) as BatteryManager + val batLevel: Int = bm.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY) + + rvAdapter = view?.let { + UserListAdapter(requireContext(), hemoCubeViewModel, recyclerViewOptions, + it, batLevel, requireActivity()) + }!! + binding.rvOrder.adapter = rvAdapter + + rvAdapter.startListening() + } catch (e: Exception) { + Firebase.crashlytics.recordException(e) } - val recyclerViewOptions = - FirestoreRecyclerOptions.Builder().setQuery(query, UserData::class.java) - .build() - rvAdapter = UserListAdapter(recyclerViewOptions, binding.root) - binding.rvOrder.adapter = rvAdapter - rvAdapter.startListening() } + private fun saveUserId(userId: String) { with(sharedPreference.edit()) { putString(Constants.USER_ID, userId) apply() } } + private fun logoutUser(context: Context) { val builder = AlertDialog.Builder(context) builder.setTitle("Log Out") @@ -137,22 +190,42 @@ class HomeFragment : Fragment() { val dialog = builder.create() dialog.show() } + private fun getData(search: String?, field: String) { - val capitalizedSearch = search?.replaceFirstChar { + val userSearchTrace = Firebase.performance.newTrace("user_search_trace") + userSearchTrace.start() + + search?.replaceFirstChar { if (search.lowercase() .startsWith(it.lowercase()) ) it.titlecase(Locale.getDefault()) else it.toString() } - val query = Firebase.firestore.collection("patientData").orderBy(field).startAt(search) - .endAt(search + "\uf8ff") + val currentDate = Date() + val dateFormat = SimpleDateFormat("yyyyMMdd") + val partition = dateFormat.format(currentDate) + val searchTerm = partition + search + val searchField = field + "Search" + val query = Firebase.firestore.collection("patientData").orderBy(searchField).startAt(searchTerm) + .endAt(searchTerm + "\uf8ff") + query.get().addOnSuccessListener { + userSearchTrace.stop() + } val recyclerViewOptions = FirestoreRecyclerOptions.Builder().setQuery(query, UserData::class.java) .build() - rvAdapter = UserListAdapter(recyclerViewOptions, binding.root) + val bm = requireContext().getSystemService(BATTERY_SERVICE) as BatteryManager + batLevel = bm.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY) + + rvAdapter = view?.let { + UserListAdapter(requireContext(), hemoCubeViewModel, recyclerViewOptions, it, batLevel, requireActivity()) + }!! binding.rvOrder.adapter = rvAdapter rvAdapter.startListening() + + userSearchTrace.stop() } + private fun setSearch() { binding.searchProduct.setOnQueryTextListener(object : androidx.appcompat.widget.SearchView.OnQueryTextListener { @@ -173,6 +246,7 @@ class HomeFragment : Fragment() { false } } + override fun onQueryTextChange(query: String?): Boolean { return if (!query.isNullOrEmpty()) { val field = when { @@ -190,6 +264,7 @@ class HomeFragment : Fragment() { } }) } + private fun checkForLocalDBData() { viewModel.allUserData.observe(viewLifecycleOwner) { userDataList -> val uploadDataVisibility = if (userDataList.isEmpty()) View.GONE else View.VISIBLE @@ -197,10 +272,11 @@ class HomeFragment : Fragment() { } hemoCubeViewModel.allUserData.observe(viewLifecycleOwner) { userDataList -> - val uploadDataVisibility = if (userDataList.isEmpty()) View.GONE else View.VISIBLE + val uploadDataVisibility = if (userDataList.any { !it.localFlag }) View.VISIBLE else View.GONE binding.uploadData.visibility = uploadDataVisibility } } + private fun showUploadDialog(context: Context) { val builder = AlertDialog.Builder(context) builder.setTitle(R.string.upload_db_registration_title) @@ -229,28 +305,19 @@ class HomeFragment : Fragment() { } dialog.dismiss() Toast.makeText( - requireContext(), - R.string.upload_success_message, - Toast.LENGTH_SHORT + requireContext(), R.string.upload_success_message, Toast.LENGTH_SHORT ).show() } } hemoCubeViewModel.allUserData.observe(viewLifecycleOwner) { userDataList -> - if (userDataList.isEmpty()) { - dialog.dismiss() - } else { - userDataList.forEach { userData -> + userDataList.forEach { userData -> + if (!userData.localFlag) { userData.localFlag = true hemoCubeViewModel.bulkAddResultTestToDb(userData) } - dialog.dismiss() - Toast.makeText( - requireContext(), - R.string.upload_success_message, - Toast.LENGTH_SHORT - ).show() } + dialog.dismiss() } } diff --git a/app/src/main/java/com/example/hpostesting/presentation/diagnostics/DiagnosticsActivity.kt b/app/src/main/java/com/example/hpostesting/presentation/diagnostics/DiagnosticsActivity.kt new file mode 100644 index 0000000..df5d356 --- /dev/null +++ b/app/src/main/java/com/example/hpostesting/presentation/diagnostics/DiagnosticsActivity.kt @@ -0,0 +1,176 @@ +package com.example.hpostesting.presentation.diagnostics + +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 androidx.appcompat.app.AppCompatActivity +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.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.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.ActivityDiagnosticsBinding + +@AndroidEntryPoint +class DiagnosticsActivity : AppCompatActivity() { + private lateinit var binding: ActivityDiagnosticsBinding + val viewModel: DiagnosticsViewModel 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 onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + binding = ActivityDiagnosticsBinding.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() + } + } + } + + + fun onErrorReported(msg: String) { + Toast.makeText(this, msg, Toast.LENGTH_SHORT).show() + if (!isFinishing) onBackPressed() + } + + + private fun moveToNext() { + if (supportFragmentManager.isDestroyed) return + + supportFragmentManager.beginTransaction().replace(binding.fgDiagnostics.id, DiagnosticsFragment()) + .commit() + } + + 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) + } + + + 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 + } + } +} diff --git a/app/src/main/java/com/example/hpostesting/presentation/diagnostics/DiagnosticsFragment.kt b/app/src/main/java/com/example/hpostesting/presentation/diagnostics/DiagnosticsFragment.kt new file mode 100644 index 0000000..fd48f9e --- /dev/null +++ b/app/src/main/java/com/example/hpostesting/presentation/diagnostics/DiagnosticsFragment.kt @@ -0,0 +1,197 @@ +package com.example.hpostesting.presentation.diagnostics + +import android.content.Context +import android.content.SharedPreferences +import android.os.Bundle +import android.util.Log +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.Toast +import androidx.fragment.app.Fragment +import androidx.fragment.app.activityViewModels +import androidx.lifecycle.MutableLiveData +import com.example.hpostesting.data.constant.Constants +import com.example.hpostesting.data.constant.HemoCubeCommands +import com.example.hpostesting.data.model.diagnostics.DiagnosticsData +import com.example.hpostesting.data.model.patient.DeviceData +import com.example.hpostesting.presentation.UsbServiceListener +import com.google.firebase.crashlytics.ktx.crashlytics +import com.google.firebase.ktx.Firebase +import `in`.sminnovations.hpostesting.databinding.FragmentDiagnosticsBinding +import java.text.SimpleDateFormat +import java.util.Calendar +import java.util.Locale + +class DiagnosticsFragment : Fragment() { + + private lateinit var binding: FragmentDiagnosticsBinding + private val diagnosticsViewModel: DiagnosticsViewModel by activityViewModels() + private lateinit var sharedPreferences: SharedPreferences + private var currentDeviceData: DeviceData? = null + private var resultData: String = "" + private val messages = MutableLiveData() + private var startListening = MutableLiveData(false) + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?, + ): View { + binding = FragmentDiagnosticsBinding.inflate(inflater, container, false) + sharedPreferences = + requireContext().getSharedPreferences("PREFERENCE_NAME", Context.MODE_PRIVATE) + return binding.root + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + initViews() + observeViewModel() + } + + + private fun initViews() { + binding.btnSubmit.visibility = View.GONE + listenToHemoCube() + getDeviceId() + binding.btnSubmit.setOnClickListener { + binding.btnSubmit.visibility = View.GONE + runDeviceDiagnostics() + } + } + + + private fun observeViewModel() { + + diagnosticsViewModel.deviceData.observe(viewLifecycleOwner) { + currentDeviceData = it + } + + messages.observe(viewLifecycleOwner) { + binding.tvSubtitle4.text = it + Log.e("diagnostics",binding.tvSubtitle4.text.toString()) + } + + diagnosticsViewModel.fireBaseUpload.observe(viewLifecycleOwner) { result -> + if (result == "Success") { + showToast("Diagnostics data uploaded successfully") + } + if (result == "Local") { + showToast("Diagnostics uploading failed, note it down manually") + } + + binding.progressBar.visibility = View.GONE + } + } + + private fun getDeviceId() { + diagnosticsViewModel.progressBar.postValue(true) + (activity as DiagnosticsActivity).mService.sendAndListenToHemoCube( + HemoCubeCommands.getDeviceId, + object : UsbServiceListener { + override fun onUsbRead(data: ByteArray?) { + data?.let { + val stringData = String(it) + diagnosticsViewModel.messages.postValue(stringData) + binding.tvSubtitle4.text = stringData + } + } + override fun onUsbError(e: Exception?) { + diagnosticsViewModel.progressBar.postValue(false) + } + }) + } + + private fun runDeviceDiagnostics() { + diagnosticsViewModel.progressBar.postValue(true) + (activity as DiagnosticsActivity).mService.sendAndListenToHemoCube( + HemoCubeCommands.runDiagnostics, + object : UsbServiceListener { + override fun onUsbRead(data: ByteArray?) { + + + } + override fun onUsbError(e: Exception?) { + diagnosticsViewModel.progressBar.postValue(false) + } + }) + } + + private fun listenToHemoCube() { + + val fullReadOutput = StringBuilder() + startListening.postValue(true) + + try { + (activity as DiagnosticsActivity).mService.listenToHemoCube(object : UsbServiceListener { + override fun onUsbRead(data: ByteArray?) { + data?.let { + val stringData = String(it) + diagnosticsViewModel.messages.postValue(stringData) + fullReadOutput.append(stringData) + resultData += stringData + binding.tvSubtitle4.text = resultData + if (stringData.contains("SN")) { + val slData = stringData.split(" ") + if (slData.size > 1) { + val hardwareId = slData[1].trim() + with(sharedPreferences.edit()) { + putString(Constants.DEVICE_ID, hardwareId) + apply() + } + } + activity?.runOnUiThread { + binding.btnSubmit.visibility = View.VISIBLE + } + } + } + + if (resultData.contains("END") || fullReadOutput.contains("END")) { + diagnosticsViewModel.addDiagnosticsDataToDb(DiagnosticsData( + deviceId = sharedPreferences.getString(Constants.DEVICE_ID, "").toString(), + deviceData = resultData, + runTime = SimpleDateFormat( + "yyyy-MM-dd HH:mm:ss", Locale.getDefault() + ).format(Calendar.getInstance().time) + )) + } +// val inputData = mutableListOf() +// while (true) { +// if (fullReadOutput.toString().lowercase() == "end") { +// break +// } +// inputData.add(fullReadOutput.toString()) +// } +// +// val parsedData = parseData(inputData) +// Log.i("D-Data", parsedData.toString()) + } + + override fun onUsbError(e: Exception?) { + diagnosticsViewModel.progressBar.postValue(false) + } + }) + } catch (e: Exception) { + Firebase.crashlytics.recordException(e) + } + } + + + fun parseData(inputData: List): List> { + val pattern = Regex("([A-Z]+)\\s(\\d+)") + val parsedData = mutableListOf>() + + for (item in inputData) { + val matchResult = pattern.find(item) + if (matchResult != null) { + val (letters, numbers) = matchResult.destructured + parsedData.add(Pair(letters, numbers)) + } + } + + return parsedData + } + + private fun showToast(message: String) { + Toast.makeText(requireContext(), message, Toast.LENGTH_SHORT).show() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/hpostesting/presentation/diagnostics/DiagnosticsViewModel.kt b/app/src/main/java/com/example/hpostesting/presentation/diagnostics/DiagnosticsViewModel.kt new file mode 100644 index 0000000..27319f4 --- /dev/null +++ b/app/src/main/java/com/example/hpostesting/presentation/diagnostics/DiagnosticsViewModel.kt @@ -0,0 +1,65 @@ +package com.example.hpostesting.presentation.diagnostics + +import android.content.Context +import android.content.SharedPreferences +import android.util.Log +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.example.hpostesting.data.DataHolder +import com.example.hpostesting.data.NetworkStatusLiveData +import com.example.hpostesting.data.constant.Constants +import com.example.hpostesting.data.dao.HemoCubeDao +import com.example.hpostesting.data.model.Response +import com.example.hpostesting.data.model.diagnostics.DiagnosticsData +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.repository.DatabaseRepository +import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.launch +import java.text.SimpleDateFormat +import java.util.Calendar +import java.util.Locale +import javax.inject.Inject +@HiltViewModel +class DiagnosticsViewModel @Inject constructor( + private val hemoCubeDao: HemoCubeDao, + private val repository: DatabaseRepository, + context: Context +) : ViewModel() { + var isServiceConnected = false + val progressBar = MutableLiveData(false) + val messages = MutableLiveData() + private val sharedPreference: SharedPreferences = + context.getSharedPreferences("PREFERENCE_NAME", Context.MODE_PRIVATE) + + private val _networkStatusLiveData = NetworkStatusLiveData(context) + val allUserData = hemoCubeDao.getAll() + val deviceData = MutableLiveData() + val networkStatusLiveData: LiveData + get() = _networkStatusLiveData + val fireBaseUpload = MutableLiveData() + + fun addDiagnosticsDataToDb(data: DiagnosticsData) { + viewModelScope.launch { + try { + when (val response = repository.addDiagnostics(data)) { + 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") + } + } + } +} \ No newline at end of file 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 6d3d08a..ee592d9 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 @@ -3,7 +3,9 @@ package com.example.hpostesting.presentation.hemocube import android.content.Context import android.content.Intent import android.content.SharedPreferences +import android.graphics.Color import android.os.Bundle +import android.util.Log import android.view.LayoutInflater import android.view.View import android.view.ViewGroup @@ -11,28 +13,20 @@ import android.widget.Toast import androidx.fragment.app.Fragment import androidx.fragment.app.activityViewModels import androidx.lifecycle.MutableLiveData -import androidx.lifecycle.lifecycleScope import com.example.hpostesting.data.DataHolder import com.example.hpostesting.data.constant.Constants import com.example.hpostesting.data.constant.HemoCubeCommands import com.example.hpostesting.data.model.patient.DeviceData import com.example.hpostesting.data.model.patient.toHemoCubeTestData -import com.example.hpostesting.presentation.KitScanActivity import com.example.hpostesting.presentation.UsbServiceListener import com.example.hpostesting.presentation.dashboard.DashboardActivity 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.firestore.FirebaseFirestore -import com.google.firebase.firestore.ktx.firestore import com.google.firebase.ktx.Firebase +import com.google.firebase.perf.ktx.performance import `in`.sminnovations.hpostesting.R import `in`.sminnovations.hpostesting.databinding.FragmentHemoCubeReferenceBinding -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.delay -import kotlinx.coroutines.launch -import kotlinx.coroutines.tasks.await -import kotlinx.coroutines.withContext import kotlin.math.log10 class HemoCubeFragment : Fragment() { @@ -40,12 +34,15 @@ class HemoCubeFragment : Fragment() { private lateinit var binding: FragmentHemoCubeReferenceBinding private val hemoCubeViewModel: HemoCubeViewModel by activityViewModels() private lateinit var sharedPreferences: SharedPreferences + val testDetails = DataHolder.selectedTest?.toHemoCubeTestData() private var isOnline = false private var currentDeviceData: DeviceData? = null private var resultData: String = "" private var isUsingExistingBuffer = false private var isTestOngoing = false private var startListening = MutableLiveData(false) + val testingTrace = Firebase.performance.newTrace("testing_trace") + override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?, ): View { @@ -64,92 +61,50 @@ class HemoCubeFragment : Fragment() { private fun initViews() { binding.btnSubmit.setOnClickListener { - val bloodGroupText = binding.etBloodGroup.text.toString() - if (bloodGroupText.isEmpty()) { - binding.etBloodGroup.error = "Please enter your blood group" - } else { - fetchResult() + activity?.runOnUiThread { + binding.progressBar.visibility = View.VISIBLE } + hemoCubeViewModel.uploadHemoCubeResultToDatabase( + isOnline, true, sharedPreferences.getString(Constants.KIT_NUMBER, "") + ) } if (isTestOngoing) { - binding.btnKit.visibility = View.GONE - binding.btnKit.isEnabled = false - binding.btnKit.isClickable = false - +// binding.btnKit.visibility = View.GONE +// binding.btnKit.isEnabled = false +// binding.btnKit.isClickable = false } + + 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.btnKit.setOnClickListener { - if (isTestOngoing) { - val title = "WARNING" - val message = - "There is a on going test, do you want to stop it. if Yes please disconnect the device and connect it again, once you reached to kitDetails Screen" - val negativeText = getString(R.string.no) - val positiveText = "Yes" - UIUtils.createAlertDialog(requireContext(), - title, - message, - negativeText, - positiveText, - object : MyDialogListener { - override fun onClickNegativeButton() { - } - - override fun onClickPositiveButton() { - resetKitCount() - val intent = Intent(context, KitScanActivity::class.java) - startActivity(intent) - (activity as HemocubeActivity).finish() - } - }) - } else { - resetKitCount() - val intent = Intent(context, KitScanActivity::class.java) - startActivity(intent) - (activity as HemocubeActivity).finish() - } - } - } - - private suspend fun checkBloodGroup() { - binding.apply { - val bloodGroup = etBloodGroup.text.toString() - if (bloodGroup.isEmpty()) { - etBloodGroup.error = "Please enter your blood group" - } else { - val db: FirebaseFirestore = Firebase.firestore - val userdata = db.collection("patientData") - .whereEqualTo("_id", hemoCubeViewModel.testDetails?._id).get().await() - if (userdata.documents.isNotEmpty()) { - db.collection("patientData").document(userdata.documents[0].id) - .update("bloodGroup", bloodGroup) - withContext(Dispatchers.Main) { - val i = Intent( - requireContext().applicationContext, DashboardActivity::class.java - ) - startActivity(i) - (activity as HemocubeActivity).finish() - } - } - } - } + binding.tvName.text = "Name: ${testDetails?.name}\n ID: ${testDetails?._id}" + Log.e("nametext", binding.tvName.text.toString()) } private fun observeViewModel() { hemoCubeViewModel.fireBaseUpload.observe(viewLifecycleOwner) { result -> if (result == "Success") { showToast("Test Results Uploaded Successfully") - lifecycleScope.launch { - checkBloodGroup() - } + val i = Intent( + requireContext().applicationContext, DashboardActivity::class.java + ) + startActivity(i) + (activity as HemocubeActivity).finish() } if (result == "Local") { showToast("Internet not available, test details stored locally") startActivity(Intent(requireActivity(), DashboardActivity::class.java)) } + if (result == "Error") { + showToast("Error uploading data, test details stored locally") + startActivity(Intent(requireActivity(), DashboardActivity::class.java)) + } binding.progressBar.visibility = View.GONE } @@ -167,7 +122,6 @@ class HemoCubeFragment : Fragment() { val coefficient1 = coefficients[0] val coefficient2 = coefficients[1] val result = coefficient1 * coefficient2 - showToast("coefficients: $coefficient1, $coefficient2") } } isOnline = isNetworkAvailable @@ -278,17 +232,13 @@ class HemoCubeFragment : Fragment() { isTestOngoing = true } - resultData += fullReadOutput.toString() + resultData += stringData when { - stringData.contains("ERROR 500") -> showError500Dialog() - stringData.contains("ERROR 501") -> showError501Dialog() stringData.contains("#Buffer Completed") -> showStartSampleDialog() stringData.contains("#Sample Completed") -> { - activity?.runOnUiThread { - binding.btnSubmit.isEnabled = true - binding.btnSubmit.isClickable = true - } + fetchResult() + testingTrace.stop() } stringData.contains("RESULT") || resultData.contains("REND") -> { @@ -329,23 +279,6 @@ class HemoCubeFragment : Fragment() { } } - - private fun showError500Dialog() { - UIUtils.createAlertDialog(requireContext(), - "BUFFER ERROR", - "Do you want to continue with existing buffer?", - getString(R.string.noo), - "Yes", - object : MyDialogListener { - override fun onClickNegativeButton() {} - - override fun onClickPositiveButton() { - listenToHemoCube() - startBufferProcess() - } - }) - } - fun handleBackButtonPress() { if (isTestOngoing) { // If the test is ongoing, don't allow the back button action @@ -358,23 +291,6 @@ class HemoCubeFragment : Fragment() { } } - - private fun showError501Dialog() { - UIUtils.createAlertDialog(requireContext(), - "SAMPLE ERROR", - "Do you want to continue with Sample?", - getString(R.string.noo), - "Yes", - object : MyDialogListener { - override fun onClickNegativeButton() {} - - override fun onClickPositiveButton() { - listenToHemoCube() - startSampleProcess() - } - }) - } - private fun showStartSampleDialog() { activity?.runOnUiThread { UIUtils.createAlertDialog(requireContext(), @@ -396,8 +312,20 @@ class HemoCubeFragment : Fragment() { private fun handleValidResult(validString: String, fullReadOutput: String) { try { val result = validString.split(" ") + val deviceLog = resultData if (result.size == 8) { val deviceSerialNo = result[2] + with(sharedPreferences.edit()) { + putString(Constants.DEVICE_ID, deviceSerialNo) + apply() + } + val loginId = sharedPreferences.getString(Constants.USER_ID, "").toString() + if (checkDeviceIdMismatch(deviceSerialNo, loginId)) { + activity?.runOnUiThread { + binding.errorMessage.text = "Warning: Login Id is different" + binding.errorMessage.visibility = View.VISIBLE + } + } val led1BufferForDevice = if (isUsingExistingBuffer) { sharedPreferences.getString(Constants.BUFFER_VALUE_1, "")?.toDoubleOrNull() } else { @@ -426,6 +354,14 @@ class HemoCubeFragment : Fragment() { // } // } // } + + if (led1Average < 0 || led2Average < 0) { + activity?.runOnUiThread{ + binding.errorMessage.text = "Warning: Negative Abs. Retake Blank Reading" + binding.errorMessage.visibility = View.VISIBLE + } + } + DataHolder.hemoCubeTestData?.apply { deviceId = deviceSerialNo led1Buffer = led1BufferForDevice @@ -437,9 +373,10 @@ class HemoCubeFragment : Fragment() { this.led2Average = led2Average this.deviceRatio = deviceRatio this.calculatedRatio = calculateRatio(deviceRatio) + this.coefficients = currentDeviceData?.coefficients?.get(0).toString() + ", " + currentDeviceData?.coefficients?.get(1).toString() this.classificationResult = findResult(calculatedRatio) hemoCubeViewModel.messages.postValue(this.classificationResult) - this.resultData = fullReadOutput + this.resultData = deviceLog if (!isUsingExistingBuffer) { with(sharedPreferences.edit()) { putString(Constants.BUFFER_VALUE_1, led1Buffer.toString()) @@ -448,11 +385,11 @@ class HemoCubeFragment : Fragment() { } } activity?.runOnUiThread { - binding.progressBar.visibility = View.VISIBLE + binding.btnSubmit.visibility = View.VISIBLE + binding.btnSubmit.isEnabled = true + binding.btnSubmit.isClickable = true + binding.clParent.setBackgroundColor(Color.parseColor("#edfffd")) } - hemoCubeViewModel.uploadHemoCubeResultToDatabase( - isOnline, true, sharedPreferences.getString(Constants.KIT_NUMBER, "") - ) } } } catch (e: Exception) { @@ -468,9 +405,9 @@ class HemoCubeFragment : Fragment() { private fun findResult(calculatedRatio: Double?): String { try { if (calculatedRatio != null) { - if (calculatedRatio < 0.085) + if (calculatedRatio < 0.05) return "Inconclusive. Very low Absorbance - Repeat test with Higher Blood Volume" - if (calculatedRatio in 0.085..0.155) + if (calculatedRatio in 0.05..0.155) return "Normal" if (calculatedRatio in 0.155..0.175) return "Negative Borderline. Repeat Test" @@ -511,6 +448,7 @@ class HemoCubeFragment : Fragment() { } private fun startSampleProcess() { + testingTrace.start() hemoCubeViewModel.progressBar.postValue(true) (activity as HemocubeActivity).mService.sendAndListenToHemoCube(HemoCubeCommands.startSample, @@ -565,11 +503,9 @@ class HemoCubeFragment : Fragment() { } } - private fun resetKitCount() { - with(sharedPreferences.edit()) { - putString(Constants.KIT_NUMBER, "") - putInt(Constants.KIT_COUNT, 0) - apply() - } + private fun checkDeviceIdMismatch(deviceId: String, loginId: String): Boolean { + if (!deviceId.isNullOrEmpty() && !loginId.isNullOrEmpty() && deviceId.last() != loginId.last()) + return true + return false } } diff --git a/app/src/main/java/com/example/hpostesting/presentation/hemocube/HemoCubeViewModel.kt b/app/src/main/java/com/example/hpostesting/presentation/hemocube/HemoCubeViewModel.kt index 1901e76..73d05fc 100644 --- a/app/src/main/java/com/example/hpostesting/presentation/hemocube/HemoCubeViewModel.kt +++ b/app/src/main/java/com/example/hpostesting/presentation/hemocube/HemoCubeViewModel.kt @@ -12,6 +12,7 @@ import com.example.hpostesting.data.NetworkStatusLiveData import com.example.hpostesting.data.constant.Constants import com.example.hpostesting.data.dao.HemoCubeDao import com.example.hpostesting.data.model.Response +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 @@ -44,6 +45,7 @@ class HemoCubeViewModel @Inject constructor( val networkStatusLiveData: LiveData get() = _networkStatusLiveData val fireBaseUpload = MutableLiveData() + val fireBaseBulkUpload = MutableLiveData() fun uploadHemoCubeResultToDatabase(isOnline: Boolean, testStatus: Boolean, kitSerial: String?) = viewModelScope.launch { if (kitSerial != null) { @@ -68,6 +70,13 @@ class HemoCubeViewModel @Inject constructor( } } + + + fun uploadHemoCubeResultToDatabaseforbuffercheckN(bufferCheckData: BufferCheckData) = + viewModelScope.launch { + addResultTestToDbforbuffercheck(bufferCheckData) + } + fun getDeviceData(deviceId: String?) = viewModelScope.launch { deviceData.postValue(deviceId?.let { repository.getDeviceDataById(it) }) } @@ -92,6 +101,7 @@ class HemoCubeViewModel @Inject constructor( testDetails?.led2Average = DataHolder.hemoCubeTestData?.led2Average testDetails?.deviceRatio = DataHolder.hemoCubeTestData?.deviceRatio testDetails?.calculatedRatio = DataHolder.hemoCubeTestData?.calculatedRatio + testDetails?.coefficients = DataHolder.hemoCubeTestData?.coefficients testDetails?.classificationResult = DataHolder.hemoCubeTestData?.classificationResult!! } @@ -111,6 +121,32 @@ class HemoCubeViewModel @Inject constructor( } Log.i("Testdb", "Data uploaded to Firestore successfully") fireBaseUpload.postValue("Success") + testDetails.localFlag = true + hemoCubeDao.insertAll(testDetails) + } + + is Response.Error -> { + Log.e("Testdb", "Error uploading data to Firestore: $response") + fireBaseUpload.postValue("Error") + hemoCubeDao.insertAll(testDetails) + } + } + } catch (e: Exception) { + Log.e("Testdb", "Exception during data upload: ${e.message}") + fireBaseUpload.postValue("Error") + } + } + } + + + 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 -> { @@ -132,15 +168,21 @@ class HemoCubeViewModel @Inject constructor( ).format(Calendar.getInstance().time) when (repository.addTestToDatabase(userData)) { is Response.Success -> { - fireBaseUpload.postValue("Success") - deleteById(userData._id) + fireBaseBulkUpload.postValue("Success") + updateLocalFlag(userData._id) } - else -> {} + else -> { + fireBaseBulkUpload.postValue("Error") + } } } } + private fun updateLocalFlag(userId: String) = viewModelScope.launch { + hemoCubeDao.updateFieldById(id = userId, true) + } + fun deleteById(userId: String) = viewModelScope.launch { hemoCubeDao.deleteById(id = userId) } diff --git a/app/src/main/res/drawable/ic_add.xml b/app/src/main/res/drawable/ic_add.xml new file mode 100644 index 0000000..c22704d --- /dev/null +++ b/app/src/main/res/drawable/ic_add.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/layout/activity_buffer_check.xml b/app/src/main/res/layout/activity_buffer_check.xml new file mode 100644 index 0000000..cb786da --- /dev/null +++ b/app/src/main/res/layout/activity_buffer_check.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_diagnostics.xml b/app/src/main/res/layout/activity_diagnostics.xml new file mode 100644 index 0000000..e1e4185 --- /dev/null +++ b/app/src/main/res/layout/activity_diagnostics.xml @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/dialog_custom.xml b/app/src/main/res/layout/dialog_custom.xml new file mode 100644 index 0000000..afdf55e --- /dev/null +++ b/app/src/main/res/layout/dialog_custom.xml @@ -0,0 +1,24 @@ + + + + + + + + diff --git a/app/src/main/res/layout/fragment_diagnostics.xml b/app/src/main/res/layout/fragment_diagnostics.xml new file mode 100644 index 0000000..7b96f9b --- /dev/null +++ b/app/src/main/res/layout/fragment_diagnostics.xml @@ -0,0 +1,102 @@ + + + + + + + + + + + + + + + + + + + + + + +