Compare commits

..

34 Commits
QR-SCAN ... uat

Author SHA1 Message Date
Mariya
95b3a072b4 fixed usb permission error for v1 device 2024-03-01 16:32:43 +05:30
Pritimay Sarkar
be21430d0a Merge remote-tracking branch 'origin/nats_certificate_download' into dev 2024-02-11 14:08:00 +05:30
Pritimay Sarkar
3609d728c1 release 2.1.110 2024-02-10 12:18:52 +05:30
Pritimay Sarkar
7302ad669f release 2.1.108 2024-02-10 11:10:06 +05:30
Pritimay Sarkar
ef98b09eaf release 2.1.107 2024-02-10 10:46:54 +05:30
Pritimay Sarkar
f631a273d7 TLS impleamentation on NATS server 2024-02-09 13:50:33 +05:30
Pritimay Sarkar
de74da3135 add package name dynamically in provider 2024-02-09 13:49:16 +05:30
Mariya
37c8dde5bd Added code in manifest file for launcher 2024-02-08 15:44:27 +05:30
Mariya
5f460f6aa3 Added code for firefox and Files redirection 2024-02-08 15:30:51 +05:30
Pritimay Sarkar
ad625992a7 fix pipeline 2024-02-08 14:41:27 +05:30
Pritimay Sarkar
db518007b3 Merge branch 'dev' of https://gitlab.com/sminnovations/hpos into dev 2024-02-08 12:33:49 +05:30
Pritimay Sarkar
4d09bad516 log addititonal data in diagnostics and auto dac 2024-02-08 12:33:36 +05:30
Mariya
aad33e3907 Merge remote-tracking branch 'origin/dev' into dev 2024-02-08 11:59:06 +05:30
Mariya
52279c4419 code removed from manifest for molbio 2024-02-08 11:58:45 +05:30
Pritimay Sarkar
190a72e407 Merge branch 'dev' of https://gitlab.com/sminnovations/hpos into dev 2024-02-07 17:19:33 +05:30
Mariya
466d0e2afc code added related to nats 2024-02-07 16:33:57 +05:30
Pritimay Sarkar
4aa6fb020b display device ratio and slope ratio 2024-02-07 16:25:34 +05:30
Mariya
044a545979 code added in manifest file 2024-02-07 13:01:09 +05:30
Mariya
45fd4861f4 nats code 2024-02-07 12:49:31 +05:30
Mariya
1f9f8060cb manifest file changes and drawable file added for release apk 2024-02-06 16:48:19 +05:30
Mariya
1a3bb67dce nats 2024-02-06 16:23:33 +05:30
Pritimay Sarkar
4fef75b937 add new thresholds with additonal method and unit tests 2024-02-06 13:44:00 +05:30
Mariya
3dd247b15d Nats Certificate Download API is completed 2024-02-06 13:25:06 +05:30
Mariya
42b33c9397 Added code for scan , Issue fixed 2024-02-05 17:23:33 +05:30
Pritimay Sarkar
c5980a9cfe add unit tests 2024-02-04 15:21:34 +05:30
Pritimay Sarkar
3e0523e163 refactor kit scan 2024-02-04 14:59:07 +05:30
Pritimay Sarkar
173d4a2767 save dac values and refactor 2024-02-04 14:45:37 +05:30
Pritimay Sarkar
c166bab804 add unit test 2024-02-04 13:55:25 +05:30
Pritimay Sarkar
205c7f1f53 Merge branch 'dev' of https://gitlab.com/sminnovations/hpos into dev 2024-02-02 16:55:06 +05:30
Pritimay Sarkar
468cacfbe5 release 2.1.99 2024-02-02 16:53:02 +05:30
Pritimay Sarkar
a4bdfa1c71 Merge branch 'QR-SCAN' into dev 2024-02-02 16:24:02 +05:30
Pritimay Sarkar
985c484b8f release 2.1.99 2024-02-02 16:23:28 +05:30
Pritimay Sarkar
0ac384a7b5 add new screen for workflow change 2024-02-02 13:16:26 +05:30
Mariya
feac2dc75b Changes related to device ratio and lowerlimit added 2024-02-02 12:33:37 +05:30
50 changed files with 3445 additions and 234 deletions

View File

@@ -14,17 +14,18 @@ android {
compileSdk 34
namespace 'in.sminnovations.hpostesting'
// prod - production, preprod - preproduction, quality - qc, dev - development
// dev - development, quality - qc, uat - User Acceptance Test, preprod - preproduction, prod - production
defaultConfig {
applicationId "in.sminnovations.hpostesting.dev"
applicationId "in.sminnovations.hpostesting.quality"
minSdk 21
targetSdk 34
versionCode 97
versionName "2.1.97"
versionCode 106
versionName "2.1.106"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
@@ -111,6 +112,8 @@ dependencies {
androidTestImplementation 'org.mockito:mockito-android:3.12.4'
androidTestImplementation 'org.mockito:mockito-inline:3.12.4'
androidTestImplementation 'org.mockito:mockito-android:3.12.4'
testImplementation 'org.powermock:powermock-api-mockito2:2.0.9'
testImplementation 'org.powermock:powermock-module-junit4:2.0.9'
testImplementation "androidx.arch.core:core-testing:2.2.0"
testImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-test:1.7.1'
@@ -154,7 +157,7 @@ dependencies {
implementation("com.squareup.okhttp3:okhttp:4.9.3")
implementation "androidx.preference:preference-ktx:1.2.1"
implementation 'io.nats:jnats:2.11.2'
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.1'
implementation("androidx.work:work-runtime-ktx:2.9.0")
@@ -162,5 +165,6 @@ dependencies {
implementation 'com.google.android.play:core:1.10.3'
implementation fileTree(dir: 'libs', include: ['*.aar'])
// implementation fileTree(dir: '/Users/apple/Downloads/work/hpos/app/libs', include: ['*.aar', '*.jar'])
implementation 'io.nats:jnats:2.11.4'
}

Binary file not shown.

View File

@@ -4,15 +4,15 @@
"type": "APK",
"kind": "Directory"
},
"applicationId": "com.example.hpos",
"applicationId": "in.sminnovations.hpostesting.quality",
"variantName": "release",
"elements": [
{
"type": "SINGLE",
"filters": [],
"attributes": [],
"versionCode": 1,
"versionName": "1.0",
"versionCode": 101,
"versionName": "2.1.101",
"outputFile": "app-release.apk"
}
],

View File

@@ -69,6 +69,15 @@
android:parentActivityName="com.example.hpostesting.presentation.MainActivity"
android:theme="@style/Theme.HPOS.NoActionBar"
android:windowSoftInputMode="adjustPan" />
<activity
android:name="com.example.hpostesting.presentation.trueheme.TrueHemeActivity"
android:exported="false"
android:noHistory="true"
android:parentActivityName="com.example.hpostesting.presentation.MainActivity"
android:theme="@style/Theme.HPOS.NoActionBar"
android:windowSoftInputMode="adjustPan" />
<activity
android:name="com.example.hpostesting.presentation.buffercheck.HemocubeBufferCheckActivity"
android:exported="false"
@@ -86,6 +95,7 @@
android:exported="false"
android:label="@string/title_activity_dashboard"
android:theme="@style/Theme.HPOS.NoActionBar"
android:screenOrientation="portrait"
tools:ignore="AppLinkUrlError,MissingClass">
<intent-filter>
@@ -110,8 +120,12 @@
android:theme="@style/AppTheme.NoActionBar">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
<category android:name="android.intent.category.HOME" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.MONKEY"/>
<category android:name="android.intent.category.LAUNCHER_APP" />
</intent-filter>
</activity>
<activity
@@ -151,7 +165,7 @@
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="com.example.hpostesting.fileprovider"
android:authorities="${applicationId}.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data

View File

@@ -7,8 +7,11 @@ import com.example.hpostesting.data.model.updates.CheckUpdateRequest
import com.example.hpostesting.data.model.updates.CheckUpdateResponse
import com.example.hpostesting.data.model.updates.DeviceUpdateRequest
import okhttp3.MultipartBody
import okhttp3.Response
import okhttp3.ResponseBody
import retrofit2.http.Body
import retrofit2.http.GET
import retrofit2.http.Header
import retrofit2.http.Multipart
import retrofit2.http.POST
import retrofit2.http.PUT
@@ -31,6 +34,10 @@ interface MolbioResultApi {
@Body deviceUpdateRequest: DeviceUpdateRequest
): ResponseBody
@GET("deviceService/device/getClientCertificate")
suspend fun downloadClientCertificate(
): ResponseBody
@Multipart
@POST("deviceService/device/uploadLogs")
suspend fun uploadLogs(

View File

@@ -2,13 +2,13 @@ package com.example.hpostesting.data.constant
object Constants {
const val ACTION_USB_PERMISSION = "shanmukha.in.sickle_cell.USB_PERMISSION"
const val HOMOCUBE_USB_PERMISSION = "shanmukha.in.sickle_cell_homocube.USB_PERMISSION"
const val HEMOCUBE_USB_PERMISSION = "shanmukha.in.sickle_cell_homocube.USB_PERMISSION"
const val BASE_URL = "www.google.com"
const val ABHA_APP_PACKAGE = "in.ndhm.phr"
const val MOLBIO_INTERGATION = false
const val MOLBIO_INTEGRATION = false
const val deviceProvisionEmail = "HPOS_provisioner@bigtec.co.in"
const val deviceProvisionPassword = "f2ab0e7f9d69"
const val DEVICE_ID_API = "deviceIDAPI"
@@ -35,8 +35,9 @@ object Constants {
const val MAX_WAVELENGTH_RANGE_TO_RECORD = 750
const val DEVICE_PRODUCT_ID = 24597
const val DEVICE_TYPE_HOMOCUBE = "HOMOCUBE"
const val DEVICE_TYPE_HEMOCUBE = "HEMOCUBE"
const val DEVICE_TYPE_TEST_RIGHT = "TEST_RIGHT"
const val DEVICE_TYPE_TRUEHEME = "TRUEHEME"
const val DEVICE_VENDOR_ID = 1027
const val HOMO_CUBE_ID = 29987
@@ -58,6 +59,8 @@ object Constants {
const val QUICK_CAPTURE_VOLUME = "VOLUME"
const val QUICK_CAPTURE_READING_PER_SAMPLE = "READING_PER_SAMPLE"
const val BLANK_EVERY_TEST = false
val STATICID = listOf(
"FACTORY",
"ADMIN",

View File

@@ -4,6 +4,10 @@ enum class TestStatus(val code: Double) {
TEST_STARTED(1.0),
CONFIG_STARTED(2.0),
CONFIG_COMPLETED(3.0),
FIRST_EMPTY_AIR_READING_STARTED(4.1),
FIRST_EMPTY_AIR_READING_COMPLETED(4.2),
FIRST_EMPTY_AIR_READING_PRINT_STARTED(4.3),
FIRST_EMPTY_AIR_READING_PRINT_COMPLETED(4.4),
BUFFER_STARTED(4.0),
BUFFER_COMPLETED(5.0),
BUFFER_PRINT_STARTED(6.0),
@@ -12,6 +16,10 @@ enum class TestStatus(val code: Double) {
SAMPLE_COMPLETED(9.0),
SAMPLE_PRINT_STARTED(10.0),
SAMPLE_PRINT_COMPLETED(11.0),
SECOND_EMPTY_AIR_READING_STARTED(11.1),
SECOND_EMPTY_AIR_READING_COMPLETED(11.2),
SECOND_EMPTY_AIR_PRINT_STARTED(11.3),
SECOND_EMPTY_AIR_PRINT_COMPLETED(11.4),
FIRST_GAIN_STARTED(12.0),
FIRST_GAIN_COMPLETED(13.0),
FIRST_GAIN_PRINT_STARTED(14.0),

View File

@@ -8,7 +8,7 @@ import com.example.hpostesting.data.model.patient.DeviceData
import com.example.hpostesting.data.model.patient.HemoCubeTestData
import com.example.hpostesting.data.model.patient.UserData
@Database(entities = [UserData::class, HemoCubeTestData::class, DeviceData::class, BufferCheckData::class], version = 23, exportSchema = false)
@Database(entities = [UserData::class, HemoCubeTestData::class, DeviceData::class, BufferCheckData::class], version = 24, exportSchema = false)
@TypeConverters(Converters::class)
abstract class MyDatabase : RoomDatabase() {
abstract fun userDao(): UserDao

View File

@@ -24,6 +24,14 @@ data class TestState (
var fittedAbs2: Double = 0.0,
var fittedAbs3: Double = 0.0,
var fittedAbs4: Double = 0.0,
// var led1Air1: Double? = null,
// var led2Air1: Double? = null,
// var led3Air1: Double? = null,
// var led4Air1: Double? = null,
// var led1Air2: Double? = null,
// var led2Air2: Double? = null,
// var led3Air2: Double? = null,
// var led4Air2: Double? = null,
var calculatedPredictedDenovixRatio: Double = 0.0,
var validationError: Boolean = false,
var deviceHardwareId: String = "",

View File

@@ -5,5 +5,8 @@ data class DiagnosticsData (
var appVersion: String? = "",
var deviceType: String = "HEMOCUBE",
var deviceData: String = "",
var devicePassword: String = "",
var deviceNatsToken: String = "",
var accessToken: String = "",
var runTime: String = ""
)

View File

@@ -61,6 +61,14 @@ data class HemoCubeTestData(
var led2Gain4: Double? = null,
var led3Gain4: Double? = null,
var led4Gain4: Double? = null,
var led1Air1: Double? = null,
var led2Air1: Double? = null,
var led3Air1: Double? = null,
var led4Air1: Double? = null,
var led1Air2: Double? = null,
var led2Air2: Double? = null,
var led3Air2: Double? = null,
var led4Air2: Double? = null,
var deviceRatio: Double? = null,
var calculatedRatio: Double? = null,
var predictedDenovixRatio: Double? = null,

View File

@@ -77,6 +77,9 @@ class DatabaseRepository @Inject constructor(
return safeApiCall { molbioResultApi.deviceUpdate(deviceUpdateRequest) }
}
override suspend fun downloadClientCertificate(): Result<ResponseBody> {
return safeApiCall { molbioResultApi.downloadClientCertificate() }
}
override suspend fun uploadLogs(logFile: MultipartBody.Part): Result<UploadLogsResponse> {
return safeApiCall { molbioResultApi.uploadLogs(logFile) }
}

View File

@@ -45,5 +45,6 @@ interface Repository {
suspend fun deviceUpdate(deviceUpdateRequest: DeviceUpdateRequest): Result<ResponseBody>
suspend fun downloadClientCertificate(): Result<ResponseBody>
suspend fun uploadLogs(logFile: MultipartBody.Part): Result<UploadLogsResponse>
}

View File

@@ -1,7 +1,6 @@
package com.example.hpostesting.domain
import android.content.Context
import android.util.Log
import java.io.File
import java.io.FileOutputStream
import java.io.IOException
@@ -32,7 +31,7 @@ class LogFileManagerImpl @Inject constructor(private val context: Context) : Log
file
} catch (e: IOException) {
Log.e("LogFileManager", "Error creating log file: ${e.message}")
// Log.e("LogFileManager", "Error creating log file: ${e.message}")
null
}
}

View File

@@ -3,8 +3,8 @@ package com.example.hpostesting.presentation
import android.content.Context
import android.content.Intent
import android.content.SharedPreferences
import android.os.AsyncTask
import android.os.Bundle
import android.text.Editable
import android.util.Log
import android.view.View
import android.widget.EditText
@@ -31,7 +31,6 @@ import com.zebra.scannercontrol.IDcsSdkApiDelegate
import com.zebra.scannercontrol.SDKHandler
import `in`.sminnovations.hpostesting.R
import `in`.sminnovations.hpostesting.databinding.ActivityKitScanBinding
import java.nio.charset.StandardCharsets
class KitScanActivity : AppCompatActivity(), IDcsSdkApiDelegate {
@@ -209,77 +208,62 @@ class KitScanActivity : AppCompatActivity(), IDcsSdkApiDelegate {
}
}
//Setting up the SDK handler
//Setting up the SDK handler
sdkHandler = SDKHandler(this)
//Registers a particular object which conforms to IDcsSdkApiDelegate interface as a receiver of SDK notifications.
//Registers a particular object which conforms to IDcsSdkApiDelegate interface as a receiver of SDK notifications.
sdkHandler!!.dcssdkSetDelegate(this)
//this command is telling the sdk that we're going to be connecting to the scanner via USB
//this command is telling the sdk that we're going to be connecting to the scanner via USB
sdkHandler!!.dcssdkSetOperationalMode(DCSSDKDefs.DCSSDK_MODE.DCSSDK_OPMODE_SNAPI)
//deciding what kind of notifications we want to receive. Explained more in the function
//first we use bitmapping to set these values into the notifications_mask.
//deciding what kind of notifications we want to receive. Explained more in the function
//first we use bitmapping to set these values into the notifications_mask.
var notifications_mask = 0
// We would like to subscribe to all barcode events
// We would like to subscribe to all barcode events
notifications_mask =
notifications_mask or DCSSDKDefs.DCSSDK_EVENT.DCSSDK_EVENT_BARCODE.value
// subscribe to events set in notification mask
// subscribe to events set in notification mask
sdkHandler!!.dcssdkSubsribeForEvents(notifications_mask)
//so adding all the scanners to the mScannerInfoList, then connecting to the first scanner.
//There's only one scanner so this step is unnecessary, I did it thinking about possible scalability.
//here I just get the list of available scanners which also returns the USB scanner attached to the tinker board.
//so adding all the scanners to the mScannerInfoList, then connecting to the first scanner.
//There's only one scanner so this step is unnecessary, I did it thinking about possible scalability.
//here I just get the list of available scanners which also returns the USB scanner attached to the tinker board.
mScannerInfoList.addAll(sdkHandler!!.dcssdkGetAvailableScannersList())
//I'm doing mScannerInfoList.get(0) because there's only one scanner which will be at position 0.
//I'm doing mScannerInfoList.get(0) because there's only one scanner which will be at position 0.
sdkHandler!!.dcssdkEstablishCommunicationSession(mScannerInfoList[0].scannerID)
}
fun pullTrigger() {
if (!mScannerInfoList[0].isActive) {
Log.e("scannersize",sdkHandler!!.dcssdkGetAvailableScannersList().size.toString())
if (mScannerInfoList.isNotEmpty()) {
sdkHandler!!.dcssdkEstablishCommunicationSession(mScannerInfoList[0].scannerID)
} else {
Toast.makeText(this,"Error", Toast.LENGTH_LONG).show()
}
val inXML = "<inArgs><scannerID> 1 </scannerID></inArgs>"
val outXML = StringBuilder()
val result: DCSSDK_RESULT =
sdkHandler!!.dcssdkExecuteCommandOpCodeInXMLForScanner(
DCSSDK_COMMAND_OPCODE.DCSSDK_DEVICE_PULL_TRIGGER, inXML, outXML, 1
)
if (result == DCSSDK_RESULT.DCSSDK_RESULT_SUCCESS) Log.d(
"Scanning",
"Success"
) else if (result == DCSSDK_RESULT.DCSSDK_RESULT_FAILURE) Log.d("Scanning", "Failed")
//new MyAsyncTask(1, DCSSDKDefs.DCSSDK_COMMAND_OPCODE.DCSSDK_DEVICE_PULL_TRIGGER,null).execute(inXML);
}
private fun pullTrigger() {
// Check if the list is not empty before accessing its elements
if (mScannerInfoList.isNotEmpty()) {
// Only proceed if the scanner is not active
if (!mScannerInfoList[0].isActive) {
sdkHandler?.dcssdkEstablishCommunicationSession(mScannerInfoList[0].scannerID)
}
val inXML = "<inArgs><scannerID> 1 </scannerID></inArgs>"
val outXML = StringBuilder()
val result: DCSSDK_RESULT =
sdkHandler!!.dcssdkExecuteCommandOpCodeInXMLForScanner(
DCSSDK_COMMAND_OPCODE.DCSSDK_DEVICE_PULL_TRIGGER, inXML, outXML, mScannerInfoList[0].scannerID // Ensure you're using the correct scanner ID
)
if (result == DCSSDK_RESULT.DCSSDK_RESULT_SUCCESS) {
Log.d("Scanning", "Success")
} else if (result == DCSSDK_RESULT.DCSSDK_RESULT_FAILURE) {
Log.d("Scanning", "Failed")
}
} else {
// Handle the case where the list is empty, perhaps notify the user or log an error
Log.e("ScannerError", "No scanners are connected or available.")
}
}
//this function is called if barcode is detected.
override fun dcssdkEventBarcode(barcodeData: ByteArray?, barcodeType: Int, fromScannerID: Int) {
val result = String(barcodeData!!)
Log.d("BARCODE", result)
runOnUiThread {
binding.tvTitle2.text = result
val editableResult: Editable = Editable.Factory.getInstance().newEditable(result)
binding.nameEditText.text = editableResult
}
}
@@ -302,7 +286,7 @@ class KitScanActivity : AppCompatActivity(), IDcsSdkApiDelegate {
DataHolder.deviceType.observe(this) { deviceType ->
when (deviceType) {
Constants.DEVICE_TYPE_HOMOCUBE -> {
Constants.DEVICE_TYPE_HEMOCUBE -> {
val i = Intent(applicationContext, HemocubeActivity::class.java)
startActivity(i)
}
@@ -311,6 +295,11 @@ class KitScanActivity : AppCompatActivity(), IDcsSdkApiDelegate {
val i = Intent(applicationContext, TestRightActivity::class.java)
startActivity(i)
}
Constants.DEVICE_TYPE_TRUEHEME -> {
val i = Intent(applicationContext, HemocubeActivity::class.java)
startActivity(i)
}
}
}
}

View File

@@ -1,8 +1,10 @@
package com.example.hpostesting.presentation
import com.example.hpostesting.presentation.dashboard.DashboardActivity
import android.Manifest
import android.content.*
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.content.pm.PackageManager
import android.hardware.usb.UsbManager
import android.location.Location
@@ -20,7 +22,12 @@ import com.example.hpostesting.data.constant.Constants
import com.example.hpostesting.data.constant.LanguageManager
import com.example.hpostesting.data.model.patient.UserData
import com.example.hpostesting.data.model.test.TestType
import com.google.android.gms.location.*
import com.example.hpostesting.presentation.dashboard.DashboardActivity
import com.google.android.gms.location.FusedLocationProviderClient
import com.google.android.gms.location.LocationCallback
import com.google.android.gms.location.LocationRequest
import com.google.android.gms.location.LocationResult
import com.google.android.gms.location.LocationServices
import com.google.android.material.snackbar.Snackbar
import com.google.firebase.crashlytics.FirebaseCrashlytics
import com.hoho.android.usbserial.driver.UsbSerialProber
@@ -81,7 +88,7 @@ class MainActivity : AppCompatActivity() {
DataHolder.deviceType.observe(this) { deviceType ->
with(binding) {
if (deviceType == Constants.DEVICE_TYPE_HOMOCUBE) {
if (deviceType == Constants.DEVICE_TYPE_HEMOCUBE) {
cvItem1.visibility = View.VISIBLE
cvItem3.visibility = View.VISIBLE
cvItem2.visibility = View.GONE
@@ -111,8 +118,8 @@ class MainActivity : AppCompatActivity() {
binding.cvItem3.visibility = View.VISIBLE
binding.cvItem2.visibility = View.GONE
binding.cvItem4.visibility = View.GONE
DataHolder.deviceType.postValue(Constants.DEVICE_TYPE_HOMOCUBE)
Constants.DEVICE_TYPE_HOMOCUBE
DataHolder.deviceType.postValue(Constants.DEVICE_TYPE_HEMOCUBE)
Constants.DEVICE_TYPE_HEMOCUBE
}
device.productId == Constants.HEMO_CUBE_V2_PRODUCT_ID && device.vendorId == Constants.HEMO_CUBE_V2_VENDOR_ID -> {
@@ -120,8 +127,8 @@ class MainActivity : AppCompatActivity() {
binding.cvItem3.visibility = View.VISIBLE
binding.cvItem2.visibility = View.GONE
binding.cvItem4.visibility = View.GONE
DataHolder.deviceType.postValue(Constants.DEVICE_TYPE_HOMOCUBE)
Constants.DEVICE_TYPE_HOMOCUBE
DataHolder.deviceType.postValue(Constants.DEVICE_TYPE_HEMOCUBE)
Constants.DEVICE_TYPE_HEMOCUBE
}
device.productId == 24577 && device.vendorId == 1027 -> {
@@ -129,8 +136,8 @@ class MainActivity : AppCompatActivity() {
binding.cvItem3.visibility = View.VISIBLE
binding.cvItem2.visibility = View.GONE
binding.cvItem4.visibility = View.GONE
DataHolder.deviceType.postValue(Constants.DEVICE_TYPE_HOMOCUBE)
Constants.DEVICE_TYPE_HOMOCUBE
DataHolder.deviceType.postValue(Constants.DEVICE_TYPE_TRUEHEME)
Constants.DEVICE_TYPE_TRUEHEME
}
device.productId == 8963 && device.vendorId == 1659 -> {
@@ -138,8 +145,8 @@ class MainActivity : AppCompatActivity() {
binding.cvItem3.visibility = View.VISIBLE
binding.cvItem2.visibility = View.GONE
binding.cvItem4.visibility = View.GONE
DataHolder.deviceType.postValue(Constants.DEVICE_TYPE_HOMOCUBE)
Constants.DEVICE_TYPE_HOMOCUBE
DataHolder.deviceType.postValue(Constants.DEVICE_TYPE_HEMOCUBE)
Constants.DEVICE_TYPE_HEMOCUBE
}
device.productId == Constants.DEVICE_PRODUCT_ID && device.vendorId == Constants.DEVICE_VENDOR_ID -> {
@@ -278,7 +285,6 @@ class MainActivity : AppCompatActivity() {
// exception.printStackTrace()
// }
fusedLocationClient.lastLocation.addOnSuccessListener { location: Location? ->
location?.let {
if (DataHolder.selectedTest != null) {
@@ -302,9 +308,6 @@ class MainActivity : AppCompatActivity() {
val rootView = findViewById<View>(android.R.id.content)
Snackbar.make(rootView, errorMessage, Snackbar.LENGTH_LONG).show()
}
}
private fun requestLocationUpdates() {

View File

@@ -1,6 +1,9 @@
package com.example.hpostesting.presentation
import android.os.Build
import android.util.Log
import androidx.annotation.RequiresApi
import com.example.hpostesting.presentation.assurance.AssuranceControlsActivity
import com.example.hpostesting.presentation.dashboard.DashboardActivity
import io.nats.client.AuthHandler
import io.nats.client.Connection
@@ -8,9 +11,22 @@ import io.nats.client.Message
import io.nats.client.NKey
import io.nats.client.Nats
import io.nats.client.Options
import io.nats.client.support.SSLUtils
import java.io.BufferedInputStream
import java.io.File
import java.io.FileInputStream
import java.io.IOException
import java.nio.charset.StandardCharsets
import java.nio.file.Paths
import java.security.GeneralSecurityException
import java.security.KeyStore
import java.security.SecureRandom
import java.security.cert.CertificateFactory
import javax.net.ssl.KeyManager
import javax.net.ssl.KeyManagerFactory
import javax.net.ssl.SSLContext
import javax.net.ssl.TrustManager
import javax.net.ssl.TrustManagerFactory
class NatsManager(datacollector: DashboardActivity) {
@@ -20,17 +36,57 @@ class NatsManager(datacollector: DashboardActivity) {
val datacollector = datacollector
var connect = false
private fun createSSLContext(): SSLContext {
val keyStorePassword = "prime24".toCharArray() // Change as necessary
val clientCertPath = "/storage/sdcard0/Download/client.p12"
// Load client certificate and key
val keyStore = KeyStore.getInstance("PKCS12")
FileInputStream(clientCertPath).use { keyStoreInputStream ->
keyStore.load(keyStoreInputStream, keyStorePassword)
}
val caCertPath = "/storage/sdcard0/Android/data/in.sminnovations.hpostesting.quality/files/NATS/clientCertificate/client-cert.pem"
val caCert = FileInputStream(caCertPath).use { inputStream ->
val certificateFactory = CertificateFactory.getInstance("X.509")
certificateFactory.generateCertificate(inputStream)
}
val trustStore = KeyStore.getInstance(KeyStore.getDefaultType()).apply {
load(null, null) // Initialize the keystore
setCertificateEntry("caCert", caCert) // Add the CA certificate
}
// Initialize key manager factory
val kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm())
kmf.init(keyStore, keyStorePassword)
// Initialize trust manager factory
val tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm())
tmf.init(trustStore)
// Initialize SSLContext
val sslContext = SSLContext.getInstance("TLS")
sslContext.init(kmf.keyManagers, tmf.trustManagers, SecureRandom())
return sslContext
}
@RequiresApi(Build.VERSION_CODES.O)
fun connect() {
Log.d(TAG, "TRY TO CONNECT")
Thread {
val seedString = "SUAEB5PUWNS6C2HUV3MFI6HUDEAPGPFPBHMI73NHIQATCDD2BWALVEZXZ4"
val seedString = "SUAJH5VMO6GDRQA6QTXCLIJMS74IWIUTU3NJVYIOTZF2LBWUPD77DA2ZEA"
Log.e("seedString",seedString)
val seedBytes = seedString.toCharArray()
val theNKey = NKey.fromSeed(seedBytes) // really should load from somewhere
val options = Options.Builder()
.server("nats://192.168.10.117:4222")
.server("nats://nanodgx.in:4222")
.sslContext(SSLUtils.createOpenTLSContext())
.authHandler(object : AuthHandler {
override fun getID(): CharArray? {
return try {
@@ -68,6 +124,11 @@ class NatsManager(datacollector: DashboardActivity) {
connect = true
datacollector.setConnect(true)
if (nc?.status == Connection.Status.CONNECTED) {
Log.e("NATSCONNECTION", "NATS is successfully connected.")
} else {
Log.e("NATSCONNECTION", "NATS is not connected. Current status: ${nc?.status}")
}
nc?.publish(
"server.hpos.HCV-000-3001.ping",
@@ -82,9 +143,10 @@ class NatsManager(datacollector: DashboardActivity) {
println("PRITIMOI SARKAR $msg")
}
d?.subscribe("device.hpos.HCV-000-3001.update") { msg ->
val response = String(msg.data, StandardCharsets.UTF_8)
datacollector. setResponse(response)
datacollector.setResponse(response)
println("Message received (up to 100 times): $response")
}
@@ -115,7 +177,7 @@ class NatsManager(datacollector: DashboardActivity) {
} catch (exp: Exception) {
println(exp.printStackTrace())
connect = false
datacollector.setConnect(true)
datacollector.setConnect(false)
}
}.start()

View File

@@ -3,14 +3,19 @@ package com.example.hpostesting.presentation.assurance
import android.content.Context
import android.content.SharedPreferences
import android.os.Bundle
import android.util.Log
import androidx.appcompat.app.AppCompatActivity
import com.example.hpostesting.presentation.NatsManager
import com.example.hpostesting.presentation.dashboard.IDataCollector
import dagger.hilt.android.AndroidEntryPoint
import `in`.sminnovations.hpostesting.databinding.ActivityAssuranceControlsBinding
@AndroidEntryPoint
class AssuranceControlsActivity: AppCompatActivity() {
class AssuranceControlsActivity: AppCompatActivity(), IDataCollector {
lateinit var binding: ActivityAssuranceControlsBinding
lateinit var sharedPreference: SharedPreferences
lateinit var nats: NatsManager
var responses: String = ""
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@@ -24,5 +29,21 @@ class AssuranceControlsActivity: AppCompatActivity() {
.replace(binding.fgAssuranceControls.id, AssuranceControlsFragment())
.commit()
}
// nats = NatsManager(this)
// nats.connect()
// nats.pub("server.hpos.HCV-000-3001.ping", "THIS IS A TEST MSG")
}
override fun setConnect(connect: Boolean) {
if(connect){
Log.i("NATS Connection", connect.toString())
}
}
override fun setResponse(response: String) {
responses = responses+response+"\n"
println(responses)
}
}

View File

@@ -12,6 +12,7 @@ import android.view.ViewGroup
import android.widget.AdapterView
import android.widget.ArrayAdapter
import android.widget.Spinner
import android.widget.Toast
import androidx.fragment.app.Fragment
import com.example.hpostesting.data.DataHolder
import com.example.hpostesting.data.constant.Constants
@@ -30,6 +31,9 @@ class AssuranceControlsFragment: Fragment() {
binding = FragmentAssuranceControlsBinding.inflate(inflater, container, false)
sharedPreferences =
requireContext().getSharedPreferences("HEMOCUBE", Context.MODE_PRIVATE)
DataHolder.hemoCubeTestData!!.solution = ""
DataHolder.hemoCubeTestData!!.volume = ""
return binding.root
}
@@ -42,7 +46,7 @@ class AssuranceControlsFragment: Fragment() {
// binding.btnSubmit.visibility = View.GONE
val solutionSpinner: Spinner = binding.spinnerSolutions
val solutionOptions = arrayOf("Select solution", "Tartrazine", "AR")
val solutionOptions = arrayOf("Select solution", "Tartrazine", "Acid Red")
val solutionAdapter = ArrayAdapter(requireContext(), R.layout.simple_spinner_item, solutionOptions)
solutionAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
solutionSpinner.adapter = solutionAdapter
@@ -121,6 +125,13 @@ class AssuranceControlsFragment: Fragment() {
volumeSpinner.setSelection(volumePosition)
binding.btnSubmit.setOnClickListener {
val selectedSolution = DataHolder.hemoCubeTestData!!.solution
val selectedVolume = DataHolder.hemoCubeTestData!!.volume
if (selectedSolution == "Select solution" || selectedVolume == "Select volume") {
Toast.makeText(requireContext(), "Please select both solution and volume", Toast.LENGTH_SHORT).show()
return@setOnClickListener
}
DataHolder.hemoCubeTestData!!.quickCapture = true
val currentUnixTime = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
Instant.now().epochSecond

View File

@@ -145,18 +145,18 @@ class AutoDacActivity: AppCompatActivity() {
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
this, 0, Intent(Constants.HEMOCUBE_USB_PERMISSION), PendingIntent.FLAG_MUTABLE
)
} else {
mPendingIntent = PendingIntent.getBroadcast(
this,
0,
Intent(Constants.HOMOCUBE_USB_PERMISSION),
Intent(Constants.HEMOCUBE_USB_PERMISSION),
PendingIntent.FLAG_ONE_SHOT or PendingIntent.FLAG_IMMUTABLE
)
}
val filter = IntentFilter(Constants.HOMOCUBE_USB_PERMISSION)
val filter = IntentFilter(Constants.HEMOCUBE_USB_PERMISSION)
registerReceiver(broadcastReceiver, filter)
manager.requestPermission(device, mPendingIntent)
}

View File

@@ -12,11 +12,15 @@ 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.FragmentAutoDacBinding
import java.text.SimpleDateFormat
import java.util.Calendar
import java.util.Locale
class AutoDacFragment: Fragment() {
@@ -101,8 +105,6 @@ class AutoDacFragment: Fragment() {
HemoCubeCommands.AUTO_DAC_COMMAND,
object : UsbServiceListener {
override fun onUsbRead(data: ByteArray?) {
}
override fun onUsbError(e: Exception?) {
autoDacViewModel.progressBar.postValue(false)
@@ -141,22 +143,22 @@ class AutoDacFragment: Fragment() {
}
if (resultData.contains("#CC")) {
autoDacViewModel.addAutoDacDataToDb(
DiagnosticsData(
deviceId = sharedPreferences.getString(Constants.DEVICE_ID, "").toString(),
devicePassword = sharedPreferences.getString(Constants.DEVICE_PASSWORD_API, "").toString(),
deviceNatsToken = sharedPreferences.getString(Constants.NATS_TOKEN, "").toString(),
accessToken = sharedPreferences.getString(Constants.ACCESS_TOKEN, "").toString(),
deviceData = resultData,
runTime = SimpleDateFormat(
"yyyy-MM-dd HH:mm:ss", Locale.getDefault()
).format(Calendar.getInstance().time)
)
)
activity?.runOnUiThread {
binding.ivCheck.visibility = View.VISIBLE
}
}
if (resultData.contains("END") || fullReadOutput.contains("END")) {
// autoDacViewModel.addAutoDacDataToDb(
// DiagnosticsData(
// deviceId = sharedPreferences.getString(Constants.DEVICE_ID, "").toString(),
// deviceData = resultData,
// runTime = SimpleDateFormat(
// "yyyy-MM-dd HH:mm:ss", Locale.getDefault()
// ).format(Calendar.getInstance().time)
// )
// )
}
}
override fun onUsbError(e: Exception?) {
@@ -168,7 +170,6 @@ class AutoDacFragment: Fragment() {
}
}
fun parseData(inputData: List<String>): List<Pair<String, String>> {
val pattern = Regex("([A-Z]+)\\s(\\d+)")
val parsedData = mutableListOf<Pair<String, String>>()

View File

@@ -1,5 +1,6 @@
package com.example.hpostesting.presentation.buffercheck
import android.annotation.SuppressLint
import android.app.PendingIntent
import android.content.BroadcastReceiver
import android.content.ComponentName
@@ -124,21 +125,22 @@ open class HemocubeBufferCheckActivity : AppCompatActivity() {
}
@SuppressLint("MutableImplicitPendingIntent")
private fun requestUserPermission(manager: UsbManager, device: UsbDevice) {
val mPendingIntent: PendingIntent = if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.S) {
PendingIntent.getBroadcast(
this, 0, Intent(Constants.HOMOCUBE_USB_PERMISSION), PendingIntent.FLAG_MUTABLE
this, 0, Intent(Constants.HEMOCUBE_USB_PERMISSION), PendingIntent.FLAG_MUTABLE
)
} else {
PendingIntent.getBroadcast(
this,
0,
Intent(Constants.HOMOCUBE_USB_PERMISSION),
Intent(Constants.HEMOCUBE_USB_PERMISSION),
PendingIntent.FLAG_ONE_SHOT or PendingIntent.FLAG_IMMUTABLE
)
}
val filter = IntentFilter(Constants.HOMOCUBE_USB_PERMISSION)
val filter = IntentFilter(Constants.HEMOCUBE_USB_PERMISSION)
registerReceiver(broadcastReceiver, filter)
manager.requestPermission(device, mPendingIntent)
}

View File

@@ -144,18 +144,18 @@ class CalibrationActivity: AppCompatActivity() {
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
this, 0, Intent(Constants.HEMOCUBE_USB_PERMISSION), PendingIntent.FLAG_MUTABLE
)
} else {
PendingIntent.getBroadcast(
this,
0,
Intent(Constants.HOMOCUBE_USB_PERMISSION),
Intent(Constants.HEMOCUBE_USB_PERMISSION),
PendingIntent.FLAG_ONE_SHOT or PendingIntent.FLAG_IMMUTABLE
)
}
val filter = IntentFilter(Constants.HOMOCUBE_USB_PERMISSION)
val filter = IntentFilter(Constants.HEMOCUBE_USB_PERMISSION)
registerReceiver(broadcastReceiver, filter)
manager.requestPermission(device, mPendingIntent)
}

View File

@@ -156,9 +156,10 @@ class DashboardActivity : AppCompatActivity(), IDataCollector {
val file = File(getExternalFilesDir("Updates"), "update.apk")
file.setReadable(true, false) // Ensure the file is readable
val pInfo = baseContext.packageManager.getPackageInfo(baseContext.packageName, 0)
val uri: Uri = FileProvider.getUriForFile(
this,
"com.example.hpostesting.fileprovider",
"${pInfo}.fileprovider",
file
)

View File

@@ -1,6 +1,7 @@
package com.example.hpostesting.presentation.dashboard
import android.annotation.SuppressLint
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.content.SharedPreferences
@@ -14,12 +15,13 @@ import com.example.hpostesting.data.constant.Constants
import com.example.hpostesting.presentation.autodac.AutoDacActivity
import com.example.hpostesting.presentation.buffercheck.HemocubeBufferCheckActivity
import com.example.hpostesting.presentation.calibration.CalibrationActivity
import com.example.hpostesting.presentation.deviceprovision.DeviceProvisionActivity
import com.example.hpostesting.presentation.deviceinfo.DeviceActivity
import com.example.hpostesting.presentation.deviceprovision.DeviceProvisionActivity
import com.example.hpostesting.presentation.diagnostics.DiagnosticsActivity
import com.example.hpostesting.presentation.hemocube.HemoCubeViewModel
import `in`.sminnovations.hpostesting.databinding.FragmentGalleryBinding
class GalleryFragment : Fragment() {
private var _binding: FragmentGalleryBinding? = null
@@ -82,6 +84,19 @@ class GalleryFragment : Fragment() {
startActivity(Intent(requireContext(), DeviceActivity::class.java))
}
binding.btnFirefox.setOnClickListener {
val intent = Intent(Intent.ACTION_VIEW)
intent.component = ComponentName("org.mozilla.firefox", "org.mozilla.gecko.BrowserApp")
startActivity(intent)
}
binding.btnFiles.setOnClickListener {
val intent = Intent(Intent.ACTION_GET_CONTENT)
intent.type = "file/*"
startActivity(intent)
}
userid = sharedPreferences.getString(Constants.USER_ID, "").toString()
binding.tvSubtitle4.text = "Login ID : ${userid}"

View File

@@ -43,11 +43,19 @@ import com.google.firebase.perf.ktx.performance
import dagger.hilt.android.AndroidEntryPoint
import `in`.sminnovations.hpostesting.R
import `in`.sminnovations.hpostesting.databinding.FragmentHomeBinding
import okhttp3.ResponseBody
import org.json.JSONObject
import java.io.BufferedOutputStream
import java.io.File
import java.io.FileInputStream
import java.io.FileOutputStream
import java.net.URL
import java.text.SimpleDateFormat
import java.util.Calendar
import java.util.Date
import java.util.Locale
import java.util.zip.ZipEntry
import java.util.zip.ZipInputStream
@AndroidEntryPoint
class HomeFragment : Fragment() {
@@ -62,7 +70,6 @@ class HomeFragment : Fragment() {
private val homeViewModel: HemoCubeViewModel by activityViewModels()
private var isTokenAvailable = false
private lateinit var sharedPreference: SharedPreferences
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?,
@@ -203,6 +210,7 @@ class HomeFragment : Fragment() {
hemoCubeViewModel.deviceUpdate(createDeviceUpdateRequestData())
hemoCubeViewModel.uploadLogs()
hemoCubeViewModel.startPeriodicCheckUpdate()
hemoCubeViewModel.downloadClientCertificate()
}
}
} else {
@@ -212,6 +220,11 @@ class HomeFragment : Fragment() {
Toast.LENGTH_SHORT
).show()
}
hemoCubeViewModel.loginResponse.observe(viewLifecycleOwner) { response ->
when (response) {
is Result.Success -> {
@@ -266,6 +279,41 @@ class HomeFragment : Fragment() {
}
}
hemoCubeViewModel.downloadcertificate.observe(viewLifecycleOwner) { response ->
when (response) {
is Result.Success -> {
val url = response.data
val fileName = "nats_certificate.zip"
val downloadDirectory = "NATS"
val file = downloadFile(url, requireContext(), fileName, downloadDirectory)
Toast.makeText(requireContext(), "NATS certificate Downloaded", Toast.LENGTH_SHORT).show()
val unzipDirectoryPath = requireContext().getExternalFilesDir(null)?.absolutePath + "/$downloadDirectory"
unzip(file.absolutePath, unzipDirectoryPath)
Toast.makeText(requireContext(), "NATS certificate Extracted", Toast.LENGTH_SHORT).show()
}
is Result.Error -> {
response.exception.let { message ->
Toast.makeText(
activity,
"An error occurred in nats download: $message",
Toast.LENGTH_LONG
)
.show()
}
}
is Result.Loading -> {
}
else -> {}
}
}
hemoCubeViewModel.resultUpload.observe(viewLifecycleOwner) {
when (it) {
is Result.Success -> {
@@ -290,8 +338,11 @@ class HomeFragment : Fragment() {
else -> {}
}
}
}
private fun isTokenExpired(token: String): Boolean {
val parts = token.split("\\.".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
val decodedPayload = String(Base64.decode(parts[1], Base64.DEFAULT))
@@ -341,6 +392,60 @@ class HomeFragment : Fragment() {
)
}
private fun downloadFile(responseBody: ResponseBody, context: Context, fileName: String, downloadDirectory: String): File {
// Ensure the download directory exists
val fileDir = File(context.getExternalFilesDir(null), downloadDirectory)
if (!fileDir.exists()) {
fileDir.mkdirs()
}
val file = File(fileDir, fileName)
Log.d("Download", "Starting download to $file")
responseBody.byteStream().use { inputStream ->
FileOutputStream(file).use { outputStream ->
inputStream.copyTo(outputStream)
}
}
// After download
Log.d("Download", "Download completed to ${file.absolutePath}")
return file
}
private fun unzip(zipFilePath: String, destDirectory: String) {
val destDir = File(destDirectory)
if (!destDir.exists()) {
destDir.mkdir()
}
ZipInputStream(FileInputStream(zipFilePath)).use { zipIn ->
var entry: ZipEntry? = zipIn.nextEntry
while (entry != null) {
val filePath = destDirectory + File.separator + entry.name
if (!entry.isDirectory) {
extractFile(zipIn, filePath)
} else {
val dir = File(filePath)
dir.mkdir()
}
zipIn.closeEntry()
entry = zipIn.nextEntry
}
}
}
private fun extractFile(zipIn: ZipInputStream, filePath: String) {
BufferedOutputStream(FileOutputStream(filePath)).use { bos ->
val bytesIn = ByteArray(4096)
var read: Int
while (zipIn.read(bytesIn).also { read = it } != -1) {
bos.write(bytesIn, 0, read)
}
}
}
private fun setUserId() {
binding.btnSubmit.setOnClickListener {
val userId = binding.userId.text.toString()

View File

@@ -24,15 +24,11 @@ import androidx.core.view.get
import com.example.hpostesting.data.DataHolder
import com.example.hpostesting.data.constant.Constants
import com.example.hpostesting.data.constant.LanguageManager
import com.example.hpostesting.presentation.calibration.CalibrationFragment
import com.example.hpostesting.presentation.calibration.CalibrationViewModel
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.ActivityCalibrationBinding
import `in`.sminnovations.hpostesting.databinding.ActivityDeviceBinding
@Suppress("MemberVisibilityCanBePrivate")
@@ -148,18 +144,18 @@ class DeviceActivity : AppCompatActivity() {
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
this, 0, Intent(Constants.HEMOCUBE_USB_PERMISSION), PendingIntent.FLAG_MUTABLE
)
} else {
mPendingIntent = PendingIntent.getBroadcast(
this,
0,
Intent(Constants.HOMOCUBE_USB_PERMISSION),
Intent(Constants.HEMOCUBE_USB_PERMISSION),
PendingIntent.FLAG_ONE_SHOT or PendingIntent.FLAG_IMMUTABLE
)
}
val filter = IntentFilter(Constants.HOMOCUBE_USB_PERMISSION)
val filter = IntentFilter(Constants.HEMOCUBE_USB_PERMISSION)
registerReceiver(broadcastReceiver, filter)
manager.requestPermission(device, mPendingIntent)
}

View File

@@ -4,10 +4,10 @@ import android.annotation.SuppressLint
import android.content.Context
import android.content.SharedPreferences
import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.lifecycle.MutableLiveData
import com.example.hpostesting.data.constant.Constants
@@ -32,7 +32,7 @@ class DeviceFragment : Fragment() {
): View {
binding = FragmentDeviceBinding.inflate(inflater, container, false)
sharedPreferences =
requireContext().getSharedPreferences("PREFERENCE_NAME", Context.MODE_PRIVATE)
requireContext().getSharedPreferences("HEMOCUBE", Context.MODE_PRIVATE)
return binding.root
}

View File

@@ -1,10 +1,7 @@
package com.example.hpostesting.presentation.deviceinfo
import android.content.Context
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import com.example.hpostesting.data.NetworkStatusLiveData
import com.example.hpostesting.data.dao.HemoCubeDao
import com.example.hpostesting.data.model.patient.DeviceData
import com.example.hpostesting.data.repository.Repository
@@ -14,16 +11,15 @@ import javax.inject.Inject
class DeviceViewModel @Inject constructor(
private val hemoCubeDao: HemoCubeDao,
private val repository: Repository,
context: Context
) : ViewModel() {
var isServiceConnected = false
val progressBar = MutableLiveData(false)
val messages = MutableLiveData<String>()
private val _networkStatusLiveData = NetworkStatusLiveData(context)
val allUserData = hemoCubeDao.getAll()
// private val _networkStatusLiveData = NetworkStatusLiveData(context)
// val allUserData = hemoCubeDao.getAll()
val deviceData = MutableLiveData<DeviceData?>()
val networkStatusLiveData: LiveData<Boolean>
get() = _networkStatusLiveData
// val networkStatusLiveData: LiveData<Boolean>
// get() = _networkStatusLiveData
val fireBaseUpload = MutableLiveData<String>()
}

View File

@@ -144,18 +144,18 @@ class DeviceProvisionActivity : AppCompatActivity() {
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
this, 0, Intent(Constants.HEMOCUBE_USB_PERMISSION), PendingIntent.FLAG_MUTABLE
)
} else {
PendingIntent.getBroadcast(
this,
0,
Intent(Constants.HOMOCUBE_USB_PERMISSION),
Intent(Constants.HEMOCUBE_USB_PERMISSION),
PendingIntent.FLAG_ONE_SHOT or PendingIntent.FLAG_IMMUTABLE
)
}
val filter = IntentFilter(Constants.HOMOCUBE_USB_PERMISSION)
val filter = IntentFilter(Constants.HEMOCUBE_USB_PERMISSION)
registerReceiver(broadcastReceiver, filter)
manager.requestPermission(device, mPendingIntent)
}

View File

@@ -4,6 +4,7 @@ import android.content.Context
import android.content.Intent
import android.content.SharedPreferences
import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
@@ -74,6 +75,11 @@ class DeviceProvisionFragment : Fragment() {
Constants.NATS_TOKEN,
response.data.data?.device?.deviceUser?.natsToken
)
response.data.data?.device?.deviceUser?.natsToken?.let {
Log.e("natstoken",
it
)
}
putString(
Constants.NATS_TOKEN_EXPIRE_DATE,
response.data.data?.device?.deviceUser?.natsTokenExpiry
@@ -114,7 +120,6 @@ class DeviceProvisionFragment : Fragment() {
}
}
private fun getDeviceId() {
(activity as DeviceProvisionActivity).mService.sendAndListenToHemoCube(
HemoCubeCommands.DEVICE_CONFIGURATION_COMMAND,
@@ -146,14 +151,19 @@ class DeviceProvisionFragment : Fragment() {
}
}
fun extractV2HardwareId(input: String): String? {
val pattern = Regex("SNS\\s*(.*?)\\s*SNE")
val matchResult: MatchResult? = pattern.find(input)
return matchResult?.groups?.get(1)?.value
}
private fun handleUsbData() {
when {
resultData.contains("SNE") -> {
val pattern = Regex("HPP1-\\d{4}")
val matchResult = pattern.find(resultData)
val hardwareId = matchResult?.value
val hardwareId = extractV2HardwareId(resultData)
if (hardwareId.toString().length == 9) {
if (!hardwareId.isNullOrBlank()) {
with(sharedPreferences.edit()) {
putString(Constants.DEVICE_ID, hardwareId)
apply()

View File

@@ -23,8 +23,6 @@ import androidx.core.view.get
import com.example.hpostesting.data.DataHolder
import com.example.hpostesting.data.constant.Constants
import com.example.hpostesting.data.constant.LanguageManager
import com.example.hpostesting.presentation.hemocube.HemoCubeFragment
import com.example.hpostesting.presentation.hemocube.HemoCubeViewModel
import com.example.hpostesting.presentation.testRight.UsbService
import com.hoho.android.usbserial.driver.UsbSerialDriver
import com.hoho.android.usbserial.driver.UsbSerialProber
@@ -148,18 +146,18 @@ class DiagnosticsActivity : AppCompatActivity() {
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
this, 0, Intent(Constants.HEMOCUBE_USB_PERMISSION), PendingIntent.FLAG_MUTABLE
)
} else {
mPendingIntent = PendingIntent.getBroadcast(
this,
0,
Intent(Constants.HOMOCUBE_USB_PERMISSION),
Intent(Constants.HEMOCUBE_USB_PERMISSION),
PendingIntent.FLAG_ONE_SHOT or PendingIntent.FLAG_IMMUTABLE
)
}
val filter = IntentFilter(Constants.HOMOCUBE_USB_PERMISSION)
val filter = IntentFilter(Constants.HEMOCUBE_USB_PERMISSION)
registerReceiver(broadcastReceiver, filter)
manager.requestPermission(device, mPendingIntent)
}

View File

@@ -148,6 +148,9 @@ class DiagnosticsFragment : Fragment() {
if (resultData.contains("END") || fullReadOutput.contains("END")) {
diagnosticsViewModel.addDiagnosticsDataToDb(DiagnosticsData(
deviceId = sharedPreferences.getString(Constants.DEVICE_ID, "").toString(),
devicePassword = sharedPreferences.getString(Constants.DEVICE_PASSWORD_API, "").toString(),
deviceNatsToken = sharedPreferences.getString(Constants.NATS_TOKEN, "").toString(),
accessToken = sharedPreferences.getString(Constants.ACCESS_TOKEN, "").toString(),
deviceData = resultData,
runTime = SimpleDateFormat(
"yyyy-MM-dd HH:mm:ss", Locale.getDefault()

View File

@@ -123,7 +123,7 @@ class HemoCubeFragment : Fragment() {
}
binding.btnPlacebuffer.setOnClickListener {
if (isBufferValueAvailable()) {
if (isBufferValueAvailable() || !Constants.BLANK_EVERY_TEST) {
showBufferAlertDialog()
} else {
activity?.runOnUiThread {
@@ -153,7 +153,7 @@ class HemoCubeFragment : Fragment() {
when (it) {
is Result.Success -> {
uploadedToMolbio = true
if (Constants.MOLBIO_INTERGATION) {
if (Constants.MOLBIO_INTEGRATION) {
it.data.data?.get(0)?.rawData?.let { it1 ->
hemoCubeViewModel.updateMolbioFlag(
it1._id
@@ -188,23 +188,6 @@ class HemoCubeFragment : Fragment() {
binding.progressBar.visibility = View.GONE
}
// hemoCubeViewModel.fireBaseUpload.observe(viewLifecycleOwner) { result ->
// if (result == "Success") {
// showToast(R.string.test_upload)
// handleReadingFinish()
// }
// if (result == "Local") {
// showToast(R.string.internt_not_local)
// handleReadingFinish()
// }
// if (result == "Error") {
// showToast(R.string.error_local)
// startActivity(Intent(requireActivity(), DashboardActivity::class.java))
// }
//
// binding.progressBar.visibility = View.GONE
// }
hemoCubeViewModel.getDeviceData(sharedPreferences.getString(Constants.USER_ID, ""))
hemoCubeViewModel.deviceData.observe(viewLifecycleOwner) {
@@ -235,6 +218,10 @@ class HemoCubeFragment : Fragment() {
private fun handleReadingFinish() {
if (allReadingsComplete(repeatReadingCount, readingsPerSample) && uploadedToCloud) {
if (validationError) {
hemoCubeViewModel.messages.postValue("Error")
return
}
activity?.runOnUiThread {
binding.btnSubmit.visibility = View.GONE
val i = Intent(
@@ -387,6 +374,19 @@ class HemoCubeFragment : Fragment() {
processV1HardwareId(resultData)
}
(resultData.contains("#LS") && this.testStatusCode < TestStatus.FIRST_EMPTY_AIR_READING_STARTED.code) -> {
// air reading 1
this.testStatusCode = TestStatus.FIRST_EMPTY_AIR_READING_STARTED.code
hemoCubeViewModel.messages.postValue("Air reading started")
}
(resultData.contains("#LC") && this.testStatusCode < TestStatus.FIRST_EMPTY_AIR_READING_COMPLETED.code) -> {
// air reading 1, send command to print
this.testStatusCode = TestStatus.FIRST_EMPTY_AIR_READING_COMPLETED.code
hemoCubeViewModel.messages.postValue("Air reading completed")
fetchResult()
}
resultData.contains("#BS") && this.testStatusCode < TestStatus.BUFFER_STARTED.code -> {
hemoCubeViewModel.messages.postValue(getString(R.string.buffer_started))
this.testStatusCode = TestStatus.BUFFER_STARTED.code
@@ -455,10 +455,59 @@ class HemoCubeFragment : Fragment() {
fetchResult()
}
(resultData.contains("#LS") && this.testStatusCode < TestStatus.SECOND_EMPTY_AIR_READING_STARTED.code) -> {
// air reading 2
this.testStatusCode = TestStatus.SECOND_EMPTY_AIR_READING_STARTED.code
hemoCubeViewModel.messages.postValue("Air reading started")
}
(resultData.contains("#LC") && this.testStatusCode < TestStatus.SECOND_EMPTY_AIR_READING_COMPLETED.code) -> {
// air reading 2, print values
this.testStatusCode = TestStatus.SECOND_EMPTY_AIR_READING_COMPLETED.code
hemoCubeViewModel.messages.postValue("Air reading completed")
fetchResult()
}
resultData.contains("REND") && this.testStatusCode < TestStatus.SAMPLE_PRINT_COMPLETED.code -> {
handleSampleCompleted()
}
currentResultData.contains("REND")
&& this.testStatusCode >= TestStatus.FIRST_EMPTY_AIR_READING_COMPLETED.code
&& this.testStatusCode < TestStatus.FIRST_EMPTY_AIR_READING_PRINT_COMPLETED.code -> {
this.testStatusCode = TestStatus.FIRST_GAIN_PRINT_COMPLETED.code
hemoCubeViewModel.messages.postValue("First air reading completed")
val resultLines = currentResultData.split("\\s+(?=LB|LS)".toRegex())
DataHolder.hemoCubeTestData?.apply {
led1Air1 = resultLines[5].split(' ')[1].trim().toDoubleOrNull()!!
led2Air1 = resultLines[6].split(' ')[1].trim().toDoubleOrNull()!!
led3Air1 = resultLines[7].split(' ')[1].trim().toDoubleOrNull()!!
led4Air1 = resultLines[8].split(' ')[1].split('\r')[0].trim().toDoubleOrNull()!!
}
startBufferProcess()
currentResultData = ""
}
currentResultData.contains("REND")
&& this.testStatusCode >= TestStatus.SECOND_EMPTY_AIR_READING_COMPLETED.code
&& this.testStatusCode < TestStatus.SECOND_EMPTY_AIR_PRINT_COMPLETED.code -> {
this.testStatusCode = TestStatus.SECOND_EMPTY_AIR_PRINT_COMPLETED.code
hemoCubeViewModel.messages.postValue("Second air reading completed")
val resultLines = currentResultData.split("\\s+(?=LB|LS)".toRegex())
DataHolder.hemoCubeTestData?.apply {
led1Air2 = resultLines[5].split(' ')[1].trim().toDoubleOrNull()!!
led2Air2 = resultLines[6].split(' ')[1].trim().toDoubleOrNull()!!
led3Air2 = resultLines[7].split(' ')[1].trim().toDoubleOrNull()!!
led4Air2 = resultLines[8].split(' ')[1].split('\r')[0].trim().toDoubleOrNull()!!
}
sendSecondGainCommand()
currentResultData = ""
}
currentResultData.contains("REND")
&& this.testStatusCode >= TestStatus.FIRST_GAIN_COMPLETED.code
&& this.testStatusCode < TestStatus.FIRST_GAIN_PRINT_COMPLETED.code -> {
@@ -594,10 +643,10 @@ class HemoCubeFragment : Fragment() {
if (!Constants.DEVICE_CONFIGURATION.containsKey(deviceHardwareId)) {
assignDefaultDevice(resultData)
testState.allErrorMessages += "Calibration configuration for this device id is not found\n"
// testState.allErrorMessages += "Calibration configuration for this device id is not found\n"
}
if (!Constants.BUFFER_INTENSITY_THRESHOLDS.containsKey(deviceHardwareId)) {
testState.allErrorMessages += "ADC thresholds for this device id are not found\n"
// testState.allErrorMessages += "ADC thresholds for this device id are not found\n"
}
}
@@ -679,7 +728,7 @@ class HemoCubeFragment : Fragment() {
val led2Average = log10(led2BufferForDevice.div(led2SampleForDevice))
val led3Average = log10(led3BufferForDevice.div(led3SampleForDevice))
val led4Average = log10(led4BufferForDevice.div(led4SampleForDevice))
val deviceRatio = led4Average / led1Average
val deviceRatio = led2Average / led1Average
if (led1BufferForDevice < Constants.BUFFER_INTENSITY_THRESHOLDS[deviceHardwareId]?.get(0)
?.get(0)!!
@@ -693,10 +742,10 @@ class HemoCubeFragment : Fragment() {
3
)?.get(0)!!
) {
validationError = true
testState.allErrorMessages += "Error: Invalid Test. Improper buffer reading (low)"
// validationError = true
// testState.allErrorMessages += "Error: Invalid Test. Improper buffer reading (low)"
activity?.runOnUiThread {
binding.errorMessage.text = getString(R.string.error_improper_buffer_low)
// binding.errorMessage.text = getString(R.string.error_improper_buffer_low)
// binding.errorMessage.visibility = View.VISIBLE
}
}
@@ -713,12 +762,12 @@ class HemoCubeFragment : Fragment() {
3
)?.get(1)!!
) {
validationError = true
testState.allErrorMessages += "Error: Invalid Test. Improper buffer reading (high)" + "\n"
// validationError = true
// testState.allErrorMessages += "Error: Invalid Test. Improper buffer reading (high)" + "\n"
activity?.runOnUiThread {
binding.errorMessage.text =
// binding.errorMessage.text =
getString(R.string.error_improper_buffer_high)
binding.errorMessage.visibility = View.VISIBLE
// binding.errorMessage.visibility = View.VISIBLE
}
}
@@ -756,40 +805,41 @@ class HemoCubeFragment : Fragment() {
calculatedPredictedDenovixRatio = fittedAbs3.div(fittedAbs1)
val slope = (led1Average - led2Average) / (435 - 415)
val calculatedSlopeRatio = abs(led3Average / slope)
val slope = (led4Average - led1Average) / (431-411)
val calculatedSlopeRatio = abs(led2Average / slope)
val slopeClass = slopeRatioClassification(calculatedSlopeRatio)
if (fittedAbs1 <= fittedAbs2) {
validationError = true
testState.allErrorMessages += "Error: Invalid Test. Problem with de-oxygenation" + "\n"
// validationError = true
// testState.allErrorMessages += "Error: Invalid Test. Problem with de-oxygenation" + "\n"
activity?.runOnUiThread {
binding.errorMessage.text = getString(R.string.error_invalid_test)
// binding.errorMessage.text = getString(R.string.error_invalid_test)
// binding.errorMessage.visibility = View.VISIBLE
}
}
if (fittedAbs1 < 0 || fittedAbs2 < 0 || fittedAbs3 < 0 || fittedAbs4 < 0) {
validationError = true
// validationError = true
activity?.runOnUiThread {
binding.errorMessage.text = getString(R.string.error_negative_abs)
// binding.errorMessage.visibility = View.VISIBLE
}
}
if ((led1Average < 0.7 || led2Average < 0.7 || led3Average < 0.7 || led4Average < 0.7) && calculatedSlopeRatio > 35.0) {
var absorbanceLowerLimit = 0.0
if (led1Average < absorbanceLowerLimit || led2Average < absorbanceLowerLimit || led3Average < absorbanceLowerLimit || led4Average < absorbanceLowerLimit) {
validationError = true
activity?.runOnUiThread {
binding.errorMessage.text = "Severely Low Hb. Repeat test with 12 ul in 2 ml Buffer"
binding.errorMessage.text = "Invalid"
binding.errorMessage.visibility = View.VISIBLE
}
}
if (fittedAbs3 < 0.1) {
validationError = true
testState.allErrorMessages += "Error: Low Hb. Repeat test" + "\n"
// validationError = true
// testState.allErrorMessages += "Error: Low Hb. Repeat test" + "\n"
activity?.runOnUiThread {
binding.errorMessage.text = "Error: Low Hb. Repeat test"
// binding.errorMessage.text = "Error: Low Hb. Repeat test"
// binding.errorMessage.visibility = View.VISIBLE
}
}
@@ -824,8 +874,8 @@ class HemoCubeFragment : Fragment() {
this.prdClassification = absorbanceBasedClassification(predictedDenovixRatio)
this.deviceRatioClass = deviceRatioClassification(deviceRatio)
this.slopeRatioClass = slopeClass
this.classificationResult = deviceRatioClass //findResult(calculatedRatio)
hemoCubeViewModel.messages.postValue("${this.deviceRatioClass} ${if (led4Average < 0.17) " - Low Hb" else ""}\n")
this.classificationResult = deviceRatioClass
hemoCubeViewModel.messages.postValue("${this.classificationResult} \n Device Ratio: ${"%.3f".format(this.deviceRatio)}")
if (DataHolder.hemoCubeTestData?.testType == "HB")
hemoCubeViewModel.messages.postValue("Hb: $calculatedHb4")
this.errorMessages = testState.allErrorMessages
@@ -859,50 +909,36 @@ class HemoCubeFragment : Fragment() {
}
}
fun findResult(calculatedRatio: Double?): String {
fun findResultWithAdditionalMethods(deviceRatio: Double?, deviceRatioClass: String?, slopeRatio: Double?): String {
try {
hemoCubeViewModel.messages.postValue("result classification")
if (calculatedRatio != null) {
if (calculatedRatio < 0.05)
return getString(R.string.error_repeat_test_higher_volume)
if (calculatedRatio in 0.05..0.155) {
return getString(R.string.normal)
// hemoCubeViewModel.messages.postValue("post classification checks")
if (deviceRatio != null) {
if (slopeRatio != null) {
if (deviceRatioClass == "Normal" && slopeRatio > 45.0)
return "Negative Borderline, Repeat Test"
}
if (calculatedRatio in 0.155..0.175)
return getString(R.string.negative_borderline)
if (calculatedRatio in 0.175..0.22)
return getString(R.string.sickle_cell_trait)
if (calculatedRatio in 0.22..0.25)
return getString(R.string.positive_for_sickle_cell)
if (calculatedRatio in 0.25..0.35)
return getString(R.string.sickle_cell_disease)
if (calculatedRatio > 0.35)
return getString(R.string.error_repeat_test_lower_volume)
} else {
return getString(R.string.invalid)
}
} catch (e: Exception) {
showToast(R.string.error_classification)
Firebase.crashlytics.recordException(e)
return getString(R.string.error)
handleException(e)
return "Error"
}
return getString(R.string.invalid)
return deviceRatioClass.toString()
}
fun deviceRatioClassification(ratio: Double?): String {
try {
if (ratio != null) {
if (ratio in 0.1..0.29) {
if (ratio in 0.016..0.22) {
// setSubtitleTextColor(R.color.green_2)
return "Normal"
}
if (ratio in 0.29..0.32)
return "Negative Borderline, Repeat Test"
if (ratio in 0.32..0.35)
if (ratio in 0.22..0.24)
return "Negative Borderline"
if (ratio in 0.24..0.32)
return "Sickle Cell Trait"
if (ratio in 0.35..0.38)
if (ratio in 0.32..0.37)
return "Positive for Sickle Cell. HPLC for Confirmation"
if (ratio in 0.38..0.5)
if (ratio in 0.37..0.56)
return "Sickle Cell Disease"
} else {
return "Invalid"

View File

@@ -79,6 +79,7 @@ class HemoCubeViewModel @Inject constructor(
val checkUpdate = MutableLiveData<Result<CheckUpdateResponse>>()
val deviceUpdate = MutableLiveData<Result<ResponseBody>>()
val downloadcertificate = MutableLiveData<Result<ResponseBody>>()
val uploadLogs = MutableLiveData<Result<UploadLogsResponse>?>()
@@ -141,6 +142,9 @@ class HemoCubeViewModel @Inject constructor(
}
}
fun checkUpdate(checkUpdateRequest: CheckUpdateRequest) = viewModelScope.launch {
checkUpdate.postValue(Result.Loading())
repository.checkUpdate(checkUpdateRequest).let {
@@ -155,6 +159,17 @@ class HemoCubeViewModel @Inject constructor(
}
}
fun downloadClientCertificate() = viewModelScope.launch {
downloadcertificate.postValue(Result.Loading())
repository.downloadClientCertificate().let {
downloadcertificate.postValue(it)
}
}
fun startPeriodicCheckUpdate() {
val periodicRequest = PeriodicWorkRequestBuilder<CheckUpdateWorker>(
repeatInterval = 1, repeatIntervalTimeUnit = TimeUnit.MINUTES

View File

@@ -11,12 +11,14 @@ import android.content.ServiceConnection
import android.hardware.usb.UsbDevice
import android.hardware.usb.UsbDeviceConnection
import android.hardware.usb.UsbManager
import android.os.Build
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.annotation.RequiresApi
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat
import androidx.core.view.get
@@ -43,6 +45,7 @@ open class HemocubeActivity : AppCompatActivity() {
private val TAG = "HemoCube"
private val broadcastReceiver = object : BroadcastReceiver() {
@RequiresApi(Build.VERSION_CODES.O)
override fun onReceive(context: Context, intent: Intent) {
synchronized(this) {
@@ -81,6 +84,7 @@ open class HemocubeActivity : AppCompatActivity() {
}
@RequiresApi(Build.VERSION_CODES.O)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityHemocubeBinding.inflate(layoutInflater)
@@ -105,6 +109,7 @@ open class HemocubeActivity : AppCompatActivity() {
}
}
@RequiresApi(Build.VERSION_CODES.O)
open fun connectUsb(permissionGranted: Boolean) {
Log.d(TAG, "connectUsb() called, permission variable = $permissionGranted")
val manager = getSystemService(Context.USB_SERVICE) as UsbManager
@@ -125,32 +130,40 @@ open class HemocubeActivity : AppCompatActivity() {
}
@SuppressLint("MutableImplicitPendingIntent")
@RequiresApi(Build.VERSION_CODES.O)
@SuppressLint("UnspecifiedImmutableFlag")
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),
val pendingIntentFlags = when {
Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
PendingIntent.FLAG_IMMUTABLE
}
else -> {
PendingIntent.FLAG_ONE_SHOT or PendingIntent.FLAG_IMMUTABLE
)
}
}
val filter = IntentFilter(Constants.HOMOCUBE_USB_PERMISSION)
registerReceiver(broadcastReceiver, filter)
mPendingIntent = PendingIntent.getBroadcast(
this,
0,
Intent(Constants.HEMOCUBE_USB_PERMISSION),
pendingIntentFlags
)
val filter = IntentFilter(Constants.HEMOCUBE_USB_PERMISSION)
registerReceiver(broadcastReceiver, filter, RECEIVER_EXPORTED)
// Request permission
manager.requestPermission(device, mPendingIntent)
}
fun setupService() {
val intent = Intent(this, UsbService::class.java)
bindService(intent, connection, Context.BIND_AUTO_CREATE)
}
@RequiresApi(Build.VERSION_CODES.O)
open fun reconnectDevice() {
mService.disconnect()
unbindService(connection)

View File

@@ -0,0 +1,199 @@
package com.example.hpostesting.presentation.trueheme
import android.annotation.SuppressLint
import android.app.PendingIntent
import android.content.BroadcastReceiver
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.content.ServiceConnection
import android.hardware.usb.UsbDevice
import android.hardware.usb.UsbDeviceConnection
import android.hardware.usb.UsbManager
import android.os.Bundle
import android.os.IBinder
import android.util.Log
import android.view.Menu
import android.widget.Toast
import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat
import androidx.core.view.get
import com.example.hpostesting.data.DataHolder
import com.example.hpostesting.data.constant.Constants
import com.example.hpostesting.data.constant.LanguageManager
import com.example.hpostesting.presentation.hemocube.HemoCubeFragment
import com.example.hpostesting.presentation.hemocube.HemoCubeViewModel
import com.example.hpostesting.presentation.testRight.UsbService
import com.hoho.android.usbserial.driver.UsbSerialDriver
import com.hoho.android.usbserial.driver.UsbSerialProber
import dagger.hilt.android.AndroidEntryPoint
import `in`.sminnovations.hpostesting.R
import `in`.sminnovations.hpostesting.databinding.ActivityHemocubeBinding
@AndroidEntryPoint
class TrueHemeActivity : AppCompatActivity() {
private lateinit var binding: ActivityHemocubeBinding
private val viewModel by viewModels<HemoCubeViewModel>()
private var myMenu: Menu? = null
private lateinit var mDriver: UsbSerialDriver
private var mConnection: UsbDeviceConnection? = null
lateinit var mService: UsbService
private val TAG = "HemoCube"
private val broadcastReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
synchronized(this) {
val device: UsbDevice? = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE)
if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) {
device?.apply {
connectUsb(true)
DataHolder.usbConnected.postValue(true)
}
} else {
onErrorReported("permission denied for device")
DataHolder.usbConnected.postValue(true)
}
}
}
}
private val connection = object : ServiceConnection {
override fun onServiceConnected(className: ComponentName, service: IBinder) {
val binder = service as UsbService.UsbServiceBinder
mService = binder.getService()
viewModel.isServiceConnected = true
mConnection.let { mService.connect(mDriver, mConnection!!) }
moveToNext()
}
override fun onServiceDisconnected(arg0: ComponentName) {
viewModel.isServiceConnected = false
}
}
override fun attachBaseContext(newBase: Context?) {
val languageCode = LanguageManager.getSavedLanguage(newBase!!)
LanguageManager.setLocale(newBase, languageCode)
super.attachBaseContext(newBase)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityHemocubeBinding.inflate(layoutInflater)
setContentView(binding.root)
setSupportActionBar(binding.myToolbar)
supportActionBar?.setDisplayHomeAsUpEnabled(true)
setupListener()
connectUsb(false)
}
private fun setupListener() {
DataHolder.usbConnected.observe(this) {
Log.d("USB OBSERVE", "HemoCube called -> $it")
if (it) {
myMenu?.get(0)?.icon =
ContextCompat.getDrawable(this, R.drawable.ic_baseline_usb_24)
} else {
myMenu?.get(0)?.icon =
ContextCompat.getDrawable(this, R.drawable.ic_baseline_usb_off_24)
Toast.makeText(this, "Device is Disconnected", Toast.LENGTH_SHORT).show()
}
}
}
open fun connectUsb(permissionGranted: Boolean) {
Log.d(TAG, "connectUsb() called, permission variable = $permissionGranted")
val manager = getSystemService(Context.USB_SERVICE) as UsbManager
val availableDrivers = UsbSerialProber.getDefaultProber().findAllDrivers(manager)
if (availableDrivers.isEmpty()) {
onErrorReported("No Device is Connected")
} else {
mDriver = availableDrivers[0]
mConnection = manager.openDevice(mDriver.device)
if (mConnection == null) {
requestUserPermission(manager, mDriver.device)
} else {
setupService()
}
}
}
@SuppressLint("MutableImplicitPendingIntent", "UnspecifiedRegisterReceiverFlag")
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.HEMOCUBE_USB_PERMISSION), PendingIntent.FLAG_MUTABLE
)
} else {
mPendingIntent = PendingIntent.getBroadcast(
this,
0,
Intent(Constants.HEMOCUBE_USB_PERMISSION),
PendingIntent.FLAG_ONE_SHOT or PendingIntent.FLAG_IMMUTABLE
)
}
val filter = IntentFilter(Constants.HEMOCUBE_USB_PERMISSION)
registerReceiver(broadcastReceiver, filter)
manager.requestPermission(device, mPendingIntent)
}
fun setupService() {
val intent = Intent(this, UsbService::class.java)
bindService(intent, connection, Context.BIND_AUTO_CREATE)
}
open fun reconnectDevice() {
mService.disconnect()
unbindService(connection)
viewModel.isServiceConnected = false
connectUsb(true)
}
private fun moveToNext() {
if (supportFragmentManager.isDestroyed) return
supportFragmentManager.beginTransaction().replace(binding.fghemocube.id, HemoCubeFragment())
.commit()
}
override fun onBackPressed() {
val fragment = supportFragmentManager.findFragmentById(R.id.fghemocube)
if (fragment is HemoCubeFragment) {
fragment.handleBackButtonPress()
} else {
super.onBackPressed()
}
}
fun onErrorReported(msg: String) {
Toast.makeText(this, msg, Toast.LENGTH_SHORT).show()
if (!isFinishing) onBackPressed()
}
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
menuInflater.inflate(R.menu.my_menu, menu)
return true
}
override fun onDestroy() {
super.onDestroy()
if (viewModel.isServiceConnected) {
mService.disconnect()
unbindService(connection)
viewModel.isServiceConnected = false
}
}
}

View File

@@ -0,0 +1,533 @@
package com.example.hpostesting.presentation.trueheme
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.os.BatteryManager
import android.util.Log
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import androidx.work.ExistingPeriodicWorkPolicy
import androidx.work.PeriodicWorkRequestBuilder
import androidx.work.WorkManager
import com.example.hpostesting.data.CsvWriter
import com.example.hpostesting.data.DataHolder
import com.example.hpostesting.data.NetworkStatusLiveData
import com.example.hpostesting.data.Result
import com.example.hpostesting.data.constant.Constants
import com.example.hpostesting.data.dao.HemoCubeBufferDao
import com.example.hpostesting.data.dao.HemoCubeDao
import com.example.hpostesting.data.datasource.LocalFileDataSource
import com.example.hpostesting.data.model.Response
import com.example.hpostesting.data.model.log.UploadLogsResponse
import com.example.hpostesting.data.model.login.LoginRequest
import com.example.hpostesting.data.model.login.LoginResponse
import com.example.hpostesting.data.model.molbioresult.MolbioV2Result
import com.example.hpostesting.data.model.molbioresult.MolbioV2ResultRequest
import com.example.hpostesting.data.model.molbioresult.MolbioV2ResultResponse
import com.example.hpostesting.data.model.patient.BufferCheckData
import com.example.hpostesting.data.model.patient.DeviceData
import com.example.hpostesting.data.model.patient.HemoCubeTestData
import com.example.hpostesting.data.model.patient.toHemoCubeTestData
import com.example.hpostesting.data.model.updates.CheckUpdateRequest
import com.example.hpostesting.data.model.updates.CheckUpdateResponse
import com.example.hpostesting.data.model.updates.DeviceUpdateRequest
import com.example.hpostesting.data.repository.Repository
import com.example.hpostesting.domain.CheckUpdateWorker
import com.example.hpostesting.domain.LogFileManager
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.launch
import okhttp3.MediaType.Companion.toMediaTypeOrNull
import okhttp3.MultipartBody
import okhttp3.RequestBody.Companion.asRequestBody
import okhttp3.ResponseBody
import java.text.SimpleDateFormat
import java.util.Calendar
import java.util.Locale
import java.util.concurrent.TimeUnit
import javax.inject.Inject
@Suppress("MemberVisibilityCanBePrivate")
@HiltViewModel
class TrueHemeViewModel @Inject constructor(
private val hemoCubeDao: HemoCubeDao,
private val hemoCubeBufferDao: HemoCubeBufferDao,
private val repository: Repository,
private val logFileManager: LogFileManager,
private val localFileDataSource: LocalFileDataSource,
context: Context,
) : ViewModel() {
var isServiceConnected = false
val progressBar = MutableLiveData(false)
private val testDetails = DataHolder.selectedTest?.toHemoCubeTestData()
val messages = MutableLiveData<String>()
private val sharedPreference =
context.getSharedPreferences("HEMOCUBE", Context.MODE_PRIVATE)
private val workManager = WorkManager.getInstance(context)
// init {
// startPeriodicCheckUpdate()
// }
val loginResponse = MutableLiveData<Result<LoginResponse>>()
val resultUpload = MutableLiveData<Result<MolbioV2ResultResponse>>()
val checkUpdate = MutableLiveData<Result<CheckUpdateResponse>>()
val deviceUpdate = MutableLiveData<Result<ResponseBody>>()
val uploadLogs = MutableLiveData<Result<UploadLogsResponse>?>()
// Get the device ID of the device you want to retrieve data for (e.g., the first device in the list)
private val _networkStatusLiveData = NetworkStatusLiveData(context)
val allUserData = hemoCubeDao.getAll()
val allKitTestData = hemoCubeBufferDao.getAll()
val deviceData = MutableLiveData<DeviceData?>()
val networkStatusLiveData: LiveData<Boolean>
get() = _networkStatusLiveData
val deviceMessages = MutableLiveData<String?>()
val fireBaseUpload = MutableLiveData<String>()
val fireBaseBulkUpload = MutableLiveData<String>()
private val batteryStatus: Intent? =
IntentFilter(Intent.ACTION_BATTERY_CHANGED).let { ifilter ->
context.registerReceiver(null, ifilter)
}
fun uploadHemoCubeResultToDatabase(
isOnline: Boolean, testStatus: Boolean, kitSerial: String?,
) = viewModelScope.launch {
if (kitSerial != null) {
testDetails?.kitSerial = kitSerial
}
testDetails?.testStatus = testStatus
try {
if (isOnline) {
parseData()
addResultTestToDb()
} else {
parseData()
testDetails?.testTime = SimpleDateFormat(
"yyyy-MM-dd HH:mm:ss", Locale.getDefault()
).format(Calendar.getInstance().time)
hemoCubeDao.insertAll(testDetails!!)
fireBaseUpload.postValue("Local")
}
} catch (e: Exception) {
Log.e("Testdb", "Upload failed: ${e.message}")
}
}
fun login(loginRequest: LoginRequest) = viewModelScope.launch {
loginResponse.postValue(Result.Loading())
repository.login(loginRequest).let {
loginResponse.postValue(it)
}
}
fun uploadResult(molbioV2ResultRequest: MolbioV2ResultRequest) = viewModelScope.launch {
resultUpload.postValue(Result.Loading())
repository.uploadResults(molbioV2ResultRequest).let {
resultUpload.postValue(it)
}
}
fun checkUpdate(checkUpdateRequest: CheckUpdateRequest) = viewModelScope.launch {
checkUpdate.postValue(Result.Loading())
repository.checkUpdate(checkUpdateRequest).let {
checkUpdate.postValue(it)
}
}
fun deviceUpdate(deviceUpdateRequest: DeviceUpdateRequest) = viewModelScope.launch {
deviceUpdate.postValue(Result.Loading())
repository.deviceUpdate(deviceUpdateRequest).let {
deviceUpdate.postValue(it)
}
}
fun startPeriodicCheckUpdate() {
val periodicRequest = PeriodicWorkRequestBuilder<CheckUpdateWorker>(
repeatInterval = 1, repeatIntervalTimeUnit = TimeUnit.MINUTES
).build()
workManager.enqueueUniquePeriodicWork(
"checkUpdateWorker", ExistingPeriodicWorkPolicy.KEEP, periodicRequest
)
}
fun uploadLogs() = viewModelScope.launch {
uploadLogs.postValue(Result.Loading())
val logFile = logFileManager.createLogFile().let { file ->
val requestBody = file?.asRequestBody("multipart/form-data".toMediaTypeOrNull())
val multipartFile =
requestBody?.let { MultipartBody.Part.createFormData("logFile", file?.name, it) }
multipartFile?.let { partFile ->
repository.uploadLogs(partFile).let { result ->
uploadLogs.postValue(result)
}
}
}
}
fun uploadHemoCubeResultToDatabaseForBufferCheck(
isOnline: Boolean,
bufferCheckData: BufferCheckData,
) =
viewModelScope.launch {
if (isOnline) {
try {
when (val response =
repository.addTestToDatabaseforBufferCheck(bufferCheckData)) {
is Response.Success -> {
Log.i("Testdb", "Data uploaded to Firestore successfully")
fireBaseUpload.postValue("Success")
bufferCheckData.localFlag = true
hemoCubeBufferDao.insertAll(bufferCheckData)
}
is Response.Error -> {
Log.e("Testdb", "Error uploading data to Firestore: $response")
fireBaseUpload.postValue("Error")
bufferCheckData.localFlag = true
hemoCubeBufferDao.insertAll(bufferCheckData)
}
else -> {}
}
} catch (e: Exception) {
Log.e("Testdb", "Exception during data upload: ${e.message}")
fireBaseUpload.postValue("Error")
}
} else {
hemoCubeBufferDao.insertAll(bufferCheckData)
fireBaseUpload.postValue("Local")
}
}
fun bulkAddResultKitTestToDb(bufferCheckData: BufferCheckData) {
viewModelScope.launch {
bufferCheckData.reportUploadTime = SimpleDateFormat(
"yyyy-MM-dd HH:mm:ss", Locale.getDefault()
).format(Calendar.getInstance().time)
when (repository.addTestToDatabaseforBufferCheck(bufferCheckData)) {
is Response.Success -> {
fireBaseBulkUpload.postValue("Success")
updateBufferLocalFlag(bufferCheckData._id)
}
else -> {
fireBaseBulkUpload.postValue("Error")
}
}
}
}
fun uploadHemoCubeResultToDatabaseforbuffercheckN(bufferCheckData: BufferCheckData) =
viewModelScope.launch {
addResultTestToDbforbuffercheck(bufferCheckData)
}
fun getDeviceData(deviceId: String?) = viewModelScope.launch {
deviceData.postValue(deviceId?.let { repository.getDeviceDataById(it) })
}
fun parseData() {
testDetails?.deviceRatio = DataHolder.hemocubeResult
testDetails?.resultData = DataHolder.hemoCubeTestData?.resultData.toString()
testDetails?.location = DataHolder.location
testDetails?.testTime = SimpleDateFormat(
"yyyy-MM-dd HH:mm:ss", Locale.getDefault()
).format(Calendar.getInstance().time)
testDetails?.appVersion = DataHolder.hemoCubeTestData?.appVersion
testDetails?.deviceId = DataHolder.hemoCubeTestData?.deviceId
testDetails?.deviceSerialNumber =
sharedPreference.getString(Constants.USER_ID, "").toString()
testDetails?.kitSerial = sharedPreference.getString(Constants.KIT_NUMBER, "").toString()
testDetails?.led1Buffer = DataHolder.hemoCubeTestData?.led1Buffer
testDetails?.led2Buffer = DataHolder.hemoCubeTestData?.led2Buffer
testDetails?.led3Buffer = DataHolder.hemoCubeTestData?.led3Buffer
testDetails?.led4Buffer = DataHolder.hemoCubeTestData?.led4Buffer
testDetails?.led1Sample = DataHolder.hemoCubeTestData?.led1Sample
testDetails?.led2Sample = DataHolder.hemoCubeTestData?.led2Sample
testDetails?.led3Sample = DataHolder.hemoCubeTestData?.led3Sample
testDetails?.led4Sample = DataHolder.hemoCubeTestData?.led4Sample
testDetails?.led1Average = DataHolder.hemoCubeTestData?.led1Average
testDetails?.led2Average = DataHolder.hemoCubeTestData?.led2Average
testDetails?.led3Average = DataHolder.hemoCubeTestData?.led3Average
testDetails?.led4Average = DataHolder.hemoCubeTestData?.led4Average
testDetails?.abs1 = DataHolder.hemoCubeTestData?.abs1
testDetails?.abs2 = DataHolder.hemoCubeTestData?.abs2
testDetails?.abs3 = DataHolder.hemoCubeTestData?.abs3
testDetails?.abs4 = DataHolder.hemoCubeTestData?.abs4
testDetails?.deviceRatio = DataHolder.hemoCubeTestData?.deviceRatio
testDetails?.slopeRatio = DataHolder.hemoCubeTestData?.slopeRatio
testDetails?.predictedDenovixRatio = DataHolder.hemoCubeTestData?.predictedDenovixRatio
testDetails?.calculatedRatio = DataHolder.hemoCubeTestData?.calculatedRatio
testDetails?.coefficients = DataHolder.hemoCubeTestData?.coefficients
testDetails?.incubationTime = DataHolder.hemoCubeTestData?.incubationTime.toString()
testDetails?.name = DataHolder.hemoCubeTestData?.name.toString()
testDetails?.birthYear = DataHolder.hemoCubeTestData?.birthYear.toString()
testDetails?.userImageURL = DataHolder.hemoCubeTestData?.userImageURL.toString()
testDetails?.classificationResult = DataHolder.hemoCubeTestData?.classificationResult!!
testDetails?.prdClassification = DataHolder.hemoCubeTestData?.prdClassification.toString()
testDetails?.deviceRatioClass = DataHolder.hemoCubeTestData?.deviceRatioClass.toString()
testDetails?.slopeRatioClass = DataHolder.hemoCubeTestData?.slopeRatioClass.toString()
testDetails?.errorMessages = DataHolder.hemoCubeTestData?.errorMessages.toString()
testDetails?.batteryLevel = DataHolder.hemoCubeTestData?.batteryLevel.toString()
testDetails?.batteryCapacity = DataHolder.hemoCubeTestData?.batteryCapacity.toString()
testDetails?.batteryMaxCapacity = DataHolder.hemoCubeTestData?.batteryMaxCapacity.toString()
testDetails?.batteryTemperature = DataHolder.hemoCubeTestData?.batteryTemperature.toString()
testDetails?.batteryVoltage = DataHolder.hemoCubeTestData?.batteryVoltage.toString()
testDetails?.quickCapture = DataHolder.hemoCubeTestData?.quickCapture!!
testDetails?.solution = DataHolder.hemoCubeTestData?.solution
testDetails?.concentration = DataHolder.hemoCubeTestData?.concentration
testDetails?.volume = DataHolder.hemoCubeTestData?.volume
}
private fun addResultTestToDb() {
viewModelScope.launch {
try {
testDetails!!.reportUploadTime = SimpleDateFormat(
"yyyy-MM-dd HH:mm:ss", Locale.getDefault()
).format(Calendar.getInstance().time)
when (val response = repository.addTestToDatabase(testDetails)) {
is Response.Success -> {
val kitCount = sharedPreference.getInt(Constants.KIT_COUNT, 0)
with(sharedPreference.edit()) {
putInt(Constants.KIT_COUNT, kitCount.plus(1))
apply()
}
Log.i("Testdb", "Data uploaded to Firestore successfully")
fireBaseUpload.postValue("Success")
testDetails.localFlag = true
uploadResult(
MolbioV2ResultRequest(
mutableListOf(
MolbioV2Result(
rawData = testDetails,
analysisId = testDetails._id,
analysisDate = testDetails.testTime,
analysisStatus = testDetails.classificationResult,
thresholds = Constants.BUFFER_INTENSITY_THRESHOLDS[testDetails.deviceId].toString(),
interpretation = testDetails.classificationResult,
testId = testDetails._id,
testTime = testDetails.testTime,
collectionTime = testDetails.testTime,
expiryTime = testDetails.testTime,
)
)
)
)
hemoCubeDao.insertAll(testDetails)
}
is Response.Error -> {
Log.e("Testdb", "Error uploading data to Firestore: $response")
fireBaseUpload.postValue("Error")
hemoCubeDao.insertAll(testDetails)
}
else -> {}
}
} catch (e: Exception) {
Log.e("Testdb", "Exception during data upload: ${e.message}")
fireBaseUpload.postValue("Error")
}
}
}
fun bulkAddResultTestToDb(userData: HemoCubeTestData) {
viewModelScope.launch {
userData.reportUploadTime = SimpleDateFormat(
"yyyy-MM-dd HH:mm:ss", Locale.getDefault()
).format(Calendar.getInstance().time)
when (repository.addTestToDatabase(userData)) {
is Response.Success -> {
fireBaseBulkUpload.postValue("Success")
updateLocalFlag(userData._id)
}
else -> {
fireBaseBulkUpload.postValue("Error")
}
}
}
}
private fun updateLocalFlag(userId: String) = viewModelScope.launch {
hemoCubeDao.updateFieldById(id = userId, true)
}
fun updateMolbioFlag(userId: String) = viewModelScope.launch {
hemoCubeDao.updateMolbioFlag(id = userId, true)
}
fun addUser(userData: HemoCubeTestData) = viewModelScope.launch {
hemoCubeDao.insertAll(userData)
}
fun deleteById(userId: String) = viewModelScope.launch {
hemoCubeDao.deleteById(id = userId)
}
private fun addResultTestToDbforbuffercheck(bufferCheckData: BufferCheckData) {
viewModelScope.launch {
try {
when (val response =
repository.addTestToDatabaseforBufferCheck(bufferCheckData)) {
is Response.Success -> {
Log.i("Testdb", "Data uploaded to Firestore successfully")
fireBaseUpload.postValue("Success")
}
is Response.Error -> {
Log.e("Testdb", "Error uploading data to Firestore: $response")
fireBaseUpload.postValue("Error")
}
}
} catch (e: Exception) {
Log.e("Testdb", "Exception during data upload: ${e.message}")
fireBaseUpload.postValue("Error")
}
}
}
private fun updateBufferLocalFlag(bufferId: String) =
viewModelScope.launch {
hemoCubeBufferDao.updateFieldById(id = bufferId, true)
}
fun getLocalUserDataForCsv(context: Context): Boolean {
val localUserDataLiveData: LiveData<List<HemoCubeTestData>> = hemoCubeDao.getAll()
// Observe the LiveData to get the actual data when available
localUserDataLiveData.observeForever { localUserData ->
localUserData?.let {
val csvData = mutableListOf<Array<String>>()
it.forEach { userData ->
csvData.add(
arrayOf(
userData._id,
userData.name,
userData.bloodGroup,
userData.birthYear,
userData.classificationResult,
userData.testTime.toString(),
userData.userImageURL
)
)
}
val csvWriter = CsvWriter(context)
csvWriter.writeCsv("userData.csv", csvData)
// Remove the observer to avoid leaks
localUserDataLiveData.removeObserver {}
}
}
return true // Assuming success, you might want to modify this based on your actual logic
}
fun getBatteryLevel(): Float? {
val batteryPct: Float? = batteryStatus?.let { intent ->
val level: Int =
intent.getIntExtra(
BatteryManager.EXTRA_LEVEL,
-1
)
val scale: Int =
intent.getIntExtra(
BatteryManager.EXTRA_SCALE,
-1
)
level * 100 / scale.toFloat()
}
return batteryPct
}
fun getBatteryTemperature(): Float? {
val batteryTemp: Float? = batteryStatus?.let { intent ->
val temperature = intent.getIntExtra(
BatteryManager.EXTRA_TEMPERATURE,
0
)
temperature.toFloat() / 10
}
return batteryTemp
}
fun getBatteryVoltage(context: Context): Float {
val batteryIntent =
context.registerReceiver(
null,
IntentFilter(Intent.ACTION_BATTERY_CHANGED)
)
val voltage = batteryIntent?.getIntExtra(
BatteryManager.EXTRA_VOLTAGE,
0
) ?: 0
// milli-volts to volts
return voltage.toFloat() / 1000
}
fun getBatteryCapacity(context: Context): Int {
val batteryManager =
context.getSystemService(Context.BATTERY_SERVICE) as BatteryManager
val currentCapacity =
batteryManager.getIntProperty(BatteryManager.BATTERY_PROPERTY_CHARGE_COUNTER)
return currentCapacity
}
fun getBatteryMaxCapacity(context: Context): Float {
val batteryManager = context.getSystemService(Context.BATTERY_SERVICE) as BatteryManager
val designCapacity =
batteryManager.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY)
val currentCapacity =
batteryManager.getIntProperty(BatteryManager.BATTERY_PROPERTY_CHARGE_COUNTER)
// Calculate the estimated maximum battery capacity in mAh
val maxCapacity = currentCapacity.toFloat() / designCapacity.toFloat() * 100
return maxCapacity
}
fun createCSV(hemoCubeTestData: List<HemoCubeTestData>, appContext: Context) =
viewModelScope.launch {
val fileName = "HPOS${getCurrentDate()}.csv"
if (localFileDataSource.exportDataToCSV(fileName, hemoCubeTestData)) {
hemoCubeTestData.forEach { data ->
data.localFlag = true
hemoCubeDao.updateCSVFieldById(
data._id,
true
)
}
}
}
fun getCurrentDate(): String {
return SimpleDateFormat(
"yyyy-MM-dd HH:mm:ss", Locale.getDefault()
).format(Calendar.getInstance().time)
}
}

View File

@@ -0,0 +1,5 @@
<vector android:height="24dp" android:tint="@color/primary"
android:viewportHeight="24" android:viewportWidth="24"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@color/primary" android:pathData="M19,9h-4V3H9v6H5l7,7 7,-7zM5,18v2h14v-2H5z"/>
</vector>

View File

@@ -0,0 +1,35 @@
<?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_trueheme"
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintTop_toBottomOf="@id/my_toolbar"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
tools:context=".presentation.trueheme.TrueHemeActivity">
</FrameLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>

View File

@@ -108,4 +108,33 @@
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/btn_calibration" />
<com.google.android.material.button.MaterialButton
android:id="@+id/btn_firefox"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="16dp"
android:layout_marginTop="24dp"
android:clickable="false"
android:text="@string/Firefox"
android:textColor="@color/white"
app:cornerRadius="16dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/btn_deviceProvision" />
<com.google.android.material.button.MaterialButton
android:id="@+id/btn_files"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="16dp"
android:layout_marginTop="24dp"
android:clickable="false"
android:text="@string/Files"
android:textColor="@color/white"
app:cornerRadius="16dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/btn_firefox" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -0,0 +1,264 @@
<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"
tools:context="com.example.hpostesting.presentation.trueheme.TrueHemeFragment">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/cl_parent"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/tv_title"
style="@style/title1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginHorizontal="24dp"
android:layout_marginTop="16dp"
android:text="@string/Instructions"
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"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/tv_title" />
<!-- <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"-->
<!-- android:text="@string/step5"-->
<!-- app:layout_constraintEnd_toEndOf="parent"-->
<!-- app:layout_constraintStart_toStartOf="parent"-->
<!-- app:layout_constraintTop_toBottomOf="@id/tv_subtitle2" />-->
<TextView
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" />
<TextView
android:id="@+id/tv_subtitle4"
style="@style/title1_1"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginHorizontal="24dp"
android:layout_marginTop="72dp"
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/et_abha_id" />
<!-- <Button-->
<!-- android:visibility="gone"-->
<!-- android:id="@+id/btn_samplestart"-->
<!-- android:layout_width="wrap_content"-->
<!-- android:layout_height="wrap_content"-->
<!-- android:layout_marginHorizontal="16dp"-->
<!-- android:layout_marginTop="24dp"-->
<!-- android:clickable="false"-->
<!-- android:text="@string/Start_Sample"-->
<!-- android:textColor="@color/white"-->
<!-- app:layout_constraintEnd_toEndOf="parent"-->
<!-- app:layout_constraintStart_toStartOf="parent"-->
<!-- app:layout_constraintTop_toBottomOf="@id/til_blood_group" />-->
<!-- <Button-->
<!-- android:id="@+id/btn_placebuffer"-->
<!-- android:layout_width="wrap_content"-->
<!-- android:layout_height="wrap_content"-->
<!-- android:layout_marginHorizontal="16dp"-->
<!-- android:layout_marginTop="24dp"-->
<!-- android:clickable="false"-->
<!-- android:text="@string/place_buffer"-->
<!-- android:textColor="@color/white"-->
<!-- app:layout_constraintEnd_toEndOf="parent"-->
<!-- app:layout_constraintStart_toStartOf="parent"-->
<!-- app:layout_constraintTop_toBottomOf="@id/btn_submit" />-->
<TextView
android:id="@+id/tv_title2"
style="@style/title1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginHorizontal="24dp"
android:layout_marginTop="48dp"
android:text="@string/enter_kit_serial_number_manually"
app:layout_constraintStart_toStartOf="parent"
tools:ignore="MissingConstraints" />
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/et_abha_id"
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="24dp"
android:layout_marginTop="20dp"
app:layout_constraintEnd_toStartOf="@id/btn_go"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/tv_title2">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/name_edit_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:maxLength="17"
android:inputType="text"
android:hint="@string/serial_number" />
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.button.MaterialButton
android:id="@+id/btn_go"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="24dp"
android:text="@string/go"
app:cornerRadius="16dp"
app:layout_constraintBottom_toBottomOf="@id/et_abha_id"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="1.0"
app:layout_constraintStart_toEndOf="@id/et_abha_id"
app:layout_constraintTop_toTopOf="@id/et_abha_id" />
<Button
android:visibility="gone"
android:id="@+id/btn_samplestart"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginHorizontal="16dp"
android:layout_marginTop="24dp"
android:clickable="false"
android:text="@string/Start_Sample"
android:textColor="@color/white"
app:cornerRadius="16dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/btn_placebuffer" />
<Button
android:id="@+id/btn_placebuffer"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginHorizontal="16dp"
android:layout_marginTop="24dp"
android:clickable="false"
android:text="@string/place_buffer"
android:textColor="@color/white"
app:cornerRadius="16dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/btn_submit" />
<Button
android:id="@+id/btn_submit"
android:visibility="gone"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="16dp"
android:layout_marginTop="24dp"
android:clickable="false"
android:text="@string/submit"
android:textColor="@color/white"
app:cornerRadius="16dp"
app:layout_constraintEnd_toEndOf="parent"
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="40dp"
android:layout_marginHorizontal="24dp"
android:layout_marginTop="72dp"
android:gravity="center"
android:textColor="@color/red"
android:textSize="12sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/btn_submit" />
<TextView
android:id="@+id/tv_device_messages"
style="@style/title1_1"
android:layout_width="414dp"
android:layout_height="201dp"
android:gravity="center"
android:text="no device message"
android:textColor="@color/black"
android:textSize="11sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/error_message" />
<Button
android:visibility="gone"
android:id="@+id/btn_digitalCard"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginHorizontal="16dp"
android:layout_marginTop="24dp"
android:clickable="false"
android:text="DigitalCard View"
android:textColor="@color/white"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/error_message" />
<ImageView
android:id="@+id/iv_check"
android:visibility="gone"
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_marginTop="40dp"
android:importantForAccessibility="no"
android:src="@drawable/check"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/tv_device_messages" />
<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>
</layout>

View File

@@ -115,6 +115,8 @@
<string name="assurance_controls">Quality Assurance</string>
<string name="calibration">Calibration</string>
<string name="deviceProvision">Device Provision</string>
<string name="Firefox">Firefox</string>
<string name="Files">Files</string>
<string name="deviceinfo">Device Information</string>
<string name="place_buffer">Start</string>
<string name="Start_Sample">Start Sample</string>

View File

@@ -0,0 +1,65 @@
package com.example.hpostesting
//
//@PrepareForTest(Object::class)
//class AuthInterceptorTest {
//
//// @Rule
//// val rule = PowerMockRule()
//
// @Mock
// private lateinit var sharedPreferences: SharedPreferences
//
// @Mock
// private lateinit var chain: Interceptor.Chain
//
// @Mock
// private lateinit var request: okhttp3.Request
//
// @Mock
// private lateinit var response: Response
//
// @Captor
// private lateinit var captor: ArgumentCaptor<okhttp3.Request>
//
// private lateinit var authInterceptor: AuthInterceptor
//
// @Before
// fun setup() {
// MockitoAnnotations.initMocks(this)
// authInterceptor = AuthInterceptor(sharedPreferences)
// }
//
// @Test
// fun intercept_withToken_shouldAddAuthorizationHeader() {
// // Arrange
// val accessToken = "fakeAccessToken"
// `when`(sharedPreferences.getString(Constants.ACCESS_TOKEN, null)).thenReturn(accessToken)
// `when`(chain.request()).thenReturn(request)
// `when`(chain.proceed(request)).thenReturn(response)
//
// // Act
// val result = authInterceptor.intercept(chain)
//
// // Assert
// verify(chain).proceed(captor.capture())
// assertEquals("Bearer $accessToken", captor.value.header("Authorization"))
// assertEquals(response, result)
// }
//
// @Test
// fun intercept_withoutToken_shouldNotAddAuthorizationHeader() {
// // Arrange
// `when`(sharedPreferences.getString(Constants.ACCESS_TOKEN, null)).thenReturn(null)
// `when`(chain.request()).thenReturn(request)
// `when`(chain.proceed(request)).thenReturn(response)
//
// // Act
// val result = authInterceptor.intercept(chain)
//
// // Assert
// verify(chain).proceed(captor.capture())
// assertEquals(null, captor.value.header("Authorization"))
// assertEquals(response, result)
// }
//}

View File

@@ -0,0 +1,67 @@
package com.example.hpostesting
import android.content.Context
import com.example.hpostesting.data.CsvWriter
import org.junit.Assert
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.robolectric.RobolectricTestRunner
import org.robolectric.annotation.Config
@RunWith(RobolectricTestRunner::class)
@Config(manifest = Config.NONE)
class CsvWriterTest {
private lateinit var csvWriter: CsvWriter
private lateinit var context: Context
@Before
fun setUp() {
context = androidx.test.core.app.ApplicationProvider.getApplicationContext()
csvWriter = CsvWriter(context)
}
@Test
fun writeCsv_Success() {
// Arrange
val fileName = "test.csv"
val data = listOf(
arrayOf("1", "A", "1990", "Positive", "2024-02-04", "image_url1", "John Doe"),
arrayOf("2", "B", "1985", "Negative", "2024-02-05", "image_url2", "Jane Doe")
)
// Act
val result = csvWriter.writeCsv(fileName, data)
// Assert
Assert.assertTrue(result)
}
// @Test
// fun writeCsv_Failure() {
// // Arrange
// val fileName = "test.csv"
// val data = listOf(
// arrayOf("1", "A", "1990", "Positive", "2024-02-04", "image_url1", "John Doe"),
// arrayOf("2", "B", "1985", "Negative", "2024-02-05", "image_url2", "Jane Doe")
// )
//
// // Mocking Environment.getExternalStorageDirectory()
// val mockFile = Mockito.mock(File::class.java)
// Mockito.`when`(Environment.getExternalStorageDirectory()).thenReturn(mockFile)
// Mockito.`when`(mockFile.exists()).thenReturn(true)
//
// // Mocking FileWriter to simulate IOException
// val mockWriter = Mockito.mock(FileWriter::class.java)
// Mockito.`when`(mockWriter.write(Mockito.anyString())).thenThrow(IOException::class.java)
// Mockito.`when`(mockFile.absolutePath).thenReturn("/fake/path/to/file.csv")
// Mockito.`when`(FileWriter(mockFile)).thenReturn(mockWriter)
//
// // Act
// val result = csvWriter.writeCsv(fileName, data)
//
// // Assert
// Assert.assertFalse("Expected writeCsv to fail", result)
// }
}

View File

@@ -0,0 +1,49 @@
package com.example.hpostesting
import android.content.Context
import androidx.arch.core.executor.testing.InstantTaskExecutorRule
import com.example.hpostesting.data.dao.HemoCubeDao
import com.example.hpostesting.data.repository.Repository
import com.example.hpostesting.presentation.deviceinfo.DeviceViewModel
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.junit.MockitoJUnitRunner
@RunWith(MockitoJUnitRunner::class)
class DeviceViewModelTest {
@get:Rule
val rule = InstantTaskExecutorRule()
@Mock
private lateinit var hemoCubeDao: HemoCubeDao
@Mock
private lateinit var repository: Repository
@Mock
private lateinit var context: Context
private lateinit var viewModel: DeviceViewModel
@Before
fun setup() {
viewModel = DeviceViewModel(hemoCubeDao, repository)
}
@Test
fun `test initial state`() {
assert(!viewModel.isServiceConnected)
assert(!viewModel.progressBar.value!!)
assert(viewModel.messages.value == null)
// assert(viewModel.allUserData == hemoCubeDao.getAll())
// assert(viewModel.deviceData.value == null)
// assert(viewModel.networkStatusLiveData.value == false)
assert(viewModel.fireBaseUpload.value == null)
}
// Add more tests for other functions and interactions as needed
}

View File

@@ -318,21 +318,21 @@ class HemoCubeFragmentTest {
@Test
fun testDeviceRatioClassificationNormal() {
val ratio = 0.25
val ratio = 0.22
val result = hemoCubeFragment.deviceRatioClassification(ratio)
assertEquals("Normal", result)
}
@Test
fun testDeviceRatioClassificationNegativeBorderline() {
val ratio = 0.31
val ratio = 0.235
val result = hemoCubeFragment.deviceRatioClassification(ratio)
assertEquals("Negative Borderline, Repeat Test", result)
assertEquals("Negative Borderline", result)
}
@Test
fun testDeviceRatioClassificationSickleCellTrait() {
val ratio = 0.34
val ratio = 0.25
val result = hemoCubeFragment.deviceRatioClassification(ratio)
assertEquals("Sickle Cell Trait", result)
}
@@ -357,4 +357,46 @@ class HemoCubeFragmentTest {
val result = hemoCubeFragment.deviceRatioClassification(ratio)
assertEquals("Invalid", result)
}
@Test
fun findResultWithAdditionalMethods_ValidInput_ReturnsNegativeBorderlineRepeatTest() {
// `when`(hemoCubeFragment.findResultWithAdditionalMethods(0.5, "Normal", 70.0)).thenReturn("Negative Borderline, Repeat Test")
val result = hemoCubeFragment.findResultWithAdditionalMethods(0.5, "Normal", 70.0)
assertEquals("Negative Borderline, Repeat Test", result)
}
@Test
fun findResultWithAdditionalMethods_NormalDeviceRatio_ReturnsNormalBelowSlopeRatioThreshold() {
// `when`(hemoCubeFragment.findResultWithAdditionalMethods(0.5, "Normal", 70.0)).thenReturn("Negative Borderline, Repeat Test")
val result = hemoCubeFragment.findResultWithAdditionalMethods(0.5, "Normal", 30.0)
assertEquals("Normal", result)
}
@Test
fun findResultWithAdditionalMethods_NBL_ReturnsNBL() {
// `when`(hemoCubeFragment.findResultWithAdditionalMethods(0.5, "Abnormal", 70.0)).thenReturn("Invalid")
val result = hemoCubeFragment.findResultWithAdditionalMethods(0.5, "Negative Borderline, Repeat Test", 70.0)
assertEquals("Negative Borderline, Repeat Test", result)
}
@Test
fun findResultWithAdditionalMethods_SCT_ReturnsSCT() {
// `when`(hemoCubeFragment.findResultWithAdditionalMethods(0.5, "Abnormal", 70.0)).thenReturn("Invalid")
val result = hemoCubeFragment.findResultWithAdditionalMethods(0.5, "Sickle Cell Trait", 70.0)
assertEquals("Sickle Cell Trait", result)
}
@Test
fun findResultWithAdditionalMethods_PBL_ReturnsPBL() {
// `when`(hemoCubeFragment.findResultWithAdditionalMethods(0.5, "Abnormal", 70.0)).thenReturn("Invalid")
val result = hemoCubeFragment.findResultWithAdditionalMethods(0.5, "Positive for Sickle Cell. HPLC for Confirmation", 70.0)
assertEquals("Positive for Sickle Cell. HPLC for Confirmation", result)
}
@Test
fun findResultWithAdditionalMethods_SCD_ReturnsSCD() {
// `when`(hemoCubeFragment.findResultWithAdditionalMethods(0.5, "Abnormal", 70.0)).thenReturn("Invalid")
val result = hemoCubeFragment.findResultWithAdditionalMethods(0.5, "Sickle Cell Disease", 70.0)
assertEquals("Sickle Cell Disease", result)
}
}

View File

@@ -0,0 +1,86 @@
package com.example.hpostesting
//
//class LogFileManagerImplTest {
//
// @Mock
// private lateinit var mockContext: Context
//
// private lateinit var logFileManager: LogFileManagerImpl
//
// @Before
// fun setUp() {
// mockContext = mock()
// logFileManager = LogFileManagerImpl(mockContext)
// }
// @Test
// fun testCreateLogFileSuccess() {
// // Mock filesDir
// whenever(mockContext.getString(R.string.app_name)).thenReturn("MockedAppName")
// val mockFilesDir = mock<File>()
// doReturn(true).whenever(mockContext).getFilesDir().exists()
// doReturn(mockFilesDir).whenever(mockContext).getFilesDir()
//
// // Set expected file name
// val expectedFileName = "hpos_1234567890.log"
// val currentTime = System.currentTimeMillis() / 1000L
// doReturn(true).whenever(mockFilesDir).createNewFile(expectedFileName)
// doReturn(mockFilesDir.absolutePath + File.separator + expectedFileName).whenever(mockFilesDir).absolutePath
//
// // Mock FileOutputStream
// val mockFileOutputStream = mock<FileOutputStream>()
// doReturn(mockFileOutputStream).whenever(FileOutputStream(mockFilesDir.absolutePath + File.separator + expectedFileName))
//
// // Call createLogFile and verify result
// val logFile = logFileManager.createLogFile()
//
// assertNotNull(logFile)
// assertTrue(logFile.exists())
// assertEquals(expectedFileName, logFile.name)
//
// // Verify FileOutputStream was called
// verify(mockFileOutputStream).write(any())
// verify(mockFileOutputStream).close()
// }
//
// @Test
// fun testCreateLogFileExistingFile() throws IOException {
// // Mock filesDir with existing file
// val mockFilesDir = mock<File>()
// val existingFile = mock<File>()
// doReturn(true).whenever(mockContext).getFilesDir().exists()
// doReturn(mockFilesDir).whenever(mockContext).getFilesDir()
// doReturn(true).whenever(mockFilesDir).exists()
// doReturn(listOf(existingFile)).whenever(mockFilesDir).listFiles()
//
// // Call createLogFile and expect exception
// try {
// logFileManager.createLogFile()
// fail("Expected IOException due to existing file")
// } catch (e: IOException) {
// // Expected behavior
// }
//
// // Verify FileOutputStream was not called
// verify(mockFilesDir, times(0)).createNewFile(anyString())
// }
//
// @Test
// fun testCreateLogFileIOException() throws IOException {
// // Mock filesDir and IOException
// val mockFilesDir = mock<File>()
// doReturn(true).whenever(mockContext).getFilesDir().exists()
// doReturn(mockFilesDir).whenever(mockContext).getFilesDir()
// doReturn(true).whenever(mockFilesDir).exists()
// doThrow(IOException("Mock IOException")).whenever(mockFilesDir).createNewFile(anyString())
//
// // Call createLogFile and expect null result
// val logFile = logFileManager.createLogFile()
//
// assertNull(logFile)
//
// // Verify FileOutputStream was not called
// verify(mockFilesDir, times(0)).createNewFile(anyString())
// }
//}

View File

@@ -0,0 +1,350 @@
package com.example.hpostesting
import android.content.Context
import android.content.SharedPreferences
import com.example.hpostesting.presentation.trueheme.TrueHemeFragment
import junit.framework.TestCase
import org.junit.Before
import org.junit.Test
import org.mockito.ArgumentMatchers
import org.mockito.Mock
import org.mockito.Mockito
import org.mockito.MockitoAnnotations
class TrueHemeFragmentTest {
@Mock
lateinit var mockContext: Context
@Mock
private lateinit var mockSharedPreferences: SharedPreferences
private lateinit var fragment: TrueHemeFragment
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
fragment = TrueHemeFragment()
}
@Test
fun `extractV2HardwareId to get device id`() {
// Arrange
Mockito.`when`(
mockSharedPreferences.getString(
ArgumentMatchers.anyString(),
ArgumentMatchers.anyString()
)
).thenReturn("dummy_value")
// Act
val deviceId = fragment.extractV2HardwareId("SNS HPP1-9000 SNE")
// Assert
TestCase.assertEquals("HPP1-9000", deviceId)
}
@Test
fun `updateDeviceId in shared pref`() {
// Arrange
Mockito.`when`(
mockSharedPreferences.getString(
ArgumentMatchers.anyString(),
ArgumentMatchers.anyString()
)
).thenReturn("HPP1-0001")
// Act
val deviceId = mockSharedPreferences.getString(
ArgumentMatchers.anyString(),
ArgumentMatchers.anyString()
)
// Assert
TestCase.assertEquals("HPP1-0001", deviceId)
}
@Test
fun `allReadingsComplete check`() {
// Arrange
val repeatReadingCount = 1
val readingsPerSample = 1
// Act
val result = fragment.allReadingsComplete(repeatReadingCount, readingsPerSample)
// Assert
TestCase.assertEquals(true, result)
TestCase.assertEquals(fragment.allReadingsComplete(0, 1), false)
}
@Test
fun `extractV1HardwareId should return hardware ID when input contains SN`() {
// Arrange
val input = "Some text SN ABC123 some more text"
// Act
val result = fragment.extractV1HardwareId(input)
// Assert
TestCase.assertEquals("ABC123", result)
}
@Test
fun `extractV1HardwareId should return null when input does not contain SN`() {
// Arrange
val input = "Some text without SN"
// Act
val result = fragment.extractV1HardwareId(input)
// Assert
TestCase.assertNull(result)
}
@Test
fun `extractV1HardwareId should return null when input is empty`() {
// Arrange
val input = ""
// Act
val result = fragment.extractV1HardwareId(input)
// Assert
TestCase.assertNull(result)
}
@Test
fun `extractV1HardwareId should return null when input is null`() {
// Arrange
val input: String? = null
// Act
val result = input?.let { fragment.extractV1HardwareId(it) }
// Assert
TestCase.assertNull(result)
}
@Test
fun `extractV1HardwareId should return hardware ID when input contains SN in a specific format`() {
// Arrange
val input = """
SN HCV-000-3001
#BS
#BC
#SS
#SC
RESULT
LB1 20636.32
LB2 15855.67
LB3 21801.36
LB4 18362.33
LS1 17287
LS2 14855.67
LS3 15282.31
LS4 9737.98
REND
""".trimIndent()
// Act
val result = fragment.extractV1HardwareId(input)
// Assert
TestCase.assertEquals("HCV-000-3001", result)
}
@Test
fun `extractV2HardwareId should return the correct hardware ID when it exists in the input`() {
// Arrange
val input = "SNS ABC123 SNE"
// Act
val result = fragment.extractV2HardwareId(input)
// Assert
TestCase.assertEquals("ABC123", result)
}
@Test
fun `extractV2HardwareId should return null when no hardware ID is found in the input`() {
// Arrange
val input = "No hardware ID in this input"
// Act
val result = fragment.extractV2HardwareId(input)
// Assert
TestCase.assertNull(result)
}
@Test
fun `extractV2HardwareId should handle whitespace around the hardware ID`() {
// Arrange
val input = "SNS XYZ789 SNE"
// Act
val result = fragment.extractV2HardwareId(input)
// Assert
TestCase.assertEquals("XYZ789", result)
}
@Test
fun `extractV2HardwareId should handle provided input string with HCV-000-3013`() {
// Arrange
val input = "SNS HCV-000-3013 SNE\n" +
"#SS1\n" +
"#SC1\n" +
"RESULT \n" +
"LB1 23411.00\n" +
"LB2 21417.00\n" +
"LB3 23869.00\n" +
"LB4 24967.00\n" +
"LS1 3401.00\n" +
"LS2 1107.00\n" +
"LS3 14410.00\n" +
"LS4 15047.00\n" +
"REND\n"
// Act
val result = fragment.extractV2HardwareId(input)
// Assert
TestCase.assertEquals("HCV-000-3013", result)
}
@Test
fun `extractV2HardwareId should handle provided input string with HPP1-4001`() {
// Arrange
val input = "SNS HPP1-4001 SNE#SS1\n" +
"#SC1\n" +
"RESULT\n" +
"LB1 23777\n" +
"LB2 24130\n" +
"LB3 23442\n" +
"LB4 23945\n" +
"LS1 2521\n" +
"LS2 973\n" +
"LS3 10252\n" +
"LS4 11017\n" +
"REND\n"
// Act
val result = fragment.extractV2HardwareId(input)
// Assert
TestCase.assertEquals("HPP1-4001", result)
}
@Test
fun `extractV2HardwareId should handle provided input string with HPP1-000-4001`() {
// Arrange
val input = "SNS HPP1-000-4001 SNE#SS1\n" +
"#SC1\n" +
"RESULT\n" +
"LB1 23777\n" +
"LB2 24130\n" +
"LB3 23442\n" +
"LB4 23945\n" +
"LS1 2521\n" +
"LS2 973\n" +
"LS3 10252\n" +
"LS4 11017\n" +
"REND\n"
// Act
val result = fragment.extractV2HardwareId(input)
// Assert
TestCase.assertEquals("HPP1-000-4001", result)
}
@Test
fun `extractV2HardwareId should handle provided input string with HPP-000-4001`() {
// Arrange
val input = "SNS HPP-000-4001 SNE#SS1\n" +
"#SC1\n" +
"RESULT\n" +
"LB1 23777\n" +
"LB2 24130\n" +
"LB3 23442\n" +
"LB4 23945\n" +
"LS1 2521\n" +
"LS2 973\n" +
"LS3 10252\n" +
"LS4 11017\n" +
"REND\n"
// Act
val result = fragment.extractV2HardwareId(input)
// Assert
TestCase.assertEquals("HPP-000-4001", result)
}
@Test
fun `extractV2HardwareId should handle provided input string with HPP-000-5001`() {
// Arrange
val input = "SNS HPP-000-5001 SNE#SS1\n" +
"#SC1\n" +
"RESULT\n" +
"LB1 23777\n" +
"LB2 24130\n" +
"LB3 23442\n" +
"LB4 23945\n" +
"LS1 2521\n" +
"LS2 973\n" +
"LS3 10252\n" +
"LS4 11017\n" +
"REND\n"
// Act
val result = fragment.extractV2HardwareId(input)
// Assert
TestCase.assertEquals("HPP-000-5001", result)
}
@Test
fun testDeviceRatioClassificationNormal() {
val ratio = 0.25
val result = fragment.deviceRatioClassification(ratio)
TestCase.assertEquals("Normal", result)
}
@Test
fun testDeviceRatioClassificationNegativeBorderline() {
val ratio = 0.31
val result = fragment.deviceRatioClassification(ratio)
TestCase.assertEquals("Negative Borderline, Repeat Test", result)
}
@Test
fun testDeviceRatioClassificationSickleCellTrait() {
val ratio = 0.34
val result = fragment.deviceRatioClassification(ratio)
TestCase.assertEquals("Sickle Cell Trait", result)
}
@Test
fun testDeviceRatioClassificationPositiveForSickleCell() {
val ratio = 0.37
val result = fragment.deviceRatioClassification(ratio)
TestCase.assertEquals("Positive for Sickle Cell. HPLC for Confirmation", result)
}
@Test
fun testDeviceRatioClassificationSickleCellDisease() {
val ratio = 0.45
val result = fragment.deviceRatioClassification(ratio)
TestCase.assertEquals("Sickle Cell Disease", result)
}
@Test
fun testDeviceRatioClassificationInvalid() {
val ratio: Double? = null
val result = fragment.deviceRatioClassification(ratio)
TestCase.assertEquals("Invalid", result)
}
}