Compare commits

..

1 Commits

24 changed files with 177 additions and 762 deletions

View File

@@ -18,8 +18,8 @@ android {
applicationId "in.sminnovations.hpostesting"
minSdk 21
targetSdk 34
versionCode 24
versionName "2.1.24"
versionCode 20
versionName "2.1.21"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
@@ -60,7 +60,6 @@ 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'
@@ -97,17 +96,17 @@ dependencies {
implementation 'com.google.android.gms:play-services-code-scanner:16.1.0'
//Room
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")
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")
//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.3"
implementation "androidx.navigation:navigation-ui-ktx:2.7.3"
implementation "androidx.navigation:navigation-fragment-ktx:2.7.2"
implementation "androidx.navigation:navigation-ui-ktx:2.7.2"
//Dagger - Hilt
implementation "com.google.dagger:hilt-android:2.46"

View File

@@ -13,11 +13,8 @@
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-feature android:name="android.hardware.usb.host" />
<uses-permission android:name="android.permission.USB_PERMISSION" />
<application
android:name="com.example.hpostesting.HPOSTestingApplication"
android:allowBackup="true"
@@ -29,9 +26,6 @@
android:supportsRtl="true"
android:theme="@style/Theme.HPOSTesting"
tools:targetApi="31">
<activity
android:name="com.example.hpostesting.presentation.diagnostics.DiagnosticsActivity"
android:exported="false" />
<activity
android:name="com.example.hpostesting.presentation.hemocube.HemocubeActivity"
android:exported="false"
@@ -107,6 +101,7 @@
android:screenOrientation="portrait"
android:stateNotNeeded="true"
tools:replace="android:screenOrientation" />
</application>
</manifest>

View File

@@ -69,5 +69,4 @@ object Constants {
const val KIT_COUNT = "KitCount"
const val BUFFER_VALUE_1 = "BufferValue1"
const val BUFFER_VALUE_2 = "BufferValue2"
const val DEVICE_ID = "DEVICE_ID"
}

View File

@@ -2,8 +2,7 @@ package com.example.hpostesting.data.constant
enum class HemoCubeCommands(val command: String) {
startBuffer("B\r"),
runDiagnostics("D\r"),
getBuffer("Q\r"),
startSample("S\r"),
getSample("P\r"),
getDeviceId("I\r"),
}

View File

@@ -1,9 +0,0 @@
package com.example.hpostesting.data.model.diagnostics
data class DiagnosticsData (
var deviceId: String = "",
var appVersion: String? = "",
var deviceType: String = "HEMOCUBE",
var deviceData: String = "",
var runTime: String = ""
)

View File

@@ -15,7 +15,5 @@ data class BufferCheckData(
var led2Average: Double? = null,
var deviceRatio: Double? = null,
var calculatedRatio: Double? = null,
var coefficients: String? = "",
var classificationResult: String = "",
var testTime: String = ""
var classificationResult: String = ""
)

View File

@@ -30,6 +30,5 @@ data class HemoCubeTestData(
var led2Average: Double? = null,
var deviceRatio: Double? = null,
var calculatedRatio: Double? = null,
var coefficients: String? = "",
var classificationResult: String = ""
)

View File

@@ -3,7 +3,6 @@ package com.example.hpostesting.data.repository
import android.net.Uri
import com.example.hpostesting.data.model.PendingUploads
import com.example.hpostesting.data.model.Response
import com.example.hpostesting.data.model.diagnostics.DiagnosticsData
import com.example.hpostesting.data.model.patient.BufferCheckData
import com.example.hpostesting.data.model.patient.DeviceData
import com.example.hpostesting.data.model.patient.HemoCubeTestData
@@ -62,16 +61,6 @@ class DatabaseRepository : Repository {
}
}
suspend fun addDiagnostics(data: DiagnosticsData?): Response<String> {
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<Boolean> {
try {

View File

@@ -11,7 +11,6 @@ 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

View File

@@ -29,9 +29,6 @@ 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() {
@@ -63,8 +60,6 @@ class HemoCubeBufferCheckFragment : Fragment() {
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()
@@ -217,7 +212,7 @@ class HemoCubeBufferCheckFragment : Fragment() {
try {
val result = validString.split(" ")
if (result.size == 8) {
val deviceId = result[2]
val deviceSerialNo = result[2]
val led1BufferForDevice = result[3].toDoubleOrNull()
val led2BufferForDevice = result[4].toDoubleOrNull()
val led1Sample = result[5].toDoubleOrNull()
@@ -225,11 +220,10 @@ class HemoCubeBufferCheckFragment : Fragment() {
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)
val calculatedRatio = 0.14
val classificationResult = findResult(calculatedRatio)
messages.postValue(classificationResult)
val bufferData = BufferCheckData(
deviceId= deviceId,
kitno = kitno,
led1Buffer = led1BufferForDevice,
led2Buffer = led2BufferForDevice,
@@ -238,13 +232,9 @@ class HemoCubeBufferCheckFragment : Fragment() {
led1Sample = led1Sample,
led2Sample = led2Sample,
deviceRatio = deviceRatio,
calculatedRatio = calculateRatio,
coefficients = currentDeviceData?.coefficients?.get(0).toString() + ", " + currentDeviceData?.coefficients?.get(1).toString(),
calculatedRatio = calculatedRatio,
classificationResult = classificationResult,
deviceSerialNumber = sharedPreferences.getString(Constants.USER_ID, "").toString(),
testTime = SimpleDateFormat(
"yyyy-MM-dd HH:mm:ss", Locale.getDefault()
).format(Calendar.getInstance().time)
deviceSerialNumber = deviceSerialNo
)
hemoCubeViewModel.uploadHemoCubeResultToDatabaseforbuffercheckN(bufferData)
}

View File

@@ -66,14 +66,11 @@ class DashboardActivity : AppCompatActivity() {
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()
}
Toast.makeText(
this,
"${updateProgress.updateStatus}. ${updateProgress.apkBytesDownloaded / 1048576} /${updateProgress.apkFileTotalBytes / 1048576} MB",
Toast.LENGTH_SHORT
).show()
}
.addOnFailureListener { e ->
// (Optional) Handle errors.
@@ -85,18 +82,14 @@ class DashboardActivity : AppCompatActivity() {
else -> {
// Handle other errors.
Toast.makeText(
this,
"AppDistribution error, status: ${e.errorCode}",
Toast.LENGTH_SHORT
).show()
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()
Toast.makeText(this, "App Update: Success!", Toast.LENGTH_SHORT).show()
}
}
}

View File

@@ -7,7 +7,6 @@ import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import com.example.hpostesting.presentation.buffercheck.HemocubeBufferCheckActivity
import com.example.hpostesting.presentation.diagnostics.DiagnosticsActivity
import `in`.sminnovations.hpostesting.databinding.FragmentGalleryBinding
class GalleryFragment : Fragment() {
@@ -36,12 +35,6 @@ class GalleryFragment : Fragment() {
binding.btnSubmit.setOnClickListener {
startActivity(Intent(requireContext(), HemocubeBufferCheckActivity::class.java))
}
binding.btnDiagnostics.setOnClickListener {
startActivity(Intent(requireContext(), DiagnosticsActivity::class.java))
}
return root
}

View File

@@ -28,7 +28,6 @@ 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
@@ -192,25 +191,14 @@ class HomeFragment : Fragment() {
}
private fun getData(search: String?, field: String) {
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 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 query = Firebase.firestore.collection("patientData").orderBy(field).startAt(search)
.endAt(search + "\uf8ff")
val recyclerViewOptions =
FirestoreRecyclerOptions.Builder<UserData>().setQuery(query, UserData::class.java)
.build()
@@ -222,8 +210,6 @@ class HomeFragment : Fragment() {
}!!
binding.rvOrder.adapter = rvAdapter
rvAdapter.startListening()
userSearchTrace.stop()
}
private fun setSearch() {

View File

@@ -1,176 +0,0 @@
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
}
}
}

View File

@@ -1,197 +0,0 @@
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<String>()
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<String>()
// 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<String>): List<Pair<String, String>> {
val pattern = Regex("([A-Z]+)\\s(\\d+)")
val parsedData = mutableListOf<Pair<String, String>>()
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()
}
}

View File

@@ -1,65 +0,0 @@
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<String>()
private val sharedPreference: SharedPreferences =
context.getSharedPreferences("PREFERENCE_NAME", Context.MODE_PRIVATE)
private val _networkStatusLiveData = NetworkStatusLiveData(context)
val allUserData = hemoCubeDao.getAll()
val deviceData = MutableLiveData<DeviceData?>()
val networkStatusLiveData: LiveData<Boolean>
get() = _networkStatusLiveData
val fireBaseUpload = MutableLiveData<String>()
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")
}
}
}
}

View File

@@ -3,7 +3,6 @@ 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
@@ -13,20 +12,27 @@ 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.launch
import kotlinx.coroutines.tasks.await
import kotlinx.coroutines.withContext
import kotlin.math.log10
class HemoCubeFragment : Fragment() {
@@ -41,8 +47,6 @@ class HemoCubeFragment : Fragment() {
private var isUsingExistingBuffer = false
private var isTestOngoing = false
private var startListening = MutableLiveData<Boolean>(false)
val testingTrace = Firebase.performance.newTrace("testing_trace")
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?,
): View {
@@ -61,32 +65,88 @@ class HemoCubeFragment : Fragment() {
private fun initViews() {
binding.btnSubmit.setOnClickListener {
activity?.runOnUiThread {
binding.progressBar.visibility = View.VISIBLE
}
hemoCubeViewModel.uploadHemoCubeResultToDatabase(
isOnline, true, sharedPreferences.getString(Constants.KIT_NUMBER, "")
)
// val bloodGroupText = binding.etBloodGroup.text.toString()
// if (bloodGroupText.isEmpty()) {
// binding.etBloodGroup.error = "Please enter your blood group"
// } else {
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.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.tvName.text = "Name: ${testDetails?.name}\n ID: ${testDetails?._id}"
Log.e("nametext", binding.tvName.text.toString())
Log.e("nametext",binding.tvName.text.toString())
// binding.btnKit.setOnClickListener {
// if (isTestOngoing) {
// val title = "WARNING"
// val message = "There is a on going test, do you want to stop it"
// 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()
// }
// }
// }
// }
// }
private fun observeViewModel() {
hemoCubeViewModel.fireBaseUpload.observe(viewLifecycleOwner) { result ->
if (result == "Success") {
@@ -122,6 +182,7 @@ class HemoCubeFragment : Fragment() {
val coefficient1 = coefficients[0]
val coefficient2 = coefficients[1]
val result = coefficient1 * coefficient2
// showToast("coefficients: $coefficient1, $coefficient2")
}
}
isOnline = isNetworkAvailable
@@ -232,13 +293,18 @@ class HemoCubeFragment : Fragment() {
isTestOngoing = true
}
resultData += stringData
resultData += fullReadOutput.toString()
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") -> {
@@ -279,6 +345,23 @@ 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
@@ -291,6 +374,23 @@ 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(),
@@ -312,20 +412,8 @@ 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 {
@@ -354,15 +442,9 @@ 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
led2Buffer = led2BufferForDevice
@@ -373,10 +455,9 @@ 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 = deviceLog
this.resultData = fullReadOutput
if (!isUsingExistingBuffer) {
with(sharedPreferences.edit()) {
putString(Constants.BUFFER_VALUE_1, led1Buffer.toString())
@@ -384,12 +465,6 @@ class HemoCubeFragment : Fragment() {
apply()
}
}
activity?.runOnUiThread {
binding.btnSubmit.visibility = View.VISIBLE
binding.btnSubmit.isEnabled = true
binding.btnSubmit.isClickable = true
binding.clParent.setBackgroundColor(Color.parseColor("#edfffd"))
}
}
}
} catch (e: Exception) {
@@ -448,7 +523,6 @@ class HemoCubeFragment : Fragment() {
}
private fun startSampleProcess() {
testingTrace.start()
hemoCubeViewModel.progressBar.postValue(true)
(activity as HemocubeActivity).mService.sendAndListenToHemoCube(HemoCubeCommands.startSample,
@@ -503,9 +577,11 @@ class HemoCubeFragment : Fragment() {
}
}
private fun checkDeviceIdMismatch(deviceId: String, loginId: String): Boolean {
if (!deviceId.isNullOrEmpty() && !loginId.isNullOrEmpty() && deviceId.last() != loginId.last())
return true
return false
private fun resetKitCount() {
with(sharedPreferences.edit()) {
putString(Constants.KIT_NUMBER, "")
putInt(Constants.KIT_COUNT, 0)
apply()
}
}
}

View File

@@ -101,7 +101,6 @@ 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!!
}

View File

@@ -1,5 +1,6 @@
package com.example.hpostesting.presentation.hemocube
import android.annotation.SuppressLint
import android.app.PendingIntent
import android.content.BroadcastReceiver
import android.content.ComponentName
@@ -7,6 +8,7 @@ import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.content.ServiceConnection
import android.content.SharedPreferences
import android.hardware.usb.UsbDevice
import android.hardware.usb.UsbDeviceConnection
import android.hardware.usb.UsbManager
@@ -17,10 +19,14 @@ import android.view.Menu
import android.widget.Toast
import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContentProviderCompat.requireContext
import androidx.core.content.ContextCompat
import androidx.core.view.get
import androidx.navigation.fragment.findNavController
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.dashboard.ui.LoginFragment
import com.example.hpostesting.presentation.testRight.UsbService
import com.hoho.android.usbserial.driver.UsbSerialDriver
import com.hoho.android.usbserial.driver.UsbSerialProber
@@ -33,7 +39,7 @@ open class HemocubeActivity : AppCompatActivity() {
private lateinit var binding: ActivityHemocubeBinding
private val viewModel by viewModels<HemoCubeViewModel>()
private var myMenu: Menu? = null
private lateinit var sharedPreference: SharedPreferences
private lateinit var mDriver: UsbSerialDriver
private var mConnection: UsbDeviceConnection? = null
lateinit var mService: UsbService
@@ -81,8 +87,11 @@ open class HemocubeActivity : AppCompatActivity() {
supportActionBar?.setDisplayHomeAsUpEnabled(true)
setupListener()
connectUsb(false)
applicationContext.getSharedPreferences("PREFERENCE_NAME", Context.MODE_PRIVATE)
}
@SuppressLint("SuspiciousIndentation")
private fun setupListener() {
DataHolder.usbConnected.observe(this) {
Log.d("USB OBSERVE", "HemoCube called -> $it")
@@ -92,6 +101,7 @@ open class HemocubeActivity : AppCompatActivity() {
} else {
myMenu?.get(0)?.icon =
ContextCompat.getDrawable(this, R.drawable.ic_baseline_usb_off_24)
logoutUser()
Toast.makeText(this, "Device is Disconnected", Toast.LENGTH_SHORT).show()
}
}
@@ -185,4 +195,16 @@ open class HemocubeActivity : AppCompatActivity() {
viewModel.isServiceConnected = false
}
}
private fun logoutUser() {
val intent = Intent(this, DashboardActivity::class.java)
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
startActivity(intent)
finish()
}
}

View File

@@ -1,40 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/cl_parent"
android:layout_width="match_parent"
android:layout_height="match_parent">
<!-- <androidx.appcompat.widget.Toolbar-->
<!-- android:id="@+id/my_toolbar"-->
<!-- android:layout_width="match_parent"-->
<!-- android:layout_height="?attr/actionBarSize"-->
<!-- android:background="?attr/colorPrimary"-->
<!-- app:titleTextColor="#FFFFFF"-->
<!-- android:elevation="4dp"-->
<!-- app:menu="@menu/my_menu"-->
<!-- android:theme="@style/ToolbarTheme"-->
<!-- app:popupTheme="@style/ThemeOverlay.AppCompat.Light"-->
<!-- app:layout_constraintStart_toStartOf="parent"-->
<!-- app:layout_constraintTop_toTopOf="parent"/>-->
<FrameLayout
android:id="@+id/fg_diagnostics"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
tools:context=".presentation.diagnostics.DiagnosticsActivity">
</FrameLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>

View File

@@ -1,102 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.example.hpostesting.presentation.diagnostics.DiagnosticsFragment">
<!-- <TextView-->
<!-- android:id="@+id/tv_title"-->
<!-- style="@style/title1"-->
<!-- android:layout_width="wrap_content"-->
<!-- android:layout_height="wrap_content"-->
<!-- android:layout_marginHorizontal="24dp"-->
<!-- android:text="@string/diagnostics"-->
<!-- android:layout_marginTop="16dp"-->
<!-- app:layout_constraintStart_toStartOf="parent"-->
<!-- app:layout_constraintTop_toTopOf="parent" />-->
<TextView
android:id="@+id/tv_subtitle2"
style="@style/title2"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginHorizontal="24dp"
android:layout_marginTop="16dp"
android:text="@string/diagnostics"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/tv_subtitle3"
style="@style/title2"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginHorizontal="24dp"
android:layout_marginTop="16dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/tv_subtitle2" />
<TextView
android:id="@+id/tv_subtitle4"
style="@style/title1_1"
android:layout_width="0dp"
android:layout_height="380dp"
android:layout_marginHorizontal="24dp"
android:layout_marginTop="16dp"
android:gravity="center"
android:text="Start"
android:textColor="@color/red"
android:textSize="22sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/tv_subtitle3" />
<Button
android:id="@+id/btn_submit"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginHorizontal="16dp"
android:layout_marginTop="24dp"
android:clickable="false"
android:gravity="center"
android:text="Start"
android:textColor="@color/white"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/tv_subtitle4" />
<!-- <Button-->
<!-- android:id="@+id/btn_read"-->
<!-- android:layout_width="wrap_content"-->
<!-- android:layout_height="wrap_content"-->
<!-- android:text="@string/read"-->
<!-- android:layout_margin="16dp"-->
<!-- android:clickable="false"-->
<!-- app:layout_constraintTop_toBottomOf="@id/btn_set_reference"-->
<!-- app:layout_constraintStart_toStartOf="parent" />-->
<!-- </androidx.constraintlayout.widget.ConstraintLayout>-->
<ProgressBar
android:id="@+id/progressBar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:elevation="12dp"
android:indeterminate="true"
android:indeterminateDrawable="@drawable/progressbar_drawable"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -21,16 +21,4 @@
app:layout_constraintTop_toTopOf="parent" />
<Button
android:id="@+id/btn_diagnostics"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginHorizontal="16dp"
android:layout_marginTop="24dp"
android:clickable="false"
android:text="@string/diagnostics"
android:textColor="@color/white"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/btn_submit" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -44,16 +44,12 @@
<!-- app:layout_constraintTop_toBottomOf="@id/tv_subtitle2" />-->
<TextView
android:visibility="gone"
android:id="@+id/tv_name"
style="@style/title2"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginHorizontal="24dp"
android:gravity="center"
android:padding="12dp"
android:layout_marginTop="26dp"
android:textFontWeight="700"
android:text="Name: SMI\n ID: 1672282828"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
tools:ignore="MissingConstraints" />
@@ -64,7 +60,7 @@
android:layout_width="0dp"
android:layout_height="80dp"
android:layout_marginHorizontal="24dp"
android:layout_marginTop="72dp"
android:layout_marginTop="16dp"
android:gravity="center"
android:textColor="@color/red"
android:textSize="18sp"
@@ -163,21 +159,6 @@
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/tv_subtitle4" />
<TextView
android:id="@+id/error_message"
style="@style/title1_1"
android:visibility="gone"
android:layout_width="match_parent"
android:layout_height="80dp"
android:layout_marginHorizontal="24dp"
android:layout_marginTop="172dp"
android:gravity="center"
android:textColor="@color/red"
android:textSize="18sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/btn_submit" />
<!-- <com.google.android.material.textfield.TextInputLayout-->
<!-- android:id="@+id/til_blood_group"-->
<!-- style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox.ExposedDropdownMenu"-->

View File

@@ -112,7 +112,6 @@
<string name="sickle_cert">Sickle Cert</string>
<string name="sickle_find">Sickle Find</string>
<string name="submit">Submit</string>
<string name="diagnostics">Device Diagnostics</string>
<string name="place_buffer">Start Buffer</string>
<string name="Start_Sample">Start Sample</string>
<string name="new_kit">New Kit</string>