Compare commits

..

7 Commits

34 changed files with 429 additions and 903 deletions

3
.idea/gradle.xml generated
View File

@@ -4,8 +4,9 @@
<component name="GradleSettings">
<option name="linkedExternalProjectsSettings">
<GradleProjectSettings>
<option name="testRunner" value="GRADLE" />
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="gradleJvm" value="#GRADLE_LOCAL_JAVA_HOME" />
<option name="gradleJvm" value="jbr-17" />
<option name="modules">
<set>
<option value="$PROJECT_DIR$" />

View File

@@ -1,4 +0,0 @@
### Release key
key0: prime24

1
app/.gitignore vendored
View File

@@ -1,4 +1,3 @@
/build
/release
/google-services*
/idea

View File

@@ -80,16 +80,16 @@ dependencies {
implementation 'com.google.firebase:firebase-auth-ktx'
implementation 'com.google.firebase:firebase-storage-ktx'
implementation 'com.firebaseui:firebase-ui-firestore:8.0.2'
implementation 'com.google.android.gms:play-services-auth:21.0.0'
implementation 'com.google.android.gms:play-services-auth:20.7.0'
implementation 'com.google.android.gms:play-services-location:21.1.0'
implementation 'com.google.android.gms:play-services-ads-identifier:18.0.1'
implementation 'com.google.android.things:androidthings:1.0'
implementation 'com.google.firebase:firebase-appdistribution:16.0.0-beta12'
implementation("com.google.firebase:firebase-appdistribution-api-ktx:16.0.0-beta12")
implementation 'com.google.firebase:firebase-appdistribution:16.0.0-beta11'
implementation("com.google.firebase:firebase-appdistribution-api-ktx:16.0.0-beta11")
implementation 'androidx.preference:preference-ktx:1.2.1'
implementation 'androidx.preference:preference-ktx:1.2.1'
implementation 'com.google.android.play:core:1.10.3'
implementation 'io.nats:jnats:2.11.4'
implementation 'io.nats:jnats:2.11.2'
@@ -143,8 +143,8 @@ dependencies {
annotationProcessor 'com.github.bumptech.glide:compiler:4.13.2'
// Navigation Component
implementation "androidx.navigation:navigation-fragment-ktx:2.7.7"
implementation "androidx.navigation:navigation-ui-ktx:2.7.7"
implementation "androidx.navigation:navigation-fragment-ktx:2.7.6"
implementation "androidx.navigation:navigation-ui-ktx:2.7.6"
//Dagger - Hilt
implementation "com.google.dagger:hilt-android:2.46"

View File

@@ -25,7 +25,6 @@
<application
android:name="com.example.hpostesting.HPOSTestingApplication"
android:largeHeap="true"
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
@@ -117,15 +116,18 @@
android:name="com.example.hpostesting.presentation.testRight.UsbService"
android:enabled="true"
android:exported="false" />
<activity
android:name="com.example.hpostesting.presentation.SplashActivity"
android:exported="true"
android:noHistory="true"
android:theme="@style/AppTheme.NoActionBar">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
<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
@@ -162,7 +164,7 @@
android:screenOrientation="portrait"
android:stateNotNeeded="true"
tools:replace="android:screenOrientation" />
<!-- ${applicationId}-->
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.fileprovider"

View File

@@ -5,7 +5,7 @@ object Constants {
const val HEMOCUBE_USB_PERMISSION = "shanmukha.in.sickle_cell_homocube.USB_PERMISSION"
const val BASE_URL = "www.google.com"
const val DOCUMENT_ID_FOR_UPDATE ="2o71vBKLqdgEKYtFG8Lb"
const val ABHA_APP_PACKAGE = "in.ndhm.phr"
const val MOLBIO_INTEGRATION = true
@@ -67,8 +67,6 @@ object Constants {
val STATICID = listOf(
"FACTORY",
"ADMIN",
"PQUSER",
"QCUSER",
"VIZ-1000-0004",
"VIZ-1000-0005",
"VIZ-1000-0006",

View File

@@ -29,8 +29,4 @@ interface HemoCubeDao {
@Query("UPDATE hemo_cube_test_table SET isCSVCreated = :newValue WHERE _id = :id")
suspend fun updateCSVFieldById(id: String, newValue: Boolean)
@Query("SELECT * from hemo_cube_test_table WHERE molbioFlag = :status")
suspend fun getPendingUser(status: Boolean): List<HemoCubeTestData>
}

View File

@@ -10,7 +10,7 @@ import com.example.hpostesting.data.model.patient.UserData
@Database(
entities = [UserData::class, HemoCubeTestData::class, DeviceData::class, BufferCheckData::class],
version = 28,
version = 26,
exportSchema = false
)
@TypeConverters(Converters::class)

View File

@@ -0,0 +1,36 @@
package com.example.hpostesting.data.encryption
import android.security.keystore.KeyGenParameterSpec
import android.security.keystore.KeyProperties
import android.util.Base64
import javax.crypto.Cipher
import javax.crypto.KeyGenerator
import javax.crypto.spec.SecretKeySpec
object AESCrypt {
private const val ALGORITHM = "AES"
private const val KEY = "your_secret_key"
@Throws(Exception::class)
fun encrypt(value: String): String {
val keySpec = SecretKeySpec(KEY.toByteArray(), ALGORITHM)
val cipher = Cipher.getInstance(ALGORITHM)
cipher.init(Cipher.ENCRYPT_MODE, keySpec)
val encryptedBytes = cipher.doFinal(value.toByteArray())
return Base64.encodeToString(encryptedBytes, Base64.DEFAULT)
}
@Throws(Exception::class)
fun decrypt(encrypted: String?): String {
val keySpec = SecretKeySpec(KEY.toByteArray(), ALGORITHM)
val cipher = Cipher.getInstance(ALGORITHM)
cipher.init(Cipher.DECRYPT_MODE, keySpec)
val encryptedBytes = Base64.decode(encrypted, Base64.DEFAULT)
val decryptedBytes = cipher.doFinal(encryptedBytes)
return String(decryptedBytes)
}
//generates a secret key for the encryption functions to use
//TODO: Generating keys requires a higher API level. ask someone if this is okay.
}

View File

@@ -26,14 +26,4 @@ data class DeviceData(
var natsToken: String = "",
@get:PropertyName("natsTokenExpiry") @set:PropertyName("natsTokenExpiry")
var natsTokenExpiry: String = "",
@get:PropertyName("deviceUpdateAvailable") @set:PropertyName("deviceUpdateAvailable")
var deviceUpdateAvailable: Boolean = false,
@get:PropertyName("updatePath") @set:PropertyName("updatePath")
var updatePath: String = "",
@get:PropertyName("deviceVersion") @set:PropertyName("deviceVersion")
var deviceVersion: String = "",
@get:PropertyName("globalUpdateDone") @set:PropertyName("globalUpdateDone")
var globalUpdateDone: Boolean = false,
@get:PropertyName("globalUpdateIgnore") @set:PropertyName("globalUpdateIgnore")
var globalUpdateIgnore: Boolean = false,
)

View File

@@ -78,7 +78,6 @@ data class HemoCubeTestData(
var prdClassification: String = "",
var deviceRatioClass: String = "",
var slopeRatioClass: String = "",
var borderlineMethod2Class: String = "",
var errorMessages: String = "",
var batteryLevel: String = "",
var batteryCapacity: String = "",

View File

@@ -0,0 +1,29 @@
package com.example.hpostesting.di
import android.content.Context
import com.example.hpostesting.data.repository.DatabaseRepository
import com.example.hpostesting.data.dao.UserDao
import com.example.hpostesting.domain.SaveRawData
import com.example.hpostesting.domain.SaveRawDataTest
import com.example.hpostesting.presentation.testRight.TestRightViewModel
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.android.components.ViewModelComponent
import dagger.hilt.android.qualifiers.ApplicationContext
@Module
@InstallIn(ViewModelComponent::class)
object ViewModelModule {
@Provides
fun provideTestRightViewModel(
saveRawData: SaveRawData,
saveRawDataTest: SaveRawDataTest,
databaseRepository: DatabaseRepository,
userDao: UserDao,
context: Context
): TestRightViewModel {
return TestRightViewModel(saveRawData, saveRawDataTest, databaseRepository, userDao, context)
}
}

View File

@@ -109,7 +109,20 @@ class DatabaseRepository @Inject constructor(
}
override suspend fun addTestToDatabase(data: UserData?): Response<String> {
TODO("Not yet implemented")
return try {
val userdata =
db.collection("patientData").whereEqualTo("_id", data!!._id).get().await()
if (userdata.documents.isNotEmpty()) {
userdata.documents.forEach {
db.collection("patientData").document(it.id).update("testStatus", true)
}
}
db.collection("testData").add(data).await()
Response.Success(data._id)
} catch (e: Exception) {
Firebase.crashlytics.recordException(e)
Response.Error(e)
}
}
override suspend fun addTestToDatabaseforBufferCheck(data: BufferCheckData?): Response<String> {
@@ -234,22 +247,4 @@ class DatabaseRepository @Inject constructor(
override fun <UserData> addTestToDatabase(testDetails: UserData): Any {
TODO("Not yet implemented")
}
override suspend fun addTestToDatabasefornew(data: HemoCubeTestData?): Response<String> {
return try {
val userdata =
db.collection("patientData").whereEqualTo("_id", data!!._id).get().await()
if (userdata.documents.isNotEmpty()) {
userdata.documents.forEach {
db.collection("patientData").document(it.id).update("testStatus", true)
}
}
db.collection("testData").add(data).await()
Response.Success(data._id)
} catch (e: Exception) {
Firebase.crashlytics.recordException(e)
Response.Error(e)
}
}
}

View File

@@ -25,7 +25,6 @@ import okhttp3.ResponseBody
interface Repository {
suspend fun addTestToDatabase(data: HemoCubeTestData?): Response<String>
suspend fun addTestToDatabasefornew(data: HemoCubeTestData?): Response<String>
suspend fun addTestToDatabase(data: UserData?): Response<String>

View File

@@ -22,8 +22,6 @@ import com.example.hpostesting.domain.LogFileManager
import com.example.hpostesting.domain.LogFileManagerImpl
import com.example.hpostesting.domain.SaveRawData
import com.example.hpostesting.domain.SaveRawDataTest
import com.example.hpostesting.presentation.UsbServiceListener
import com.example.hpostesting.presentation.UsbServiceListenerImpl
import com.example.hpostesting.util.PropertyProviderImpl
import dagger.Module
import dagger.Provides
@@ -181,10 +179,4 @@ object AppModule {
fun provideLocalFileDataSource(): LocalFileDataSource {
return LocalFileDataSourceImpl()
}
@Provides
@Singleton
fun provideUsbServiceListener(context: Context): UsbServiceListener {
return UsbServiceListenerImpl(context)
}
}

View File

@@ -125,8 +125,13 @@ class NatsManager(datacollector: DashboardActivity) {
if (nc?.status == Connection.Status.CONNECTED) {
Log.d("NATSCONNECTION", "NATS is successfully connected.")
val d = nc?.createDispatcher { msg: Message? ->
println("Nats dispatcher $msg")
}
nc?.subscribe("device.hpos.${deviceId}.ping")
// Log.d(TAG, "Nats subscribed with ping-"+d)
nc?.publish(
"server.hpos.${deviceId}.ping",
"ALIVE".toByteArray(StandardCharsets.UTF_8)
@@ -135,10 +140,7 @@ class NatsManager(datacollector: DashboardActivity) {
"server.hpos.${deviceId}.health",
"ALIVE".toByteArray(StandardCharsets.UTF_8)
)
val d = nc?.createDispatcher { msg: Message? ->
println("Nats dispatcher $msg")
Log.d(TAG, "Nats dispatcher--$msg")
}
d?.subscribe("device.hpos.${deviceId}.ping") { msg ->
val response = String(msg.data, StandardCharsets.UTF_8)
datacollector.setResponse(response)
@@ -170,11 +172,10 @@ class NatsManager(datacollector: DashboardActivity) {
println("Message received (up to 100 times): $response")
}
d?.subscribe("device.hpos.${deviceId}.checkUpdate") { msg ->
d?.subscribe("device.hpos.${deviceId}.checkupdate") { msg ->
val response = String(msg.data, StandardCharsets.UTF_8)
datacollector.setResponse(response)
println("Message received (up to 100 times) on topic checkupdate: $response")
Log.d(TAG, "subscribed msg ${msg} on topic checkupdate")
println("Message received (up to 100 times): $response")
}
} else {
Log.d("NATSCONNECTION", "NATS is not connected. Current status: ${nc?.status}")

View File

@@ -1,26 +0,0 @@
package com.example.hpostesting.presentation
import android.content.Context
import android.util.Log
import android.widget.Toast
class UsbServiceListenerImpl(private val context: Context): UsbServiceListener {
override fun onUsbRead(data: ByteArray?) {
if (data != null) {
val receivedData = String(data)
logData(receivedData)
}
}
override fun onUsbError(e: Exception?) {
showToast("USB Error: ${e?.message}")
}
private fun showToast(message: String) {
Toast.makeText(context, message, Toast.LENGTH_SHORT).show()
}
private fun logData(data: String) {
Log.d("UsbServiceListener", "Received data from USB: $data")
}
}

View File

@@ -162,8 +162,8 @@ class AssuranceControlsFragment : Fragment() {
}
DataHolder.hemoCubeTestData!!._id = currentUnixTime.toString() + "SMI"
DataHolder.hemoCubeTestData!!.solution = binding.spinnerSolutions.selectedItem.toString()
DataHolder.hemoCubeTestData!!.concentration = binding.spinnerConcentration.selectedItem.toString()
DataHolder.hemoCubeTestData!!.name = "${DataHolder.hemoCubeTestData!!.solution} ${DataHolder.hemoCubeTestData!!.concentration} ${DataHolder.hemoCubeTestData!!.volume}"
DataHolder.hemoCubeTestData!!.name = binding.spinnerConcentration.selectedItem.toString()
"${DataHolder.hemoCubeTestData!!.solution} ${DataHolder.hemoCubeTestData!!.concentration} ${DataHolder.hemoCubeTestData!!.volume}"
DataHolder.selectedTest = UserData()
DataHolder.selectedTest?._id = DataHolder.hemoCubeTestData!!._id

View File

@@ -163,10 +163,10 @@ class AutoDacFragment : Fragment() {
val slData = stringData.split(" ")
if (slData.size > 1) {
val hardwareId = slData[1].trim()
// with(sharedPreferences.edit()) {
// putString(Constants.DEVICE_ID, hardwareId)
// apply()
// }
with(sharedPreferences.edit()) {
putString(Constants.DEVICE_ID, hardwareId)
apply()
}
}
activity?.runOnUiThread {
binding.btnSubmit.visibility = View.VISIBLE

View File

@@ -1,9 +1,9 @@
package com.example.hpostesting.presentation.dashboard
import android.annotation.SuppressLint
import android.app.DownloadManager
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.content.SharedPreferences
import android.net.Uri
import android.os.Build
@@ -23,7 +23,6 @@ import androidx.navigation.ui.setupWithNavController
import com.example.hpostesting.data.Result
import com.example.hpostesting.data.constant.Constants
import com.example.hpostesting.data.constant.LanguageManager
import com.example.hpostesting.data.model.updates.DeviceUpdateRequest
import com.example.hpostesting.presentation.NatsManager
import com.example.hpostesting.presentation.hemocube.HemoCubeViewModel
import com.example.hpostesting.presentation.jig.JigActivity
@@ -31,19 +30,11 @@ import com.google.android.material.navigation.NavigationView
import com.google.firebase.appdistribution.FirebaseAppDistribution
import com.google.firebase.appdistribution.FirebaseAppDistributionException
import com.google.firebase.crashlytics.FirebaseCrashlytics
import com.google.firebase.firestore.ktx.firestore
import com.google.firebase.ktx.Firebase
import dagger.hilt.android.AndroidEntryPoint
import `in`.sminnovations.hpostesting.BuildConfig
import `in`.sminnovations.hpostesting.R
import `in`.sminnovations.hpostesting.databinding.ActivityDashboardBinding
import okhttp3.ResponseBody
import java.io.BufferedInputStream
import java.io.File
import java.io.FileInputStream
import java.io.FileOutputStream
import java.io.InputStream
import java.util.zip.ZipInputStream
interface NatsMessageCallback {
fun onMessageReceived(topic: String, message: String)
@@ -58,7 +49,6 @@ open interface IDataCollector: NatsMessageCallback {
class DashboardActivity : AppCompatActivity(), IDataCollector {
val TAG = "DashboardActivity"
private var isRegistered = false
private lateinit var appBarConfiguration: AppBarConfiguration
private lateinit var binding: ActivityDashboardBinding
lateinit var sharedPreferences: SharedPreferences
@@ -67,7 +57,7 @@ class DashboardActivity : AppCompatActivity(), IDataCollector {
private var downloadId: Long = 0
// TODO: Remove hemocube viewmodel
private val hemocubeViewModel: HemoCubeViewModel by viewModels()
private lateinit var sharedPreference: SharedPreferences
override fun attachBaseContext(newBase: Context?) {
val languageCode = LanguageManager.getSavedLanguage(newBase!!)
LanguageManager.setLocale(newBase, languageCode)
@@ -76,14 +66,13 @@ class DashboardActivity : AppCompatActivity(), IDataCollector {
override fun onMessageReceived(topic: String, message: String) {
// Handle incoming messages from NATS
Log.d(TAG, "Received message on topic $topic: $message")
}
@SuppressLint("SetWorldReadable")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityDashboardBinding.inflate(layoutInflater)
sharedPreferences = this.getSharedPreferences("HEMOCUBE", Context.MODE_PRIVATE)
setContentView(binding.root)
@@ -102,22 +91,15 @@ class DashboardActivity : AppCompatActivity(), IDataCollector {
when (result) {
is Result.Success -> {
// Handle success
val apk = result.data
val file = File(getExternalFilesDir("Updates"), "update.apk")
file.setReadable(true, false) // Ensure the file is readable
apk.byteStream().use { input ->
file.outputStream().use { output ->
input.copyTo(output)
}
}
Log.d("Responsebodyformat", "Responsebodyformat: ")
installApk(file)
Log.e("ApI", "APK URL: $apk")
Toast.makeText(
this,
"${result.data}",
Toast.LENGTH_SHORT
).show()
val apkUrl = result.data
// val apkUrl = "https://dl.dropboxusercontent.com/s/fi/1c3nn7t0co431hicl3hrt/app-debug.apk?rlkey=e4uf13ty1dpcked614vy1aaqp&dl=0"
initiateUpdate(apkUrl.toString())
Log.d("ApI", "APK URL: $apkUrl")
// Toast.makeText(
// this,
// "APK UPLOAD ${result.data}",
// Toast.LENGTH_SHORT
// ).show()
}
is Result.Error -> {
@@ -135,7 +117,6 @@ class DashboardActivity : AppCompatActivity(), IDataCollector {
}
}
val drawerLayout: DrawerLayout = binding.drawerLayout
val navView: NavigationView = binding.navView
val navController = findNavController(R.id.nav_host_fragment_content_dashboard)
@@ -154,8 +135,6 @@ class DashboardActivity : AppCompatActivity(), IDataCollector {
}
}
override fun onCreateOptionsMenu(menu: Menu): Boolean {
// Inflate the menu; this adds items to the action bar if it is present.
menuInflater.inflate(R.menu.dashboard, menu)
@@ -167,15 +146,52 @@ class DashboardActivity : AppCompatActivity(), IDataCollector {
return navController.navigateUp(appBarConfiguration) || super.onSupportNavigateUp()
}
private fun initiateUpdate(responseBody: String) {
val apkUrl = responseBody
if (!isValidHttpUrl(apkUrl)) {
return
}
val request = DownloadManager.Request(Uri.parse(apkUrl))
request.setTitle("App Update")
request.setDescription("Downloading update...")
request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED)
request.setDestinationInExternalFilesDir(this, "Updates", "update.apk")
val downloadManager = getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager
downloadId = downloadManager.enqueue(request)
// Register a BroadcastReceiver to receive the download complete event
// val filter = IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE)
// registerReceiver(downloadReceiver, filter)
}
private fun extractApkUrl(responseBody: ResponseBody): String {
return responseBody.string()
}
private fun isValidHttpUrl(url: String): Boolean {
return url.startsWith("http://") || url.startsWith("https://")
}
private val downloadReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
val id = intent?.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1)
if (id == downloadId) {
installApk()
}
}
}
private fun installApk() {
val file = File(getExternalFilesDir("Updates"), "update.apk")
file.setReadable(true, false) // Ensure the file is readable
private fun installApk(file: File) {
val pInfo = baseContext.packageManager.getPackageInfo(baseContext.packageName, 0)
val uri: Uri = FileProvider.getUriForFile(
this,
"${BuildConfig.APPLICATION_ID}.fileprovider",
"${pInfo}.fileprovider",
file
)
// Create an intent to install the APK
val installIntent = Intent(Intent.ACTION_INSTALL_PACKAGE)
installIntent.data = uri
installIntent.flags = Intent.FLAG_GRANT_READ_URI_PERMISSION or
@@ -191,14 +207,8 @@ class DashboardActivity : AppCompatActivity(), IDataCollector {
}
override fun onDestroy() {
if(isRegistered) {
try {
unregisterReceiver(downloadReceiver)
} catch (e: Exception) {
Log.d("HomeFragment", e.toString())
}
}
super.onDestroy()
// unregisterReceiver(downloadReceiver)
}
override fun onResume() {
@@ -248,16 +258,8 @@ class DashboardActivity : AppCompatActivity(), IDataCollector {
}
override fun setResponse(response: String) {
responses = responses+response+"\n"
println(responses)
// if (response.contains("checkUpdate")) {
// hemocubeViewModel.deviceUpdate(createDeviceUpdateRequestData())
// }
}
private fun createDeviceUpdateRequestData(): DeviceUpdateRequest {
return DeviceUpdateRequest(
serial_no = sharedPreference.getString(Constants.DEVICE_ID, "")
)
}
}

View File

@@ -1,19 +1,12 @@
package com.example.hpostesting.presentation.dashboard
import android.annotation.SuppressLint
import android.app.AlertDialog
import android.app.DownloadManager
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Context.BATTERY_SERVICE
import android.content.Context.RECEIVER_EXPORTED
import android.content.DialogInterface
import android.content.Intent
import android.content.IntentFilter
import android.content.SharedPreferences
import android.net.Uri
import android.os.BatteryManager
import android.os.Build
import android.os.Bundle
import android.util.Base64
import android.util.Log
@@ -21,7 +14,6 @@ import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Toast
import androidx.annotation.RequiresApi
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.navigation.fragment.findNavController
@@ -55,7 +47,6 @@ import com.google.firebase.perf.ktx.performance
import dagger.hilt.android.AndroidEntryPoint
import `in`.sminnovations.hpostesting.R
import `in`.sminnovations.hpostesting.databinding.FragmentHomeBinding
import kotlinx.coroutines.tasks.await
import okhttp3.ResponseBody
import org.json.JSONObject
import java.io.BufferedOutputStream
@@ -67,14 +58,15 @@ import java.text.SimpleDateFormat
import java.util.Calendar
import java.util.Date
import java.util.Locale
import java.util.concurrent.atomic.AtomicBoolean
import java.util.zip.ZipEntry
import java.util.zip.ZipInputStream
@AndroidEntryPoint
class HomeFragment : Fragment() {
private var isRegistered = false
private var downloadId: Long = 0
private lateinit var binding: FragmentHomeBinding
private var _binding: FragmentHomeBinding? = null
private val binding get() = _binding!!
private val viewModel: TestRightViewModel by activityViewModels()
private val hemoCubeViewModel: HemoCubeViewModel by activityViewModels()
private lateinit var rvAdapter: UserListAdapter
@@ -86,12 +78,21 @@ class HomeFragment : Fragment() {
private var isTokenAvailable = false
private var natsToken: String = ""
private var deviceId: String = ""
private lateinit var sharedPreference: SharedPreferences
private lateinit var sharedPreference: SharedPreferences
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?,
): View {
binding = FragmentHomeBinding.inflate(inflater, container, false)
): View? {
_binding = FragmentHomeBinding.inflate(inflater, container, false)
Log.d("OnCreate Home Fragment", "HomeFragment calls")
// Check if _binding is null
if (_binding == null) {
// Handle the case where binding could not be initialized
// You may want to log an error or return a default view in this case
return super.onCreateView(inflater, container, savedInstanceState)
}
sharedPreference = requireContext().getSharedPreferences("HEMOCUBE", Context.MODE_PRIVATE)
DataHolder.selectedTest = null
@@ -99,7 +100,6 @@ class HomeFragment : Fragment() {
return binding.root
}
@RequiresApi(Build.VERSION_CODES.P)
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
@@ -107,10 +107,8 @@ class HomeFragment : Fragment() {
binding.labelQuickCapture.visibility = View.VISIBLE
binding.btnQuickCapture.visibility = View.VISIBLE
}
getDeviceId()
checkUnprocessedCSVData()
checkForUpdate()
viewModel.allUserData.observe(viewLifecycleOwner) { userData ->
deleteIncompleteRegistrations(userData)
}
@@ -130,14 +128,15 @@ class HomeFragment : Fragment() {
binding.rvOrderOffline.adapter = adapter
}
}
// hemoCubeViewModel.deviceData.observe(viewLifecycleOwner) { deviceData ->
// val devicelist = mutableListOf<DeviceData>()
// if (deviceData != null) {
// devicelist.add(DeviceData(deviceData.deviceId))
// }
//
// }
hemoCubeViewModel.deviceData.observe(viewLifecycleOwner) { deviceData ->
val devicelist = mutableListOf<DeviceData>()
if (deviceData != null) {
devicelist.add(DeviceData(deviceData.deviceId))
}
}
viewModel.networkStatusLiveData?.observe(viewLifecycleOwner) { isConnected ->
Log.d("NETWORK OBSERVE", "OBSERVE CALLED")
if (isConnected) {
binding.internetAvailableCL.visibility = View.VISIBLE
binding.internetNotAvailableCL.visibility = View.GONE
@@ -149,7 +148,6 @@ class HomeFragment : Fragment() {
checkForTokenAndUpdate()
}
// Now re-subscribe to allUserData
hemoCubeViewModel.allUserData.observe(viewLifecycleOwner) { originalUserDataList ->
Log.d("LOCAL_DB OBSERVE", "OBSERVE CALLED")
@@ -160,34 +158,31 @@ class HomeFragment : Fragment() {
": USER DATA",
originalUserDataList.count().toString() + " : " + userData._id
)
var accessToken = sharedPreference.getString(Constants.ACCESS_TOKEN, "").toString()
// Upload results after processing all userData to avoid duplicates and ensure all modifications are done
if(accessToken.isNotEmpty()) {
if (!userData.molbioFlag && isTokenAvailable && Constants.MOLBIO_INTEGRATION) {
val currentTimeFormatted = SimpleDateFormat(
"yyyy-MM-dd'T'HH:mm:ssZZZZZ",
Locale.getDefault()
).format(Calendar.getInstance().time)
val bufferIntensityThreshold =
Constants.BUFFER_INTENSITY_THRESHOLDS[userData.deviceId]?.toString()
?: "defaultThreshold" // Handle possible nulls safely
resultList.results?.add(
MolbioV2Result(
rawData = userData,
analysisId = userData._id,
analysisDate = currentTimeFormatted,
analysisStatus = userData.classificationResult
?: "defaultStatus", // Handle possible nulls
thresholds = bufferIntensityThreshold,
interpretation = userData.classificationResult
?: "defaultInterpretation", // Handle possible nulls
testId = userData._id,
testTime = currentTimeFormatted,
collectionTime = currentTimeFormatted,
expiryTime = currentTimeFormatted
)
if (!userData.molbioFlag && isTokenAvailable && Constants.MOLBIO_INTEGRATION) {
val currentTimeFormatted = SimpleDateFormat(
"yyyy-MM-dd'T'HH:mm:ssZZZZZ",
Locale.getDefault()
).format(Calendar.getInstance().time)
val bufferIntensityThreshold =
Constants.BUFFER_INTENSITY_THRESHOLDS[userData.deviceId]?.toString()
?: "defaultThreshold" // Handle possible nulls safely
resultList.results?.add(
MolbioV2Result(
rawData = userData,
analysisId = userData._id,
analysisDate = currentTimeFormatted,
analysisStatus = userData.classificationResult
?: "defaultStatus", // Handle possible nulls
thresholds = bufferIntensityThreshold,
interpretation = userData.classificationResult
?: "defaultInterpretation", // Handle possible nulls
testId = userData._id,
testTime = currentTimeFormatted,
collectionTime = currentTimeFormatted,
expiryTime = currentTimeFormatted
)
}
)
}
}
Log.d("USER DATA LIST SIZE", resultList.results?.count().toString())
@@ -198,22 +193,17 @@ class HomeFragment : Fragment() {
if (userData != null) {
if (!userData.localFlag) {
hemoCubeViewModel.bulkAddResultTestToDb(userData)
userData.localFlag = true
}
}
}
resultList.results?.forEach { result ->
result.rawData?.let { sanitizeDoubleValues(it) }
}
// Then, check if there are any results to upload.
// Upload results after processing all userData to avoid duplicates and ensure all modifications are done
if (resultList.results?.isNotEmpty() == true) {
hemoCubeViewModel.uploadResult(resultList)
Log.d("resultcount1", "Uploading sanitized results")
}
}
} else {
binding.internetAvailableCL.visibility = View.GONE
binding.pendingTest.visibility = View.GONE
@@ -237,7 +227,7 @@ class HomeFragment : Fragment() {
}
binding.uploadData.setOnClickListener {
// showUploadDialog(requireContext())
showUploadDialog(requireContext())
}
hemoCubeViewModel.allUserData.observe(viewLifecycleOwner) { userData ->
@@ -286,33 +276,69 @@ class HomeFragment : Fragment() {
}
@RequiresApi(Build.VERSION_CODES.P)
private fun checkForTokenAndUpdate() {
var accessToken = sharedPreference.getString(Constants.ACCESS_TOKEN, "").toString()
var password = sharedPreference.getString(Constants.DEVICE_PASSWORD_API, "").toString()
var userID = sharedPreference.getString(Constants.DEVICE_ID_API, "").toString()
deviceId = sharedPreference.getString(Constants.DEVICE_ID, "").toString()
if(userID.isNotEmpty() && password.isNotEmpty()) {
Log.d("istoken",isTokenAvailable.toString())
if (!isTokenAvailable) {
Log.d("istoken1",isTokenAvailable.toString())
hemoCubeViewModel.login(createLoginRequestData(userID, password))
isTokenAvailable = true
Log.e("idpass", userID)
Log.e("idpass", password)
Log.e("idpass", deviceId)
if (userID.isNotEmpty() && password.isNotEmpty()) {
if (!isTokenAvailable) {
hemoCubeViewModel.login(createLoginRequestData(userID, password))
} else {
if (isTokenExpired(accessToken)) {
hemoCubeViewModel.login(createLoginRequestData(userID, password))
} else {
isTokenAvailable = true
}else if(isTokenAvailable){
Log.d("istoken7",isTokenAvailable.toString())
isTokenAvailable = true
hemoCubeViewModel.startPeriodicCheckUpdate()
hemoCubeViewModel.checkUpdate(createCheckUpdateRequestData())
}else{
if(isTokenExpired(accessToken)) {
Log.d("istoken8",isTokenAvailable.toString())
hemoCubeViewModel.login(createLoginRequestData(userID, password))
}
}
} else if (userID.isEmpty() && password.isEmpty() && deviceId.isNotEmpty()) {
hemoCubeViewModel.deviceUpdate(createDeviceUpdateRequestData())
hemoCubeViewModel.uploadLogs()
hemoCubeViewModel.startPeriodicCheckUpdate()
hemoCubeViewModel.downloadClientCertificate()
}
}
} else if (deviceId.isNotEmpty()) {
fetchDeviceCredentials()
} else {
// This code will execute after credentials have been successfully fetched and stored.
userID = sharedPreference.getString("username", "").toString()
password = sharedPreference.getString("password", "").toString()
accessToken = sharedPreference.getString(Constants.ACCESS_TOKEN, "").toString()
if (accessToken.isEmpty()) {
hemoCubeViewModel.login(createLoginRequestData(userID, password))
} else {
// Continue with your existing logic if the token is not empty.
isTokenAvailable = true
hemoCubeViewModel.deviceUpdate(createDeviceUpdateRequestData())
hemoCubeViewModel.uploadLogs()
hemoCubeViewModel.startPeriodicCheckUpdate()
}
}
// } else if (fis.available()>0){
// val buffer = ByteArray(fis.available())
// fis.read(buffer)
// fis.close()
// val encryptedData = String(buffer)
// val parts = encryptedData.split(",".toRegex()).dropLastWhile { it.isEmpty() }
// .toTypedArray()
// val deviceID = parts[0]
// val decryptedUsername = decrypt(parts[1])
// val decryptedPassword = decrypt(parts[2])
// if (accessToken.isEmpty()) {
// hemoCubeViewModel.login(createLoginRequestData(decryptedUsername, decryptedPassword))
//}
// else {
// // Continue with your existing logic if the token is not empty.
// isTokenAvailable = true
// hemoCubeViewModel.deviceUpdate(createDeviceUpdateRequestData())
// hemoCubeViewModel.uploadLogs()
// hemoCubeViewModel.startPeriodicCheckUpdate()
// }
//}
else {
Toast.makeText(
requireContext(),
"Contact Help and get your device provision done",
@@ -324,9 +350,6 @@ class HomeFragment : Fragment() {
when (response) {
is Result.Success -> {
updateTokens(response)
response.data.data?.accessToken
isTokenAvailable = true
Log.d("istoken2",isTokenAvailable.toString())
}
is Result.Error -> {
@@ -348,36 +371,6 @@ class HomeFragment : Fragment() {
}
}
hemoCubeViewModel.resultUpload.observe(viewLifecycleOwner) {
when (it) {
is Result.Success -> {
Log.d("success,","uploded")
it.data.data?.forEach { id ->
id.rawData?.let { it1 ->
hemoCubeViewModel.updateMolbioFlag(
it1._id
)
}
}
Toast.makeText(activity, "Molbio Result is successfully uploaded", Toast.LENGTH_LONG)
.show()
}
is Result.Error -> {
binding.btnSubmit.visibility = View.VISIBLE
//Remove this line of code while deploying to IOCL
it.exception.let { message ->
Toast.makeText(activity, "$message", Toast.LENGTH_LONG)
.show()
Log.d("resultuploadfail", message.toString())
}
}
else -> {}
}
}
hemoCubeViewModel.uploadLogs.observe(viewLifecycleOwner) { response ->
when (response) {
is Result.Success -> {
@@ -386,14 +379,13 @@ class HomeFragment : Fragment() {
// "Log uploaded ${response.data.data?.filename}",
// Toast.LENGTH_SHORT
// ).show()
}
is Result.Error -> {
response.exception.let { message ->
Toast.makeText(
activity,
"$message",
"An error occurred in uploading logs: $message",
Toast.LENGTH_LONG
)
.show()
@@ -408,84 +400,30 @@ class HomeFragment : Fragment() {
}
}
hemoCubeViewModel.checkUpdate.observe(viewLifecycleOwner) { response ->
when (response) {
is Result.Success -> {
val updatedversion = response.data.data?.version.toString()
val currentversion =
context?.let { ctx ->
val packageInfo = ctx.packageManager.getPackageInfo(ctx.packageName, 0)
val versionName = packageInfo.versionName
val versionCode: Long = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
// From Android P (API level 28), versionCode is deprecated and you should use longVersionCode instead.
packageInfo.longVersionCode
} else {
// For older Android versions, use versionCode (cast it to Long for consistency).
packageInfo.versionCode.toLong()
}
// Use versionName and versionCode as needed
Log.d("AppInfo", "Version Name: $versionName, Version Code: $versionCode")
}
Log.d("versionnow",currentversion.toString())
Log.d("versionnow",updatedversion.toString())
if(updatedversion > currentversion.toString()){
hemoCubeViewModel.deviceUpdate(createDeviceUpdateRequestData())
Toast.makeText(
activity,
"new version ${response.data.data?.version} Available",
Toast.LENGTH_LONG
)
.show()
}else{
Toast.makeText(
activity,
"App is Up to date",
Toast.LENGTH_LONG
)
.show()
}
}
is Result.Error -> {
// response.exception.let { message ->
//// Toast.makeText(
//// activity,
//// "$message",
//// Toast.LENGTH_LONG
//// )
//// .show()
// }
}
is Result.Loading -> {
}
else -> {
}
}
}
hemoCubeViewModel.downloadcertificate.observe(viewLifecycleOwner) { response ->
when (response) {
is Result.Success -> {
val url = response.data
val downloadDirectory = "NATS"
val fileName = "nats_certificate.zip"
val unzipDirectoryPath = requireContext().getExternalFilesDir(null)?.absolutePath + "/$downloadDirectory"
// Check if the directory with extracted files exists.
val directory = File(unzipDirectoryPath)
if (directory.exists() && directory.isDirectory) {
// Assuming if the directory exists, the certificate has been downloaded and extracted.
// You can add more specific checks here, e.g., checking for specific files within the directory.
Toast.makeText(requireContext(), "NATS certificate already downloaded and extracted.", Toast.LENGTH_SHORT).show()
return@observe
}
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 -> {
@@ -507,23 +445,49 @@ class HomeFragment : Fragment() {
}
}
hemoCubeViewModel.resultUpload.observe(viewLifecycleOwner) {
when (it) {
is Result.Success -> {
Log.d("MOLBIO UPLOAD", "RESULT SUCCESS")
it.data.data?.forEach { id ->
id.rawData?.let { it1 ->
hemoCubeViewModel.updateMolbioFlag(
it1._id
)
}
}
}
is Result.Error -> {
binding.btnSubmit.visibility = View.VISIBLE
//Remove this line of code while deploying to IOCL
it.exception.let { message ->
Toast.makeText(activity, "An error occurred: $message", Toast.LENGTH_LONG)
.show()
}
}
else -> {
}
}
}
}
private fun isTokenExpired(token: String): Boolean {
if(token.isNotEmpty()) {
val parts = token.split("\\.".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
val decodedPayload = String(Base64.decode(parts[1], Base64.DEFAULT))
val jsonPayload = JSONObject(decodedPayload)
val parts = token.split("\\.".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
val decodedPayload = String(Base64.decode(parts[1], Base64.DEFAULT))
val jsonPayload = JSONObject(decodedPayload)
val exp = jsonPayload.optLong("exp", 0)
val currentTimeSeconds = System.currentTimeMillis() / 1000
val exp = jsonPayload.optLong("exp", 0)
val currentTimeSeconds = System.currentTimeMillis() / 1000
return exp <= currentTimeSeconds
}else{
return false
}
return exp <= currentTimeSeconds
}
private fun updateTokens(response: Result.Success<LoginResponse>) {
@@ -536,7 +500,6 @@ class HomeFragment : Fragment() {
apply()
}
isTokenAvailable = true
Log.d("istoken3",isTokenAvailable.toString())
}
private fun createLoginRequestData(userID: String, password: String): LoginRequest {
@@ -647,7 +610,6 @@ class HomeFragment : Fragment() {
}
}
@RequiresApi(Build.VERSION_CODES.P)
private fun fetchDeviceCredentials() {
try {
val db = Firebase.firestore
@@ -674,16 +636,12 @@ class HomeFragment : Fragment() {
)
// Save credentials in SharedPreferences
with(sharedPreference.edit()) {
putString(Constants.DEVICE_ID_API, username)
putString(Constants.DEVICE_PASSWORD_API, password)
putString("username", username)
putString("password", password)
putString(Constants.NATS_TOKEN, natsToken)
apply()
}
if (!isTokenAvailable ) {
Log.d("istoken0", isTokenAvailable.toString())
hemoCubeViewModel.login(createLoginRequestData(username, password))
}
hemoCubeViewModel.login(createLoginRequestData(username, password))
} ?: Log.e("fetchDeviceCredentials", "Failed to parse device data.")
} else {
Log.e("fetchDeviceCredentials", "Document does not exist.")
@@ -872,28 +830,13 @@ class HomeFragment : Fragment() {
}
}
private fun sanitizeDoubleValues(hemoCubeTestData: HemoCubeTestData): HemoCubeTestData {
hemoCubeTestData::class.java.declaredFields.forEach { field ->
if (field.type == Double::class.javaObjectType || field.type == Double::class.javaPrimitiveType) {
field.isAccessible = true
val value = field.get(hemoCubeTestData) as Double?
if (value != null && (value.isInfinite() || value.isNaN())) {
field.set(hemoCubeTestData, 0.0) // Replace with a suitable default value
}
}
}
return hemoCubeTestData
}
private fun showUploadDialog(context: Context) {
val builder = AlertDialog.Builder(context)
builder.setTitle(R.string.upload_db_registration_title)
builder.setMessage(R.string.upload_db_registration_message)
builder.setPositiveButton(R.string.upload) { dialog, _ ->
// uploadLocalDBData(dialog)
uploadLocalDBData(dialog)
}
builder.setNegativeButton(R.string.cancel) { dialog, _ ->
@@ -937,39 +880,6 @@ class HomeFragment : Fragment() {
}
}
hemoCubeViewModel.allUserData.observe(viewLifecycleOwner) { userDataList ->
val resultList = MolbioV2ResultRequest(mutableListOf(MolbioV2Result()))
// userDataList.forEach { userData ->
// if (!userData.molbioFlag && isTokenAvailable) {
// resultList.results?.add(
// MolbioV2Result(
// rawData = userData,
// analysisId = userData._id,
// analysisDate = "2024-02-08 16:33:56", //userData.reportUploadTime,
// analysisStatus = userData.classificationResult,
// thresholds = Constants.BUFFER_INTENSITY_THRESHOLDS[userData.deviceId].toString(),
// interpretation = userData.classificationResult,
// testId = userData._id,
// testTime = userData.testTime,
// collectionTime = "2024-02-08 16:33:56",//userData.testTime,
// expiryTime = "2024-02-08 16:33:56",//userData.testTime,
// )
// )
//
// }
// if (!userData.localFlag) {
// userData.localFlag = true
// hemoCubeViewModel.bulkAddResultTestToDb(userData)
// }
// if (!userData.molbioFlag && isTokenAvailable && Constants.MOLBIO_INTEGRATION) {
// userData.molbioFlag = true
// hemoCubeViewModel.uploadResult(resultList)
// }
// }
dialog.dismiss()
}
hemoCubeViewModel.allKitTestData.observe(viewLifecycleOwner) { kitDataList ->
kitDataList.forEach { userData ->
if (!userData.localFlag) {
@@ -979,6 +889,7 @@ class HomeFragment : Fragment() {
}
dialog.dismiss()
}
}
// private fun downloadLocalDBData(dialog: DialogInterface) {
@@ -1021,6 +932,7 @@ class HomeFragment : Fragment() {
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
private fun downloadCsv() {
@@ -1099,7 +1011,6 @@ class HomeFragment : Fragment() {
}
private fun getDeviceId() {
Log.d("HomeFragmentUSb","getDeviceId")
val handler = activity as? DeviceCommunicationHandler
handler?.sendAndListenToDevice(
HemoCubeCommands.DEVICE_CONFIGURATION_COMMAND,
@@ -1107,9 +1018,9 @@ class HomeFragment : Fragment() {
override fun onUsbRead(data: ByteArray?) {
data?.let {
val receivedData = String(it, Charset.forName("UTF-8"))
Log.d("HomeFragment","USB data"+receivedData)
// Assuming the device ID is the full content of the received data. Adjust if needed.
deviceId = extractDeviceId(receivedData) // Implement this method based on your data format.
deviceId =
extractDeviceId(receivedData) // Implement this method based on your data format.
if (deviceId.isNotEmpty()) {
// Store the deviceId in SharedPreferences
with(sharedPreference.edit()) {
@@ -1126,7 +1037,6 @@ class HomeFragment : Fragment() {
override fun onUsbError(e: Exception?) {
// Handle USB communication error
Log.d("HomeFragment","USB read error"+e.toString())
}
})
@@ -1138,168 +1048,5 @@ class HomeFragment : Fragment() {
val matchResult = regex.find(receivedData)
return matchResult?.groups?.get(1)?.value ?: ""
}
@SuppressLint("SuspiciousIndentation")
private fun checkForUpdate() {
try {
val db = Firebase.firestore
//val deviceId = deviceId
val deviceRef = db.collection("deviceUpdate").document(Constants.DOCUMENT_ID_FOR_UPDATE)
deviceRef.get().addOnSuccessListener { documentSnapshot ->
if (documentSnapshot.exists()) {
val deviceData =
documentSnapshot.toObject(DeviceData::class.java)
// deviceData?.let { data ->
val deviceVersion = deviceData!!.deviceVersion
val deviceUpdateAvailableGlobal= deviceData.deviceUpdateAvailable
val updatePathGlobal= deviceData.updatePath
// if(deviceUpdateAvailableGlobal){
db.collection("devices").whereEqualTo("deviceId", deviceId).get().addOnSuccessListener { documentSnapshotNew ->
if (documentSnapshotNew.documents.isNotEmpty()) {
documentSnapshotNew.documents.forEach{
val documentIn = it.toObject(DeviceData::class.java)
val globalUpdateIgnore = documentIn!!.globalUpdateIgnore
val deviceUpdateAvailable = documentIn.deviceUpdateAvailable
val globalUpdateDone = documentIn.globalUpdateDone
val updatePath = documentIn.updatePath
if(globalUpdateIgnore){
if(deviceUpdateAvailable){
val update = db.collection("devices").document(it.id).update("deviceUpdateAvailable",false)
update.addOnSuccessListener {
Log.d("HomeFragmentUpdate","Device local update done")
initiateUpdate(updatePath)
}.addOnFailureListener{
Log.e("fetchDeviceUpdate", "update fail.")
}
}else{
Log.d("HomeFragmentUpdate","Device update not available")
}
}else{
if(!globalUpdateDone){
val update = db.collection("devices").document(it.id).update("globalUpdateDone",true)
update.addOnSuccessListener {
Log.d("HomeFragmentUpdate","Device global update done")
initiateUpdate(updatePathGlobal)
}.addOnFailureListener{
Log.e("fetchDeviceUpdate", "update fail.")
}
}
}
}
} else {
Log.e("fetchDeviceUpdate", "Document does not exist.")
}
}.addOnFailureListener { exception ->
Log.e("fetchDeviceUpdate", "Error fetching device data", exception)
}
// Toast.makeText(requireActivity()," true -version."+deviceVersion+"updatePath.."+updatePath,Toast.LENGTH_LONG).show()
// Log for debugging
Log.d(
"fetchDeviceCredentials",
"deviceVersion: $deviceVersion, Password: $deviceUpdateAvailableGlobal, updatePath: $updatePathGlobal"
)
// } ?: Log.e("fetchDeviceUpdate", "Failed to parse device data.")
} else {
Log.e("fetchDeviceUpdate", "Document does not exist.")
}
}
.addOnFailureListener { exception ->
Log.e("fetchDeviceUpdate", "Error fetching device data", exception)
}
} catch (e: Exception) {
Log.e("fetchDeviceUpdate", "Error in fetchDeviceUpdate", e)
}
}
private fun initiateUpdate(url: String) {
val apkUrl = url
if (!isValidHttpUrl(apkUrl)) {
return
}
val request = DownloadManager.Request(Uri.parse(apkUrl))
request.setTitle("App Update")
request.setDescription("Downloading update...")
request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED)
request.setDestinationInExternalFilesDir(requireActivity(), "Updates", "update.apk")
val downloadManager = requireActivity().getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager
downloadId = downloadManager.enqueue(request)
// Register a BroadcastReceiver to receive the download complete event
val filter = IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
isRegistered = true
requireActivity().registerReceiver(downloadReceiver, filter, RECEIVER_EXPORTED)
}
}
private fun extractApkUrl(responseBody: ResponseBody): String {
return responseBody.string()
}
private fun isValidHttpUrl(url: String): Boolean {
return url.startsWith("http://") || url.startsWith("https://")
}
private val downloadReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
val id = intent?.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1)
if (id == downloadId) {
installApk()
}
}
}
private fun installApk() {
val file = File(requireActivity().getExternalFilesDir("Updates"), "update.apk")
file.setReadable(true, false) // Ensure the file is readable
val pInfo = requireActivity().baseContext.packageManager.getPackageInfo(requireActivity().baseContext.packageName, 0)
Log.d("HomeFragmentShowInfo",pInfo.packageName.toString())
val uri: Uri = FileProvider.getUriForFile(
requireActivity(),
"${pInfo.packageName}.fileprovider",
file
)
// Create an intent to install the APK
val installIntent = Intent(Intent.ACTION_INSTALL_PACKAGE)
installIntent.data = uri
installIntent.flags = Intent.FLAG_GRANT_READ_URI_PERMISSION or
Intent.FLAG_ACTIVITY_NEW_TASK or
Intent.FLAG_ACTIVITY_CLEAR_TOP
installIntent.putExtra(Intent.EXTRA_NOT_UNKNOWN_SOURCE, true)
// Start the installation
startActivity(installIntent)
Log.d("InstallApk", "Install Intent URI: $uri")
Log.d("InstallApk", "Package Name: ${requireActivity().packageName}")
}
override fun onDestroy() {
if(isRegistered) {
try {
requireActivity().unregisterReceiver(downloadReceiver)
} catch (e: Exception) {
Log.d("HomeFragment", e.toString())
}
}
super.onDestroy()
}
}

View File

@@ -93,7 +93,6 @@ class DeviceProvisionActivity : AppCompatActivity(), DeviceCommunicationHandler
supportActionBar?.setDisplayHomeAsUpEnabled(true)
setupListener()
connectUsb(false)
}
private fun setupListener() {

View File

@@ -15,6 +15,7 @@ import androidx.lifecycle.MutableLiveData
import com.example.hpostesting.data.Result
import com.example.hpostesting.data.constant.Constants
import com.example.hpostesting.data.constant.HemoCubeCommands
import com.example.hpostesting.data.encryption.AESCrypt
import com.example.hpostesting.data.model.deviceprovision.DeviceProvisionRequest
import com.example.hpostesting.data.model.patient.DeviceData
import com.example.hpostesting.presentation.UsbServiceListener
@@ -23,6 +24,7 @@ import com.google.firebase.crashlytics.ktx.crashlytics
import com.google.firebase.ktx.Firebase
import `in`.sminnovations.hpostesting.databinding.FragmentDeviceProvisionBinding
class DeviceProvisionFragment : Fragment() {
private var resultData: String = ""
private lateinit var binding: FragmentDeviceProvisionBinding
@@ -116,16 +118,16 @@ class DeviceProvisionFragment : Fragment() {
password = response.data.data?.credentials?.password.toString(),
deviceProvisionResponse = response.data.data.toString(),
natsToken = response.data.data?.device?.deviceUser?.natsToken.toString(),
natsTokenExpiry = response.data.data?.device?.deviceUser?.natsTokenExpiry.toString(),
globalUpdateIgnore = false,
globalUpdateDone = false,
deviceUpdateAvailable = false
natsTokenExpiry = response.data.data?.device?.deviceUser?.natsTokenExpiry.toString()
)
)
// viewModel.addDeviceId(DeviceData(deviceId = sharedPreferences.getString(Constants.DEVICE_ID, "").toString()))
Log.e("idpass", response.toString())
Log.e("idpass", response.data.data?.credentials?.username.toString())
Log.e("idpass", deviceProvisionResponse)
saveDataToLocalFile(response.data.data?.credentials?.username.toString(), response.data.data?.credentials?.password.toString())
} else {
Toast.makeText(
activity,
@@ -221,4 +223,18 @@ class DeviceProvisionFragment : Fragment() {
}
}
}
//saves username and password to a local file after encrypting it.
private fun saveDataToLocalFile( username : String, password : String){
val deviceID = sharedPreferences.getString(Constants.DEVICE_ID, "").toString();
val encryptedUsername = AESCrypt.encrypt(username)
val encryptedPassword = AESCrypt.encrypt(password)
val encryptedData = deviceID + "\n" + encryptedUsername + "\n" + encryptedPassword
val fileOutputStream = requireContext().openFileOutput("credentials.txt", Context.MODE_PRIVATE)
fileOutputStream.write(encryptedData.toByteArray())
fileOutputStream.close()
}
}

View File

@@ -1,6 +1,5 @@
package com.example.hpostesting.presentation.hemocube
import android.annotation.SuppressLint
import android.content.Context
import android.content.Intent
import android.content.SharedPreferences
@@ -91,11 +90,8 @@ class HemoCubeFragment : Fragment() {
observeViewModel()
}
@SuppressLint("SetTextI18n")
private fun initViews() {
binding.btnSubmit.setOnClickListener {
binding.btnSubmit.isEnabled = false
binding.btnSubmit.isClickable = false
activity?.runOnUiThread {
binding.progressBar.visibility = View.VISIBLE
binding.btnSubmit.visibility = View.GONE
@@ -110,6 +106,8 @@ class HemoCubeFragment : Fragment() {
binding.nameEditText.visibility = View.GONE
binding.tvTitle.visibility = View.GONE
binding.btnGo.visibility = View.GONE
// binding.btnSubmit.isEnabled = false
// binding.btnSubmit.isClickable = false
binding.btnPlacebuffer.visibility = View.GONE
binding.tvName.text = "Name: ${testDetails?.name}\n ID: ${testDetails?._id}"
@@ -149,7 +147,6 @@ class HemoCubeFragment : Fragment() {
hemoCubeViewModel.fireBaseUpload.observe(viewLifecycleOwner) { result ->
if (result == "Success") {
uploadedToCloud = true
var accessToken = sharedPreferences.getString(Constants.ACCESS_TOKEN, "").toString()
showToast(R.string.test_upload)
if (Constants.MOLBIO_INTEGRATION) {
hemoCubeViewModel.resultUpload.observe(viewLifecycleOwner) {
@@ -162,8 +159,6 @@ class HemoCubeFragment : Fragment() {
)
}
handleReadingFinish()
hemoCubeViewModel.uploadLogs()
hemoCubeViewModel.downloadClientCertificate()
}
is Result.Error -> {
@@ -590,7 +585,8 @@ class HemoCubeFragment : Fragment() {
led1Gain4 = resultLines[5].split(' ')[1].trim().toDoubleOrNull()!!
led2Gain4 = resultLines[6].split(' ')[1].trim().toDoubleOrNull()!!
led3Gain4 = resultLines[7].split(' ')[1].trim().toDoubleOrNull()!!
led4Gain4 = resultLines[8].split(' ')[1].split('\r')[0].trim().toDoubleOrNull()!!
led4Gain4 =
resultLines[8].split(' ')[1].split('\r')[0].trim().toDoubleOrNull()!!
}
finishReading()
@@ -768,7 +764,6 @@ class HemoCubeFragment : Fragment() {
val led3Average = log10(led3BufferForDevice.div(led3SampleForDevice))
val led4Average = log10(led4BufferForDevice.div(led4SampleForDevice))
val deviceRatio = led2Average / led1Average
val borderlineMetric = (led1Average - led2Average) / deviceRatio
if (led1BufferForDevice < Constants.BUFFER_INTENSITY_THRESHOLDS[deviceHardwareId]?.get(0)
?.get(0)!!
@@ -866,7 +861,7 @@ class HemoCubeFragment : Fragment() {
}
}
val absorbanceLowerLimit = 0.0
var absorbanceLowerLimit = 0.0
if (led1Average < absorbanceLowerLimit || led2Average < absorbanceLowerLimit || led3Average < absorbanceLowerLimit || led4Average < absorbanceLowerLimit) {
validationError = true
activity?.runOnUiThread {
@@ -913,13 +908,8 @@ class HemoCubeFragment : Fragment() {
.toString() + ", " + currentDeviceData?.coefficients?.get(1).toString()
this.prdClassification = absorbanceBasedClassification(predictedDenovixRatio)
this.deviceRatioClass = deviceRatioClassification(deviceRatio)
this.borderlineMethod2Class = reclassifyWithBorderlineMethod2(deviceRatio, deviceRatioBorderlineThresholds(deviceRatio), led2Average)
this.slopeRatioClass = slopeClass
this.classificationResult = findResultWithAdditionalMethods(
deviceRatio,
deviceRatioClass,
borderlineMetric
)
this.classificationResult = deviceRatioClass
hemoCubeViewModel.messages.postValue(
"${this.classificationResult} \n Device Ratio: ${
"%.3f".format(
@@ -960,48 +950,17 @@ class HemoCubeFragment : Fragment() {
}
}
fun reclassifyWithBorderlineMethod2(deviceRatio: Double?, deviceRatioClass: String?, led2Average: Double?): String {
try {
if (deviceRatio != null && led2Average != null) {
if (deviceRatioClass == "Negative Borderline") {
return if (led2Average >= 0.15)
"Borderline. Normal"
else
"Borderline. Sickle Cell Trait"
}
if (deviceRatioClass == "Positive for Sickle Cell. HPLC for Confirmation") {
return if (led2Average >= 0.19)
"Borderline. Sickle Cell Trait"
else
"Borderline. Sickle Cell Disease"
}
}
} catch (e: Exception) {
handleException(e)
return "Error"
}
return deviceRatioClass.toString()
}
fun findResultWithAdditionalMethods(
deviceRatio: Double?,
deviceRatioClass: String?,
borderlineMetric: Double?,
slopeRatio: Double?,
): String {
try {
// hemoCubeViewModel.messages.postValue("post classification checks")
if (deviceRatio != null && borderlineMetric != null) {
if (deviceRatioClass == "Negative Borderline") {
return if (borderlineMetric >= 2.4)
"Borderline. Normal"
else
"Borderline. Sickle Cell Trait"
}
if (deviceRatioClass == "Positive for Sickle Cell. HPLC for Confirmation") {
return if (borderlineMetric >= 1.34)
"Borderline. Sickle Cell Trait"
else
"Borderline. Sickle Cell Disease"
if (deviceRatio != null) {
if (slopeRatio != null) {
if (deviceRatioClass == "Normal" && slopeRatio > 45.0)
return "Negative Borderline, Repeat Test"
}
}
} catch (e: Exception) {
@@ -1011,46 +970,20 @@ class HemoCubeFragment : Fragment() {
return deviceRatioClass.toString()
}
fun deviceRatioBorderlineThresholds(ratio: Double?): String {
try {
if (ratio != null) {
val roundedRatio = String.format("%.3f", ratio).toDouble()
if (roundedRatio >= 0.11 && roundedRatio < 0.237) {
// setSubtitleTextColor(R.color.green_2)
return "Normal"
}
if (roundedRatio in 0.237..0.242)
return "Negative Borderline"
if (roundedRatio in 0.242..0.318)
return "Sickle Cell Trait"
if (roundedRatio >= 0.318 && roundedRatio < 0.356)
return "Positive for Sickle Cell. HPLC for Confirmation"
if (roundedRatio in 0.356..0.7)
return "Sickle Cell Disease"
} else {
return "Invalid"
}
} catch (e: Exception) {
handleException(e)
return "Error"
}
return "Invalid"
}
fun deviceRatioClassification(ratio: Double?): String {
try {
if (ratio != null) {
if (ratio in 0.16..0.23) {
if (ratio in 0.016..0.22) {
// setSubtitleTextColor(R.color.green_2)
return "Normal"
}
if (ratio in 0.23..0.25)
if (ratio in 0.22..0.24)
return "Negative Borderline"
if (ratio in 0.25..0.31)
if (ratio in 0.24..0.32)
return "Sickle Cell Trait"
if (ratio in 0.31..0.36)
if (ratio in 0.32..0.37)
return "Positive for Sickle Cell. HPLC for Confirmation"
if (ratio in 0.36..0.7)
if (ratio in 0.37..0.56)
return "Sickle Cell Disease"
} else {
return "Invalid"

View File

@@ -88,7 +88,6 @@ class HemoCubeViewModel @Inject constructor(
private val _networkStatusLiveData = NetworkStatusLiveData(context)
val allUserData = hemoCubeDao.getAll()
val allPendingUserToUpload = MutableLiveData<List<HemoCubeTestData>>()
val allKitTestData = hemoCubeBufferDao.getAll()
val deviceData = MutableLiveData<DeviceData?>()
@@ -138,14 +137,7 @@ class HemoCubeViewModel @Inject constructor(
fun uploadResult(molbioV2ResultRequest: MolbioV2ResultRequest) = viewModelScope.launch {
resultUpload.postValue(Result.Loading())
repository.uploadResults(molbioV2ResultRequest).let {
resultUpload.postValue(it)
}
}
fun uploadResultfornew(molbioV2ResultRequest: MolbioV2ResultRequest) = viewModelScope.launch {
resultUpload.postValue(Result.Loading())
Log.d("API CALL", "UPLOADED RESULT")
repository.uploadResults(molbioV2ResultRequest).let {
resultUpload.postValue(it)
}
@@ -195,9 +187,6 @@ class HemoCubeViewModel @Inject constructor(
}
}
}
fun uploadPendingUser() = viewModelScope.launch {
allPendingUserToUpload.postValue(hemoCubeDao.getPendingUser(false))
}
fun uploadHemoCubeResultToDatabaseForBufferCheck(
isOnline: Boolean,
@@ -302,7 +291,6 @@ class HemoCubeViewModel @Inject constructor(
testDetails?.prdClassification = DataHolder.hemoCubeTestData?.prdClassification.toString()
testDetails?.deviceRatioClass = DataHolder.hemoCubeTestData?.deviceRatioClass.toString()
testDetails?.slopeRatioClass = DataHolder.hemoCubeTestData?.slopeRatioClass.toString()
testDetails?.borderlineMethod2Class = DataHolder.hemoCubeTestData?.borderlineMethod2Class.toString()
testDetails?.errorMessages = DataHolder.hemoCubeTestData?.errorMessages.toString()
testDetails?.batteryLevel = DataHolder.hemoCubeTestData?.batteryLevel.toString()
testDetails?.batteryCapacity = DataHolder.hemoCubeTestData?.batteryCapacity.toString()
@@ -375,7 +363,7 @@ class HemoCubeViewModel @Inject constructor(
userData.reportUploadTime = SimpleDateFormat(
"yyyy-MM-dd HH:mm:ss", Locale.getDefault()
).format(Calendar.getInstance().time)
when (repository.addTestToDatabasefornew(userData)) {
when (repository.addTestToDatabase(userData)) {
is Response.Success -> {
fireBaseBulkUpload.postValue("Success")
updateLocalFlag(userData._id)
@@ -420,8 +408,6 @@ class HemoCubeViewModel @Inject constructor(
Log.e("Testdb", "Error uploading data to Firestore: $response")
fireBaseUpload.postValue("Error")
}
else -> {}
}
} catch (e: Exception) {
Log.e("Testdb", "Exception during data upload: ${e.message}")

View File

@@ -11,14 +11,12 @@ 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
@@ -45,7 +43,6 @@ 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) {
@@ -85,7 +82,6 @@ open class HemocubeActivity : AppCompatActivity() {
super.attachBaseContext(newBase)
}
@RequiresApi(Build.VERSION_CODES.O)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityHemocubeBinding.inflate(layoutInflater)
@@ -110,7 +106,6 @@ 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
@@ -130,7 +125,6 @@ open class HemocubeActivity : AppCompatActivity() {
}
}
@RequiresApi(Build.VERSION_CODES.O)
@SuppressLint("MutableImplicitPendingIntent")
private fun requestUserPermission(manager: UsbManager, device: UsbDevice) {
val mPendingIntent: PendingIntent
@@ -148,21 +142,15 @@ open class HemocubeActivity : AppCompatActivity() {
}
val filter = IntentFilter(Constants.HEMOCUBE_USB_PERMISSION)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
registerReceiver(broadcastReceiver, filter, RECEIVER_EXPORTED)
}else{
registerReceiver(broadcastReceiver, filter)
}
registerReceiver(broadcastReceiver, filter)
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

@@ -33,53 +33,32 @@ class UsbService : Service() {
var bus: UsbServiceListener? = null
fun connect(driver: UsbSerialDriver, connection: UsbDeviceConnection) {
try {
mPort = driver.ports[0]
mPort.open(connection)
mPort = driver.ports[0] // Most devices have just one port (port 0)
mPort.open(connection)
mPort.setParameters(9600, 8, UsbSerialPort.STOPBITS_1, UsbSerialPort.PARITY_NONE)
isUsbConnected = true
Log.d(TAG, "My Usb Connected ${mPort.driver}")
if (mPort.device.vendorId == 6790 && mPort.device.productId == 29987)
mPort.setParameters(115200, 8, UsbSerialPort.STOPBITS_1, UsbSerialPort.PARITY_NONE)
else
mPort.setParameters(9600, 8, UsbSerialPort.STOPBITS_1, UsbSerialPort.PARITY_NONE)
val usbIoManager = SerialInputOutputManager(mPort,
object : SerialInputOutputManager.Listener {
override fun onNewData(data: ByteArray?) {
listener?.onUsbRead(data)
}
isUsbConnected = true
Log.d(TAG, "Usb Connected ${mPort.driver}")
override fun onRunError(e: Exception?) {
Log.e(TAG, "onRunError() called inside eventDrivenWrite()")
listener?.onUsbError(e)
}
val usbIoManager = SerialInputOutputManager(mPort,
object : SerialInputOutputManager.Listener {
override fun onNewData(data: ByteArray?) {
listener?.onUsbRead(data)
}
override fun onRunError(e: Exception?) {
Log.e(TAG, "onRunError() called")
listener?.onUsbError(e)
}
})
usbIoManager.start()
} catch (ioException: IOException) {
Log.e(TAG, "IOException during USB connection: ${ioException.message}", ioException)
listener?.onUsbError(ioException)
} catch (e: Exception) {
Log.e(TAG, "Error connecting USB: ${e.message}", e)
listener?.onUsbError(e)
}
})
usbIoManager.start();
}
fun disconnect() {
try {
if (isUsbConnected) {
mPort.close()
isUsbConnected = false
Log.d(TAG, "USB Port closed successfully:: ${mPort.driver}")
} else {
Log.d(TAG, "USB Port is not connected")
}
} catch (e: IOException) {
Log.e(TAG, "Error closing USB Port: ${e.message}", e)
} catch (e: Exception) {
Log.e(TAG, "An unexpected error occurred: ${e.message}", e)
if (isUsbConnected) {
mPort.close()
isUsbConnected = false;
Log.d(TAG, "My Usb disconnected:: ${mPort.driver}")
}
}

View File

@@ -217,7 +217,6 @@
android:text="no device message"
android:textColor="@color/black"
android:textSize="11sp"
android:visibility="gone"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/error_message" />

View File

@@ -17,4 +17,4 @@
android:icon="@drawable/baseline_settings_24"
android:title="@string/menu_settings" />
</group>
</menu>
</menu>

View File

@@ -1,3 +1,3 @@
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-files-path name="downloaded_file" path="." />
<external-files-path name="Updates" path="." />
</paths>

View File

@@ -1,7 +1,11 @@
package com.example.hpostesting
import android.content.Context
import android.content.SharedPreferences
import com.example.hpostesting.presentation.hemocube.HemoCubeFragment
import com.example.hpostesting.presentation.hemocube.HemocubeActivity
import `in`.sminnovations.hpostesting.databinding.FragmentHemoCubeReferenceBinding
import junit.framework.TestCase
import junit.framework.TestCase.assertEquals
import junit.framework.TestCase.assertNull
import org.junit.Before
@@ -13,9 +17,18 @@ import org.mockito.MockitoAnnotations
class HemoCubeFragmentTest {
@Mock
lateinit var mockContext: Context
@Mock
private lateinit var mockSharedPreferences: SharedPreferences
@Mock
private lateinit var mockActivity: HemocubeActivity // Replace with your actual Activity class
@Mock
private lateinit var mockBinding: FragmentHemoCubeReferenceBinding // Replace with your actual Binding class
private lateinit var hemoCubeFragment: HemoCubeFragment
@Before
@@ -38,7 +51,7 @@ class HemoCubeFragmentTest {
val deviceId = hemoCubeFragment.extractV2HardwareId("SNS HPP1-9000 SNE")
// Assert
assertEquals("HPP1-9000", deviceId)
TestCase.assertEquals("HPP1-9000", deviceId)
}
@Test
@@ -58,7 +71,7 @@ class HemoCubeFragmentTest {
)
// Assert
assertEquals("HPP1-0001", deviceId)
TestCase.assertEquals("HPP1-0001", deviceId)
}
@Test
@@ -71,8 +84,8 @@ class HemoCubeFragmentTest {
val result = hemoCubeFragment.allReadingsComplete(repeatReadingCount, readingsPerSample)
// Assert
assertEquals(true, result)
assertEquals(hemoCubeFragment.allReadingsComplete(0, 1), false)
TestCase.assertEquals(true, result)
TestCase.assertEquals(hemoCubeFragment.allReadingsComplete(0, 1), false)
}
@Test
@@ -303,13 +316,6 @@ class HemoCubeFragmentTest {
assertEquals("HPP-000-5001", result)
}
@Test
fun testDeviceRatioClassificationNormalWithStartRange() {
val ratio = 0.16
val result = hemoCubeFragment.deviceRatioClassification(ratio)
assertEquals("Normal", result)
}
@Test
fun testDeviceRatioClassificationNormal() {
val ratio = 0.22
@@ -325,33 +331,19 @@ class HemoCubeFragmentTest {
}
@Test
fun testDeviceRatioClassificationSickleCellTraitLowerBound() {
val ratio = 0.251
val result = hemoCubeFragment.deviceRatioClassification(ratio)
assertEquals("Sickle Cell Trait", result)
}
@Test
fun testDeviceRatioClassificationSickleCellTraitUpperBound() {
val ratio = 0.309
fun testDeviceRatioClassificationSickleCellTrait() {
val ratio = 0.25
val result = hemoCubeFragment.deviceRatioClassification(ratio)
assertEquals("Sickle Cell Trait", result)
}
@Test
fun testDeviceRatioClassificationPositiveForSickleCell() {
val ratio = 0.359
val ratio = 0.37
val result = hemoCubeFragment.deviceRatioClassification(ratio)
assertEquals("Positive for Sickle Cell. HPLC for Confirmation", result)
}
@Test
fun testDeviceRatioClassificationSickleCellDiseaseLowerBound() {
val ratio = 0.361
val result = hemoCubeFragment.deviceRatioClassification(ratio)
assertEquals("Sickle Cell Disease", result)
}
@Test
fun testDeviceRatioClassificationSickleCellDisease() {
val ratio = 0.45
@@ -367,166 +359,44 @@ class HemoCubeFragmentTest {
}
@Test
fun findResultWithAdditionalMethods_ValidInput_ReturnsBorderlineNormal() {
val result =
hemoCubeFragment.findResultWithAdditionalMethods(0.5, "Negative Borderline", 2.5)
assertEquals("Borderline. Normal", result)
}
@Test
fun findResultWithAdditionalMethods_ValidInput_ReturnsBorderlineSickleCellTrait1() {
val result =
hemoCubeFragment.findResultWithAdditionalMethods(0.5, "Negative Borderline", 2.2)
assertEquals("Borderline. Sickle Cell Trait", result)
}
@Test
fun findResultWithAdditionalMethods_ValidInput_ReturnsBorderlineSickleCellTrait2() {
val result = hemoCubeFragment.findResultWithAdditionalMethods(
0.5,
"Positive for Sickle Cell. HPLC for Confirmation",
1.35
)
assertEquals("Borderline. Sickle Cell Trait", result)
}
@Test
fun findResultWithAdditionalMethods_ValidInput_ReturnsBorderlineSickleCellDisease() {
val result = hemoCubeFragment.findResultWithAdditionalMethods(
0.5,
"Positive for Sickle Cell. HPLC for Confirmation",
1.33
)
assertEquals("Borderline. Sickle Cell Disease", result)
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() {
val result = hemoCubeFragment.findResultWithAdditionalMethods(
0.5,
"Negative Borderline, Repeat Test",
70.0
)
// `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() {
val result =
hemoCubeFragment.findResultWithAdditionalMethods(0.5, "Sickle Cell Trait", 70.0)
// `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() {
val result = hemoCubeFragment.findResultWithAdditionalMethods(
0.5,
"Positive for Sickle Cell. HPLC for Confirmation",
1.35
)
assertEquals("Borderline. Sickle Cell Trait", result)
// `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() {
val result =
hemoCubeFragment.findResultWithAdditionalMethods(0.5, "Sickle Cell Disease", 70.0)
// `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)
}
@Test
fun testReclassifyWithBorderlineMethod2_NegativeBorderlineToNormal() {
// Arrange
val deviceRatio = 0.1
val deviceRatioClass = "Negative Borderline"
val led2Average = 0.2
// Act
val result = hemoCubeFragment.reclassifyWithBorderlineMethod2(
deviceRatio,
deviceRatioClass,
led2Average
)
// Assert
assertEquals("Borderline. Normal", result)
}
@Test
fun testReclassifyWithBorderlineMethod2_NegativeBorderlineToSCT() {
// Arrange
val deviceRatio = 0.1
val deviceRatioClass = "Negative Borderline"
val led2Average = 0.14
// Act
val result = hemoCubeFragment.reclassifyWithBorderlineMethod2(
deviceRatio,
deviceRatioClass,
led2Average
)
// Assert
assertEquals("Borderline. Sickle Cell Trait", result)
}
@Test
fun testReclassifyWithBorderlineMethod2_PositiveSickleCell() {
// Arrange
val deviceRatio = 0.2
val deviceRatioClass = "Positive for Sickle Cell. HPLC for Confirmation"
val led2Average = 0.18
// Act
val result = hemoCubeFragment.reclassifyWithBorderlineMethod2(
deviceRatio,
deviceRatioClass,
led2Average
)
// Assert
assertEquals("Borderline. Sickle Cell Disease", result)
}
@Test
fun testReclassifyWithBorderlineMethod2_PositiveSickleCellToSCT() {
// Arrange
val deviceRatio = 0.2
val deviceRatioClass = "Positive for Sickle Cell. HPLC for Confirmation"
val led2Average = 0.195
// Act
val result = hemoCubeFragment.reclassifyWithBorderlineMethod2(
deviceRatio,
deviceRatioClass,
led2Average
)
// Assert
assertEquals("Borderline. Sickle Cell Trait", result)
}
@Test
fun testReclassifyWithBorderlineMethod2_PositiveSickleCellToSCD() {
// Arrange
val deviceRatio = 0.2
val deviceRatioClass = "Positive for Sickle Cell. HPLC for Confirmation"
val led2Average = 0.189
// Act
val result = hemoCubeFragment.reclassifyWithBorderlineMethod2(
deviceRatio,
deviceRatioClass,
led2Average
)
// Assert
assertEquals("Borderline. Sickle Cell Disease", result)
}
}

View File

@@ -3,7 +3,7 @@ buildscript {
kotlin_version = '1.8.21'
}
dependencies {
classpath 'com.android.tools.build:gradle:8.3.0'
classpath 'com.android.tools.build:gradle:8.1.1'
classpath 'com.google.gms:google-services:4.4.0'
classpath 'com.google.firebase:firebase-appdistribution-gradle:4.0.1'
}

View File

@@ -1,6 +1,6 @@
#Tue Feb 27 16:09:58 IST 2024
#Mon Jun 12 17:07:47 IST 2023
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

Binary file not shown.