Compare commits

..

29 Commits

Author SHA1 Message Date
chandrashekhar reddy
12e022a00a app version 132 - Added changes according to single test and also added upload csv to firebase storage 2025-02-26 16:34:22 +05:30
chandrashekhar reddy
88cdc01978 app version 132 - Added changes according to single test and also added upload csv to firebase storage 2025-02-24 19:02:08 +05:30
sathwikcs
736dff2b11 Fixed the issue where dialogue is showing wrong 2025-02-20 18:23:10 +05:30
sathwikcs
20dd1e41eb Fixed the issue where app wil crash when switched to pre pod 2025-02-19 18:41:11 +05:30
sathwikcs
ad6670a1e8 Merge remote-tracking branch 'origin/dev-server-2.1.132' into dev-server-2.1.132 2025-02-19 15:42:43 +05:30
sathwikcs
e6f8ab69e9 Fixed the issue where app wil crash when switched to pre pod 2025-02-19 15:42:23 +05:30
Sathwik C S
ccd2138f66 Update file README.md 2025-02-18 09:31:30 +00:00
sathwikcs
d54436996d added dev icon 2025-02-18 14:53:36 +05:30
sathwikcs
52a555a63a Changes in josn - default : prod 2025-02-18 14:53:14 +05:30
sathwikcs
8e80cfe525 SettingsFragment.kt 2025-02-18 14:52:02 +05:30
sathwikcs
f26f54e51d Added the developer shared pref 2025-02-18 14:51:31 +05:30
sathwikcs
1d029b8ff8 removed the selector drop down 2025-02-18 14:50:40 +05:30
sathwikcs
c88efd148b App version Tap manager 2025-02-18 14:50:08 +05:30
sathwikcs
cb27983d21 NetworkStatusLiveData.kt has been removed 2025-02-17 12:38:11 +05:30
Sathwik C S
0b268dda47 Edit README.md 2025-01-29 11:02:47 +00:00
Sathwik C S
d9777edf02 Edit README.md 2025-01-29 11:01:52 +00:00
sathwikcs
238424ad89 NetworkStatusLiveData.kt has been removed 2025-01-29 15:39:47 +05:30
sathwikcs
c1f02c878c updated the NetworkChecker.kt 2025-01-29 15:39:29 +05:30
sathwikcs
b344dbcb69 updated the test case 2025-01-28 17:12:06 +05:30
sathwikcs
af7b426416 Merge remote-tracking branch 'origin/dev-server-2.1.131-network' into dev-server-2.1.131-network 2025-01-28 16:12:21 +05:30
sathwikcs
07abfcce6c updated build.gradle to make the api 31 as the min sdk from 21 -> 30 as device targets api 31 2025-01-28 16:12:00 +05:30
sathwikcs
2c62cc11ba updated build.gradle to make the api 31 as the min sdk from 21 -> 30 as device targets api 31 2025-01-28 16:11:42 +05:30
sathwikcs
312e2da79a TrueHemeViewmodel to fit new network Checker 2025-01-28 16:10:40 +05:30
sathwikcs
9768a0a835 updated TrueHeme TestViewmodel.kt NetworkChecker.kt 2025-01-28 16:08:29 +05:30
sathwikcs
db7aba4549 updated NetworkChecker.kt 2025-01-28 16:07:27 +05:30
sathwikcs
302239d191 Network Checker updated in TestRightViewModel 2025-01-28 12:50:14 +05:30
sathwikcs
cc1ae1ff82 Network Checker updated in NetworkStatusLiveData.kt 2025-01-28 12:49:06 +05:30
sathwikcs
d420a42fcc Network Checker 2025-01-28 12:47:39 +05:30
sathwikcs
21b3960ca9 Merge remote-tracking branch 'origin/dev-server-2.1.130-review-retryUsb' into dev-server-2.1.130-review-retryUsb 2025-01-22 15:00:55 +05:30
21 changed files with 698 additions and 328 deletions

View File

@@ -1,3 +1,5 @@
# Latest hpos app version
## current version : 2.1.132
### Release key

View File

@@ -18,10 +18,10 @@ android {
// server -> for server switching for internal test
defaultConfig {
applicationId "in.sminnovations.hpostesting.server"
minSdk 21
minSdk 30
targetSdk 34
versionCode 133
versionName "2.1.131"
versionCode 134
versionName "2.1.132"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
@@ -30,6 +30,7 @@ android {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
signingConfig signingConfigs.debug
}
}
compileOptions {

View File

@@ -1,33 +1,33 @@
{
"project_info": {
"project_number": "650071678820",
"project_id": "hpos-af3cc",
"storage_bucket": "hpos-af3cc.appspot.com"
"project_number": "1012719870714",
"project_id": "hpos-prod",
"storage_bucket": "hpos-prod.appspot.com"
},
"client": [
{
"client_info": {
"mobilesdk_app_id": "1:650071678820:android:f6fd45e2f6a63aef6c6471",
"mobilesdk_app_id": "1:1012719870714:android:d8d7c962d96f4097de5f43",
"android_client_info": {
"package_name": "in.sminnovations.hpostesting.server"
}
},
"oauth_client": [
{
"client_id": "650071678820-to41afi9f0gi5r3k6v7pe1e5p96nupcs.apps.googleusercontent.com",
"client_id": "1012719870714-uli6cdjea3lfnnueh6e4r15i10ei1vlj.apps.googleusercontent.com",
"client_type": 3
}
],
"api_key": [
{
"current_key": "AIzaSyAVdNGCev_AFX0qmuZJF6kxzRzXUTeAW5I"
"current_key": "AIzaSyA_O0oZGzy3Kiw9fiLQ3OFPME-xQJP88vs"
}
],
"services": {
"appinvite_service": {
"other_platform_oauth_client": [
{
"client_id": "650071678820-to41afi9f0gi5r3k6v7pe1e5p96nupcs.apps.googleusercontent.com",
"client_id": "1012719870714-uli6cdjea3lfnnueh6e4r15i10ei1vlj.apps.googleusercontent.com",
"client_type": 3
}
]

View File

@@ -4,15 +4,15 @@
"type": "APK",
"kind": "Directory"
},
"applicationId": "in.sminnovations.hpostesting.server",
"applicationId": "in.sminnovations.hpostesting.dev",
"variantName": "release",
"elements": [
{
"type": "SINGLE",
"filters": [],
"attributes": [],
"versionCode": 133,
"versionName": "2.1.131",
"versionCode": 127,
"versionName": "2.1.127",
"outputFile": "app-release.apk"
}
],

View File

@@ -47,6 +47,7 @@ object Constants {
const val MAXIMUM_TEST_ALLOWED_BEFORE_REFERENCE = 34
const val MAXIMUM_TEST_ALLOWED_BEFORE_REFERENCE10MM = 9
const val MAXIMUM_TEST_ALLOWED_SINGLE_TEST = 0
const val RANGE_IN_RESULT_CALCULATIONS = 10
@@ -1586,6 +1587,7 @@ object Constants {
//Trueheme for 10mm
//Before changing below values review before
//classification borderline metric
const val TEST_FOR_10MM = "SMI/SC-2-D10/"
const val positiveBoderLineMetricCheck10mmMin = 1.3
const val positiveBoderLineMetricCheck10mmMax = 1.66
const val negativeBoderLineMetricCheck10mmMin = 2.0
@@ -1603,6 +1605,7 @@ object Constants {
const val sickleCellDiseaseMax10mm = 0.7
//Trueheme for 2mm
//classification borderline metric
const val TEST_FOR_2MM = "SMI/SC/"
const val positiveBoderLineMetricCheck2mmMin = 0.8
const val positiveBoderLineMetricCheck2mmMax = 1.1
const val negativeBoderLineMetricCheck2mmMin = 1.5
@@ -1619,6 +1622,25 @@ object Constants {
const val sickleCellDiseaseMin2mm = 0.45
const val sickleCellDiseaseMax2mm = 0.7
//Trueheme for 10mm Single Test
//classification borderline metric
const val singleTest = "SMI/SC-ST/"
const val positiveBoderLineMetricCheck2mmStMin = 1.3
const val positiveBoderLineMetricCheck2mmStMax = 1.66
const val negativeBoderLineMetricCheck2mmStMin = 2.0
const val negativeBoderLineMetricCheck2mmStMax = 2.4
//classification device ratio
const val normalMinSt2mm = 0.07
const val normalMaxSt2mm = 0.23
const val negativeBorderlineMinSt2mm = 0.23
const val negativeBorderlineMaxSt2mm = 0.27
const val sickleCellTraitMinSt2mm = 0.27
const val sickleCellTraitMaxSt2mm = 0.31
const val positiveForSickleCellMinSt2mm = 0.31
const val positiveForSickleCellMaxSt2mm = 0.39
const val sickleCellDiseaseMinSt2mm = 0.39
const val sickleCellDiseaseMaxSt2mm = 0.7
const val min2mmLed1 = 0.34
const val max2mmLed1 = 1.48
const val min2mmLed2 = 0.04

View File

@@ -38,15 +38,16 @@ class LocalFileDataSourceImpl @Inject constructor() : LocalFileDataSource {
}
override fun exportDataToCSV(
fileName: String, dataList: List<HemoCubeTestData>,
filePath: String, dataList: List<HemoCubeTestData>,
): Boolean {
try {
val formattedFileName = fileName.replace(
Regex("[^a-zA-Z0-9.-]"),
"_"
) // Replace special characters with underscores
val filePath = File(getExternalStorageDirectory(), formattedFileName)
val writer = FileWriter(filePath)
// Create the file directly from the provided path
val file = File(filePath)
// Ensure parent directory exists
file.parentFile?.mkdirs()
val writer = FileWriter(file)
val csvWriter = CSVWriter(writer)
// Write CSV header
val header = arrayOf(
@@ -103,8 +104,8 @@ class LocalFileDataSourceImpl @Inject constructor() : LocalFileDataSource {
csvWriter.writeNext(header)
// Filter and write data rows where testStatus is true
val filteredDataList = dataList.filter { it.testStatus == true }
for (data in filteredDataList) {
// val filteredDataList = dataList.filter { it.testStatus == true }
for (data in dataList) {
val row = arrayOf(
data._id,
data.name,
@@ -226,8 +227,8 @@ class LocalFileDataSourceImpl @Inject constructor() : LocalFileDataSource {
csvWriter.writeNext(header)
// Filter and write data rows where testStatus is true
val filteredDataList = dataList.filter { it.testStatus == true }
for (data in filteredDataList) {
//val filteredDataList = dataList.filter { it.testStatus == true }
for (data in dataList) {
val row = arrayOf(
data._id,
data.name,

View File

@@ -38,6 +38,7 @@ import com.example.hpostesting.domain.SaveRawDataTest
import com.example.hpostesting.firebase.FirebaseManager
import com.example.hpostesting.presentation.utils.UsbServiceListener
import com.example.hpostesting.presentation.utils.UsbServiceListenerImpl
import com.example.hpostesting.util.NetworkMonitor
import com.example.hpostesting.util.PropertyProvider
import com.example.hpostesting.util.PropertyProviderImpl
import com.google.firebase.firestore.FirebaseFirestore
@@ -240,8 +241,11 @@ object AppModule {
return UsbServiceListenerImpl(context)
}
@Provides
@Singleton
fun provideNetworkMonitor(@ApplicationContext context: Context): NetworkMonitor {
return NetworkMonitor(context)
}
}

View File

@@ -14,6 +14,7 @@ package com.example.hpostesting.firebase
import android.content.Context
import android.content.Intent
import android.os.Process
import androidx.core.content.edit
import com.google.firebase.FirebaseApp
import com.google.firebase.FirebaseOptions
import com.google.firebase.firestore.FirebaseFirestore
@@ -27,8 +28,8 @@ import javax.inject.Inject
class FirebaseManager @Inject constructor(private val context: Context) {
private val sharedPreferences =
context.getSharedPreferences("FirebaseConfigPrefs", Context.MODE_PRIVATE)
private val sharedPreferencesServerConfig = context.getSharedPreferences("FirebaseConfigPrefs", Context.MODE_PRIVATE)
private val sharedPreferencesDev = context.getSharedPreferences("DevMode",Context.MODE_PRIVATE)
private var currentFirestore: FirebaseFirestore = FirebaseFirestore.getInstance()
private var currentStorage: FirebaseStorage = FirebaseStorage.getInstance()
@@ -36,7 +37,7 @@ class FirebaseManager @Inject constructor(private val context: Context) {
private val defaultServers = listOf(
// i have it in different place look down
firebaseConfig1, firebaseConfig2, firebaseConfig3,
firebaseConfigProd, firebaseConfigDev , firebaseConfigQaQc, firebaseConfigPreProd
)
init {
@@ -44,6 +45,18 @@ class FirebaseManager @Inject constructor(private val context: Context) {
switchServer(lastSelectedServer)
}
fun setDevModeOn(state : Boolean){
sharedPreferencesDev.edit {
putBoolean( "isDevModeOn",state )
}
}
fun getDevMode(): Boolean {
return sharedPreferencesDev.getBoolean("isDevModeOn",false)
}
fun getCurrentFirestore(): FirebaseFirestore = currentFirestore
fun getCurrentStorage(): FirebaseStorage = currentStorage
@@ -73,7 +86,7 @@ class FirebaseManager @Inject constructor(private val context: Context) {
}
private fun saveSelectedServer(firebaseConfig: FirebaseConfig) {
with(sharedPreferences.edit()) {
with(sharedPreferencesServerConfig.edit()) {
putString("selectedServerName", firebaseConfig.serverName)
putString("projectId", firebaseConfig.projectId)
putString("appId", firebaseConfig.appId)
@@ -93,12 +106,12 @@ class FirebaseManager @Inject constructor(private val context: Context) {
fun getLastSelectedServer(): FirebaseConfig {
val selectedServerName =
sharedPreferences.getString("selectedServerName", defaultServers.first().serverName)
val projectId = sharedPreferences.getString("projectId", defaultServers.first().projectId)
val appId = sharedPreferences.getString("appId", defaultServers.first().appId)
val apiKey = sharedPreferences.getString("apiKey", defaultServers.first().apiKey)
sharedPreferencesServerConfig.getString("selectedServerName", defaultServers.first().serverName)
val projectId = sharedPreferencesServerConfig.getString("projectId", defaultServers.first().projectId)
val appId = sharedPreferencesServerConfig.getString("appId", defaultServers.first().appId)
val apiKey = sharedPreferencesServerConfig.getString("apiKey", defaultServers.first().apiKey)
val storageBucket =
sharedPreferences.getString("storageBucket", defaultServers.first().storageBucket)
sharedPreferencesServerConfig.getString("storageBucket", defaultServers.first().storageBucket)
return FirebaseConfig(
serverName = selectedServerName ?: defaultServers.first().serverName,
@@ -128,7 +141,7 @@ fun delayedRestart(context: Context) {
}
}
private val firebaseConfig1 = FirebaseConfig(
private val firebaseConfigDev = FirebaseConfig(
serverName = "dev",
apiKey = "AIzaSyAVdNGCev_AFX0qmuZJF6kxzRzXUTeAW5I",
appId = "1:650071678820:android:a53292637abb7c0d6c6471",
@@ -136,7 +149,7 @@ private val firebaseConfig1 = FirebaseConfig(
storageBucket = "hpos-af3cc.appspot.com"
)
private val firebaseConfig2 = FirebaseConfig(
private val firebaseConfigQaQc = FirebaseConfig(
serverName = "qc-qa",
apiKey = "AIzaSyC4d4GhWHr_NMJAeBvnztQ_yQ3Qe9MAnLs",
appId = "1:1004619739289:android:3dbcefb10ea99654e5c808",
@@ -144,10 +157,18 @@ private val firebaseConfig2 = FirebaseConfig(
storageBucket = "hpos-qa.appspot.com"
)
private val firebaseConfig3 = FirebaseConfig(
serverName = " pre prod",
private val firebaseConfigProd = FirebaseConfig(
serverName = "prod",
apiKey = "AIzaSyA_O0oZGzy3Kiw9fiLQ3OFPME-xQJP88vs",
appId = "1:1012719870714:android:d8d7c962d96f4097de5f43",
projectId = "hpos-prod",
storageBucket = "hpos-prod.appspot.com"
)
private val firebaseConfigPreProd = FirebaseConfig(
serverName = "pre prod",
apiKey = "AIzaSyDYySi27LioZGNisP1NfnNU5inJX_0FT38",
appId = "1:121176529204:android:a7bc0842f61e5095bbed61",
appId = "1:121176529204:android:e6841fdac57bdc95bbed61",
projectId = "hpos-preprod",
storageBucket = "hpos-preprod.appspot.com"
)

View File

@@ -24,6 +24,9 @@ import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import com.example.hpostesting.data.constant.DataHolder
import com.example.hpostesting.data.constant.Constants
import com.example.hpostesting.data.constant.Constants.TEST_FOR_10MM
import com.example.hpostesting.data.constant.Constants.TEST_FOR_2MM
import com.example.hpostesting.data.constant.Constants.singleTest
import com.example.hpostesting.data.constant.LanguageManager
import com.example.hpostesting.data.model.test.TestType
import com.example.hpostesting.presentation.main_base.DashboardActivity
@@ -52,7 +55,7 @@ class KitScanActivity : AppCompatActivity(), IDcsSdkApiDelegate {
private var fromWhere = "Home"
private val TAG = "KitScanActivity"
private lateinit var binding: ActivityKitScanBinding
private var maxTest = 9
private lateinit var sharedPreference: SharedPreferences
var sdkHandler: SDKHandler? = null
@@ -71,20 +74,27 @@ class KitScanActivity : AppCompatActivity(), IDcsSdkApiDelegate {
}
private fun processScannedData(contents: String) {//edited auto selection of cuvette size
if(contents.contains("SMI/SC/")){
if(contents.contains(TEST_FOR_2MM)){
with(sharedPreference.edit()) {
putString(Constants.CUVETTE_SIZE, "2mm")
apply()
}
Toast.makeText(this, "Selected cuvette size: 2mm", Toast.LENGTH_SHORT).show()
binding.nameEditText.setText(contents)
}else if(contents.contains("SMI/SC-2-D10/")){
}else if(contents.contains(TEST_FOR_10MM)){
with(sharedPreference.edit()) {
putString(Constants.CUVETTE_SIZE, "10mm")
apply()
}
Toast.makeText(this, "Selected cuvette size: 10mm", Toast.LENGTH_SHORT).show()
binding.nameEditText.setText(contents)
}else if(contents.contains(singleTest)){
with(sharedPreference.edit()) {
putString(Constants.CUVETTE_SIZE, "10mmSt")
apply()
}
Toast.makeText(this, "Selected cuvette size: 2mm for single test", Toast.LENGTH_SHORT).show()
binding.nameEditText.setText(contents)
}else{
Toast.makeText(this, R.string.invalid_kit, Toast.LENGTH_LONG).show()
}
@@ -133,14 +143,19 @@ class KitScanActivity : AppCompatActivity(), IDcsSdkApiDelegate {
setContentView(binding.root)
binding.toolbar.title = "Kit Serial Number"
fromWhere = intent.getStringExtra("fromWhere").toString()
val maxTest = if(sharedPreference.getString(Constants.CUVETTE_SIZE, "10mm").toString() == "2mm"){
Constants.MAXIMUM_TEST_ALLOWED_BEFORE_REFERENCE
}else{
Constants.MAXIMUM_TEST_ALLOWED_BEFORE_REFERENCE10MM
if(sharedPreference.getString(Constants.CUVETTE_SIZE, "10mm").toString() == "2mm"){
maxTest = Constants.MAXIMUM_TEST_ALLOWED_BEFORE_REFERENCE
}else if(sharedPreference.getString(Constants.CUVETTE_SIZE, "10mm").toString() == "10mm"){
maxTest = Constants.MAXIMUM_TEST_ALLOWED_BEFORE_REFERENCE10MM
} else if(sharedPreference.getString(Constants.CUVETTE_SIZE, "10mmSt").toString() == "10mmSt"){
maxTest = Constants.MAXIMUM_TEST_ALLOWED_SINGLE_TEST
}
val time = timeDifference(sharedPreference.getString(Constants.KIT_TIME, "").toString())
val kitNum = sharedPreference.getString(Constants.KIT_NUMBER, "").toString()
// Toast.makeText(this@KitScanActivity,"Test MAx time"+ time+"-Test count-"+kitNum,Toast.LENGTH_SHORT).show()
// if (sharedPreference.getString(Constants.USER_ID, "").toString() == "ADMIN") {
// moveToNext()
// }else{
if (sharedPreference.getString(Constants.USER_ID, "").toString() == "ADMIN") {
moveToNext()
}else{
@@ -209,19 +224,27 @@ class KitScanActivity : AppCompatActivity(), IDcsSdkApiDelegate {
binding.btnGo.setOnClickListener {
val serialNumber = binding.nameEditText.text.toString().trim()
if (serialNumber.isNotEmpty() && isSerialValid(serialNumber)) {
if(serialNumber.contains("SMI/SC/")){
if(serialNumber.contains(TEST_FOR_2MM)){
with(sharedPreference.edit()) {
putString(Constants.CUVETTE_SIZE, "2mm")
apply()
}
Toast.makeText(this, "Selected cuvette size: 2mm", Toast.LENGTH_SHORT).show()
}else if(serialNumber.contains("SMI/SC-2-D10/")){
}else if(serialNumber.contains(TEST_FOR_10MM)){
with(sharedPreference.edit()) {
putString(Constants.CUVETTE_SIZE, "10mm")
apply()
}
Toast.makeText(this, "Selected cuvette size: 10mm", Toast.LENGTH_SHORT).show()
}else if(serialNumber.contains(singleTest)){
with(sharedPreference.edit()) {
putString(Constants.CUVETTE_SIZE, "10mmSt")
apply()
}
Toast.makeText(this, "Selected cuvette size: 2mm for single test", Toast.LENGTH_SHORT).show()
binding.nameEditText.setText(serialNumber)
}
val kitTime = SimpleDateFormat(
"yyyy-MM-dd HH:mm:ss", Locale.getDefault()
).format(Calendar.getInstance().time).toString()
@@ -348,16 +371,21 @@ class KitScanActivity : AppCompatActivity(), IDcsSdkApiDelegate {
// }
private fun isSerialValid(s: String): Boolean {
if (s.contains("SMI/SC/")) {
if (s.contains(TEST_FOR_2MM)) {
if(s.length != 17){
binding.nameEditText.error = "Invalid Kit Serial Number, correct example SMI/SC/000/00/000"
return false
}
}else if(s.contains("SMI/SC-2-D10/")){
}else if(s.contains(TEST_FOR_10MM)){
if(s.length != 27){
binding.nameEditText.error = "Invalid Kit Serial Number, correct example SMI/SC-2-D10/000000/000/000"
return false
}
}else if(s.contains(singleTest)){
if(s.length != 27){
binding.nameEditText.error = "Invalid Kit Serial Number, correct example SMI/SC-ST/000000/000/000"
return false
}
}else{
return false
}

View File

@@ -2,7 +2,7 @@ package com.example.hpostesting.presentation.main_base
class AppVersionTapManager {
private var tapCount = 0
private var tapCount = 0
private val maxTapCount = 5
fun registerTap(onMaxTapsReached: () -> Unit) {

View File

@@ -13,37 +13,59 @@
package com.example.hpostesting.presentation.main_base
import android.app.AlertDialog
import android.content.Context
import android.content.DialogInterface
import android.content.SharedPreferences
import android.content.pm.PackageManager
import android.net.Uri
import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.AdapterView
import android.widget.ArrayAdapter
import android.widget.Toast
import androidx.appcompat.app.AlertDialog
import androidx.core.content.ContextCompat
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.lifecycle.lifecycleScope
import androidx.preference.ListPreference
import androidx.preference.Preference
import androidx.preference.PreferenceFragmentCompat
import androidx.preference.PreferenceManager
import androidx.preference.SwitchPreferenceCompat
import com.example.hpostesting.data.constant.Constants
import com.example.hpostesting.data.constant.DataHolder
import com.example.hpostesting.data.constant.LanguageManager
import com.example.hpostesting.data.model.patient.HemoCubeTestData
import com.example.hpostesting.data.repository.DatabaseRepository
import com.example.hpostesting.firebase.FirebaseConfig
import com.example.hpostesting.firebase.FirebaseManager
import com.example.hpostesting.presentation.trueheme_test.TrueHemeTestViewModel
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.firebase.ktx.Firebase
import com.google.firebase.storage.ktx.storage
import com.google.firebase.storage.ktx.storageMetadata
import `in`.sminnovations.hpostesting.R
import `in`.sminnovations.hpostesting.databinding.FragmentSlideshowBinding
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.io.File
import java.text.SimpleDateFormat
import java.util.Date
import java.util.Locale
private var isLanguageChanged = false
class SlideshowFragment : Fragment() {
private var selectedItem = "10mm"
private val values = arrayOf("10mm", "2mm")
private val values = arrayOf("10mm", "2mm", "10mmSt")
private lateinit var binding: FragmentSlideshowBinding
private val trueHemeTestViewModel: TrueHemeTestViewModel by activityViewModels()
private lateinit var sharedPreferences: SharedPreferences
override fun onCreateView(
@@ -56,9 +78,10 @@ class SlideshowFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.btnDownload.setOnClickListener {
sendData()
}
binding.nameEditText.setText(sharedPreferences.getString(Constants.LABNAME, ""))
selectedItem = sharedPreferences.getString(Constants.CUVETTE_SIZE, "10mm").toString()
binding.btnGo.setOnClickListener {
var labname = binding.nameEditText.text.toString()
@@ -79,8 +102,10 @@ class SlideshowFragment : Fragment() {
val pos: Int
if (selectedItem == "10mm") {
pos = 0
} else {
} else if (selectedItem == "2mm"){
pos = 1
} else {
pos = 2
}
binding.spinnerCuvette.adapter = adapter
@@ -97,6 +122,7 @@ class SlideshowFragment : Fragment() {
// Do nothing here
}
}
binding.spinnerCuvette.setSelection(pos)
binding.btnAddSize.setOnClickListener {
with(sharedPreferences.edit()) {
@@ -111,26 +137,132 @@ class SlideshowFragment : Fragment() {
childFragmentManager.beginTransaction().replace(binding.container.id, PrefsFragment())
.commit()
}
private fun sendData() {
//This data will saved in firebase storage check in HPOS-Prod
val dialog = AlertDialog.Builder(requireContext())
.setTitle("Downloading")
.setMessage("Please wait...")
.setCancelable(false)
.create()
dialog.show()
downloadLocalDBData(dialog)
}
private fun downloadLocalDBData(dialog: DialogInterface) {
var csvDownloaded = false
trueHemeTestViewModel.allUserData.observe(viewLifecycleOwner) { userDataList ->
if (!csvDownloaded) {
val downloadList = mutableListOf<HemoCubeTestData>()
userDataList.forEach { userData ->
Log.d("DownloadDebug", "userData: $userData, isCSVCreated: ${userData.isCSVCreated}")
downloadList.add(userData)
}
if (downloadList.isNotEmpty()) {
// Set flag to prevent multiple executions due to observer
csvDownloaded = true
// Launch coroutine to handle CSV creation and upload
lifecycleScope.launch {
try {
// Call suspend function to create CSV and wait for result
val csvFile = trueHemeTestViewModel.createCSVNew(downloadList, requireContext())
if (csvFile != null && csvFile.exists() && csvFile.length() > 0) {
uploadToFirebaseStorage(csvFile)
Toast.makeText(
requireContext(),
"CSV file created successfully at ${csvFile.absolutePath}",
Toast.LENGTH_LONG
).show()
} else {
csvDownloaded = false // Reset flag if failed
Toast.makeText(
requireContext(),
"CSV file creation failed, please try again",
Toast.LENGTH_SHORT
).show()
}
} catch (e: Exception) {
csvDownloaded = false // Reset flag if exception
e.printStackTrace()
Toast.makeText(
requireContext(),
"Error creating CSV file: ${e.message}",
Toast.LENGTH_SHORT
).show()
} finally {
dialog.dismiss()
}
}
} else {
Toast.makeText(requireContext(), "No data to download", Toast.LENGTH_SHORT).show()
dialog.dismiss()
}
}
}
}
private fun uploadToFirebaseStorage(file: File) {
// Get Firebase Storage reference
val storage = Firebase.storage
val timestamp = SimpleDateFormat("yyyyMMdd_HHmmss", Locale.getDefault()).format(Date())
val storageRef = storage.reference.child("!!!csv_files/hemocube_data_$timestamp.csv")
// Create file metadata
val metadata = storageMetadata {
contentType = "text/csv"
}
// Upload file
val uploadTask = storageRef.putFile(Uri.fromFile(file), metadata)
uploadTask
.addOnSuccessListener {
Toast.makeText(
requireContext(),
"CSV file uploaded to Firebase successfully",
Toast.LENGTH_SHORT
).show()
}
.addOnFailureListener { exception ->
Log.e("FirebaseUpload", "Upload failed", exception)
Toast.makeText(
requireContext(),
"Failed to upload CSV to Firebase",
Toast.LENGTH_SHORT
).show()
}
.addOnProgressListener { taskSnapshot ->
val progress = (100.0 * taskSnapshot.bytesTransferred / taskSnapshot.totalByteCount)
Log.d("FirebaseUpload", "Upload is $progress% done")
}
}
}
class PrefsFragment : PreferenceFragmentCompat(){
class PrefsFragment : PreferenceFragmentCompat() {
private lateinit var tapManager: AppVersionTapManager
private var isServerSelectionVisible = false
private lateinit var serverPreference: Preference
private lateinit var currentServerPreference: Preference
lateinit var firebaseManager: FirebaseManager
private var topDevMode = false
lateinit var databaseRepository: DatabaseRepository
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
firebaseManager = FirebaseManager(requireContext())
firebaseManager = FirebaseManager(requireContext())
databaseRepository = (activity as DashboardActivity).databaseRepository
tapManager = AppVersionTapManager()
topDevMode = firebaseManager.getDevMode()
val sharedPreference =
requireContext().getSharedPreferences("HEMOCUBE", Context.MODE_PRIVATE)
val preferenceScreen = preferenceManager.createPreferenceScreen(requireContext())
val currentServer = firebaseManager.getLastSelectedServer().serverName
// Language Preference
val languagePreference = ListPreference(requireContext()).apply {
key = "language_preference"
@@ -140,52 +272,102 @@ class PrefsFragment : PreferenceFragmentCompat(){
entryValues = arrayOf("en", "kn", "hi")
setDefaultValue("en")
icon = ContextCompat.getDrawable(requireContext(), R.drawable.baseline_translate_24)
onPreferenceChangeListener = Preference.OnPreferenceChangeListener { _, newValue ->
val languageCode = newValue as String
updateLanguage(requireContext(), languageCode)
true
}
}
val servers = firebaseManager.getAvailableServers()
val serverNames = servers.map { it.serverName }.toTypedArray()
val serverSelectorPreference = CustomDialogPreference(requireContext()).apply {
key = "server_preference"
title = "Select the Server"
summary = "Current server: $currentServer"
setEntries(serverNames)
setEntryValues(serverNames)
setValue(currentServer)
icon = ContextCompat.getDrawable(requireContext(), R.drawable.baseline_cloud_sync_24)
setOnPreferenceChangeListener { newValue ->
val preferenceConfig = servers.find { it.serverName == newValue }
switchFirebaseServer(preferenceConfig!!)
true
}
}
// Add preferences to screen
preferenceScreen.addPreference(languagePreference)
preferenceScreen.addPreference(languagePreference)
val developerToggle = SwitchPreferenceCompat(requireContext()).apply {
title = "Developer Options"
summary = "Enable or disable developer options"
icon =
ContextCompat.getDrawable(requireContext(), R.drawable.baseline_developer_mode_24)
key = "developer_options" // Unique key for SharedPreferences
// Load toggle state from SharedPreferences
isChecked = firebaseManager.getDevMode()
setOnPreferenceChangeListener { _, newValue ->
val isEnabled = newValue as Boolean
firebaseManager.setDevModeOn(isEnabled)
if (!isEnabled) {
preferenceScreen.removePreference(this)
preferenceScreen.removePreference(serverSelectorPreference)
}
if (!firebaseManager.getDevMode() && currentServer != "prod"){
val prodConfig = servers.find { it.serverName == "prod" }
switchFirebaseServer(prodConfig!!)
}
true
}
}
val appVersionPreference = Preference(requireContext()).apply {
title = "App Version"
summary =
getAppVersion(requireContext()) + " [ " + getAppEnvironment(requireContext()) +"->"+currentServer+"]"
getAppVersion(requireContext()) + " [ " + getAppEnvironment(requireContext()) + "->" + currentServer + "]"
icon = ContextCompat.getDrawable(requireContext(), R.drawable.baseline_info_24)
setOnPreferenceClickListener {
if ((sharedPreference.getString(Constants.USER_ID, "").toString() == "ADMIN")) {
tapManager.registerTap {
if (!isServerSelectionVisible) {
showServerSelection()
}
}
} else {
tapManager.registerTap {
firebaseManager.setDevModeOn(true)
val devConfig = servers.find { it.serverName == "dev" }
if (firebaseManager.getDevMode() && currentServer != "dev") {
Toast.makeText(requireContext(), "Now you are a Developer", Toast.LENGTH_SHORT).show()
Toast.makeText(requireContext(), "Setting Up developer Environmnet", Toast.LENGTH_SHORT).show()
switchFirebaseServer(devConfig!!)
}
if (preferenceScreen.findPreference<SwitchPreferenceCompat>("developer_options") == null) {
preferenceScreen.addPreference(developerToggle)
}
if (preferenceScreen.findPreference<ListPreference>("server_preference") == null) {
preferenceScreen.addPreference(serverSelectorPreference)
}
developerToggle.isChecked = true
updateServerPreference(currentServer)
}
true
}
}
preferenceScreen.addPreference(appVersionPreference)
currentServerPreference = Preference(requireContext()).apply {
key = "current_server_preference"
title = "Current Server"
summary = "No server selected"
isVisible = false
icon = ContextCompat.getDrawable(requireContext(), R.drawable.baseline_cloud_done_24)
setOnPreferenceClickListener {
// (activity as? DashboardActivity)?.sendData("hello test data")
// Toast.makeText(context, "data sent", Toast.LENGTH_SHORT).show() this was used to send the basic data as test data
true
}
if (firebaseManager.getDevMode()) {
developerToggle.isChecked = true // Ensure it's checked
preferenceScreen.addPreference(developerToggle)
preferenceScreen.addPreference(serverSelectorPreference)
}
preferenceScreen.addPreference(currentServerPreference)
setPreferenceScreen(preferenceScreen)
if (isLanguageChanged) {
@@ -194,58 +376,23 @@ class PrefsFragment : PreferenceFragmentCompat(){
}
private fun showServerSelection() {
val currentServer = firebaseManager.getLastSelectedServer().serverName
serverPreference = Preference(requireContext()).apply {
title = "Select Server"
summary = "Choose your server"
icon = ContextCompat.getDrawable(requireContext(), R.drawable.baseline_cloud_sync_24)
setOnPreferenceClickListener {
showServerSelectionDialog()
true
}
private fun updateServerPreference(newServer: String) {
(findPreference<CustomDialogPreference>("server_preference"))?.let { pref ->
pref.setValue(newServer)
pref.summary = "Current server: $newServer"
}
preferenceScreen.addPreference(serverPreference)
currentServerPreference.isVisible = true
currentServerPreference.summary = "Current Server: $currentServer"
Toast.makeText(requireContext(), "Server selection now available", Toast.LENGTH_SHORT).show()
}
private fun showServerSelectionDialog() {
val servers = firebaseManager.getAvailableServers()
val serverNames = servers.map { it.serverName }.toTypedArray()
val builder = AlertDialog.Builder(requireContext())
builder.setTitle("Select Server")
.setItems(serverNames) { _, which ->
val selectedServer = servers[which]
switchFirebaseServer(selectedServer)
}
.setNegativeButton("Cancel", null)
.show()
}
private fun switchFirebaseServer(firebaseConfig: FirebaseConfig) {
firebaseManager.switchServer(firebaseConfig)
Toast.makeText(requireContext(), "Switched to ${firebaseConfig.serverName}, Please wait for few seconds", Toast.LENGTH_SHORT).show()
Toast.makeText(requireContext(), "Restart needed restarting the app", Toast.LENGTH_SHORT).show()
firebaseManager.restartApp()
currentServerPreference.summary = "Current Server: ${firebaseConfig.serverName}"
}
private fun switchCurrentServer(serverCode: String) {
Toast.makeText(requireContext(), "Switched to server: $serverCode", Toast.LENGTH_SHORT)
Toast.makeText(
requireContext(),
"Switched to ${firebaseConfig.serverName}, Please wait for few seconds",
Toast.LENGTH_SHORT
).show()
Toast.makeText(requireContext(), "Restart needed restarting the app", Toast.LENGTH_SHORT)
.show()
currentServerPreference.summary = "Current Server: $serverCode"
firebaseManager.restartApp()
}
private fun updateLanguage(context: Context, languageCode: String) {
@@ -275,3 +422,47 @@ class PrefsFragment : PreferenceFragmentCompat(){
}
// CustomDialogPreference.kt
class CustomDialogPreference(context: Context) : Preference(context) {
private var entries: Array<String> = arrayOf()
private var entryValues: Array<String> = arrayOf()
private var currentValue: String = ""
private var onValueChangeListener: ((String) -> Boolean)? = null
fun setEntries(entries: Array<String>) {
this.entries = entries
}
fun setEntryValues(values: Array<String>) {
this.entryValues = values
}
fun setValue(value: String) {
currentValue = value
persistString(value)
}
fun setOnPreferenceChangeListener(listener: (String) -> Boolean) {
onValueChangeListener = listener
}
override fun onClick() {
val builder = MaterialAlertDialogBuilder(context)
builder.setTitle(title)
builder.setSingleChoiceItems(entries, entries.indexOf(currentValue)) { dialog, which ->
val newValue = entryValues[which]
if (onValueChangeListener?.invoke(newValue) == true) {
setValue(newValue)
dialog.dismiss()
}
}
builder.setNegativeButton("Cancel") { dialog, _ ->
dialog.dismiss()
}
builder.show()
}
}

View File

@@ -19,7 +19,6 @@ import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.example.hpostesting.data.constant.DataHolder
import com.example.hpostesting.util.NetworkStatusLiveData
import com.example.hpostesting.data.constant.Constants
import com.example.hpostesting.data.dao.UserDao
import com.example.hpostesting.data.model.CalculationVariableForTest
@@ -36,6 +35,7 @@ import com.example.hpostesting.domain.SaveRawDataTest
import com.example.hpostesting.domain.SickleFindResultCaluculationWithMaxImpl
import com.example.hpostesting.domain.TestRightResultCalculation
import com.example.hpostesting.util.MyUtils
import com.example.hpostesting.util.NetworkMonitor
import com.google.firebase.storage.FirebaseStorage
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.launch
@@ -55,10 +55,10 @@ class TestRightViewModel @Inject constructor(
private val saveRawDataTest: SaveRawDataTest,
private val repository: DatabaseRepository,
private val userDao: UserDao,
networkMonitor: NetworkMonitor,
context: Context?
) : ViewModel() {
var isServiceConnected = false
val progressBar = MutableLiveData(false)
@@ -69,9 +69,8 @@ class TestRightViewModel @Inject constructor(
var testDetails = DataHolder.selectedTest
private val _networkStatusLiveData = context?.let { NetworkStatusLiveData(it) }
val networkStatusLiveData: NetworkStatusLiveData?
get() = _networkStatusLiveData
private var _networkStatusLiveData = networkMonitor.isConnected
val networkStatusLiveData = _networkStatusLiveData
val allUserData = userDao.getAll()
@@ -82,6 +81,11 @@ class TestRightViewModel @Inject constructor(
val calculationVariableList = ArrayList<CalculationVariableForTest>()
init {
networkMonitor.startMonitoring()
_networkStatusLiveData = networkMonitor.isConnected
}
fun mapDeviceConstants(string: String) {
if (string.isNotEmpty()) {
val listOfStrings = string.split(",")

View File

@@ -25,11 +25,8 @@ import androidx.lifecycle.viewModelScope
import androidx.work.ExistingPeriodicWorkPolicy
import androidx.work.PeriodicWorkRequestBuilder
import androidx.work.WorkManager
import com.example.hpostesting.util.CsvWriter
import com.example.hpostesting.data.constant.DataHolder
import com.example.hpostesting.util.NetworkStatusLiveData
import com.example.hpostesting.util.Result
import com.example.hpostesting.data.constant.Constants
import com.example.hpostesting.data.constant.DataHolder
import com.example.hpostesting.data.dao.HemoCubeBufferDao
import com.example.hpostesting.data.dao.HemoCubeDao
import com.example.hpostesting.data.datasource.LocalFileDataSource
@@ -46,10 +43,12 @@ 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 com.example.hpostesting.util.CsvWriter
import com.example.hpostesting.util.NetworkMonitor
import com.example.hpostesting.util.Result
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.launch
import okhttp3.MediaType.Companion.toMediaTypeOrNull
@@ -70,20 +69,22 @@ class TrueHemeViewModel @Inject constructor(
private val repository: Repository,
private val logFileManager: LogFileManager,
private val localFileDataSource: LocalFileDataSource,
networkMonitor: NetworkMonitor,
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 sharedPreference = context.getSharedPreferences("HEMOCUBE", Context.MODE_PRIVATE)
private val workManager = WorkManager.getInstance(context)
// init {
// startPeriodicCheckUpdate()
// }
// init {
// startPeriodicCheckUpdate()
// }
init {
networkMonitor.startMonitoring()
}
val loginResponse = MutableLiveData<Result<LoginResponse>>()
@@ -97,7 +98,7 @@ class TrueHemeViewModel @Inject constructor(
// 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)
private val _networkStatusLiveData = networkMonitor.isConnected
val allUserData = hemoCubeDao.getAll()
val allKitTestData = hemoCubeBufferDao.getAll()
val deviceData = MutableLiveData<DeviceData?>()
@@ -181,7 +182,7 @@ class TrueHemeViewModel @Inject constructor(
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) }
requestBody?.let { MultipartBody.Part.createFormData("logFile", file.name, it) }
multipartFile?.let { partFile ->
repository.uploadLogs(partFile).let { result ->
uploadLogs.postValue(result)
@@ -193,37 +194,35 @@ class TrueHemeViewModel @Inject constructor(
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 -> {}
) = 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)
}
} catch (e: Exception) {
Log.e("Testdb", "Exception during data upload: ${e.message}")
fireBaseUpload.postValue("Error")
is Response.Error -> {
Log.e("Testdb", "Error uploading data to Firestore: $response")
fireBaseUpload.postValue("Error")
bufferCheckData.localFlag = true
hemoCubeBufferDao.insertAll(bufferCheckData)
}
else -> {}
}
} else {
hemoCubeBufferDao.insertAll(bufferCheckData)
fireBaseUpload.postValue("Local")
} 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 {
@@ -396,8 +395,7 @@ class TrueHemeViewModel @Inject constructor(
viewModelScope.launch {
try {
when (val response =
repository.addTestToDatabaseforBufferCheck(bufferCheckData)) {
when (val response = repository.addTestToDatabaseforBufferCheck(bufferCheckData)) {
is Response.Success -> {
Log.i("Testdb", "Data uploaded to Firestore successfully")
fireBaseUpload.postValue("Success")
@@ -415,10 +413,9 @@ class TrueHemeViewModel @Inject constructor(
}
}
private fun updateBufferLocalFlag(bufferId: String) =
viewModelScope.launch {
hemoCubeBufferDao.updateFieldById(id = bufferId, true)
}
private fun updateBufferLocalFlag(bufferId: String) = viewModelScope.launch {
hemoCubeBufferDao.updateFieldById(id = bufferId, true)
}
fun getLocalUserDataForCsv(context: Context): Boolean {
val localUserDataLiveData: LiveData<List<HemoCubeTestData>> = hemoCubeDao.getAll()
@@ -455,16 +452,12 @@ class TrueHemeViewModel @Inject constructor(
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
)
val level: Int = intent.getIntExtra(
BatteryManager.EXTRA_LEVEL, -1
)
val scale: Int = intent.getIntExtra(
BatteryManager.EXTRA_SCALE, -1
)
level * 100 / scale.toFloat()
}
@@ -474,8 +467,7 @@ class TrueHemeViewModel @Inject constructor(
fun getBatteryTemperature(): Float? {
val batteryTemp: Float? = batteryStatus?.let { intent ->
val temperature = intent.getIntExtra(
BatteryManager.EXTRA_TEMPERATURE,
0
BatteryManager.EXTRA_TEMPERATURE, 0
)
temperature.toFloat() / 10
}
@@ -484,14 +476,11 @@ class TrueHemeViewModel @Inject constructor(
}
fun getBatteryVoltage(context: Context): Float {
val batteryIntent =
context.registerReceiver(
null,
IntentFilter(Intent.ACTION_BATTERY_CHANGED)
)
val batteryIntent = context.registerReceiver(
null, IntentFilter(Intent.ACTION_BATTERY_CHANGED)
)
val voltage = batteryIntent?.getIntExtra(
BatteryManager.EXTRA_VOLTAGE,
0
BatteryManager.EXTRA_VOLTAGE, 0
) ?: 0
// milli-volts to volts
@@ -500,8 +489,7 @@ class TrueHemeViewModel @Inject constructor(
fun getBatteryCapacity(context: Context): Int {
val batteryManager =
context.getSystemService(Context.BATTERY_SERVICE) as BatteryManager
val batteryManager = context.getSystemService(Context.BATTERY_SERVICE) as BatteryManager
val currentCapacity =
batteryManager.getIntProperty(BatteryManager.BATTERY_PROPERTY_CHARGE_COUNTER)
@@ -510,8 +498,7 @@ class TrueHemeViewModel @Inject constructor(
fun getBatteryMaxCapacity(context: Context): Float {
val batteryManager = context.getSystemService(Context.BATTERY_SERVICE) as BatteryManager
val designCapacity =
batteryManager.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY)
val designCapacity = batteryManager.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY)
val currentCapacity =
batteryManager.getIntProperty(BatteryManager.BATTERY_PROPERTY_CHARGE_COUNTER)
@@ -528,8 +515,7 @@ class TrueHemeViewModel @Inject constructor(
hemoCubeTestData.forEach { data ->
data.localFlag = true
hemoCubeDao.updateCSVFieldById(
data._id,
true
data._id, true
)
}
}

View File

@@ -32,6 +32,20 @@ import androidx.lifecycle.MutableLiveData
import com.example.hpostesting.data.constant.DataHolder
import com.example.hpostesting.util.Result
import com.example.hpostesting.data.constant.Constants
import com.example.hpostesting.data.constant.Constants.negativeBoderLineMetricCheck2mmStMax
import com.example.hpostesting.data.constant.Constants.negativeBoderLineMetricCheck2mmStMin
import com.example.hpostesting.data.constant.Constants.negativeBorderlineMaxSt2mm
import com.example.hpostesting.data.constant.Constants.negativeBorderlineMinSt2mm
import com.example.hpostesting.data.constant.Constants.normalMaxSt2mm
import com.example.hpostesting.data.constant.Constants.normalMinSt2mm
import com.example.hpostesting.data.constant.Constants.positiveBoderLineMetricCheck2mmStMax
import com.example.hpostesting.data.constant.Constants.positiveBoderLineMetricCheck2mmStMin
import com.example.hpostesting.data.constant.Constants.positiveForSickleCellMaxSt2mm
import com.example.hpostesting.data.constant.Constants.positiveForSickleCellMinSt2mm
import com.example.hpostesting.data.constant.Constants.sickleCellDiseaseMaxSt2mm
import com.example.hpostesting.data.constant.Constants.sickleCellDiseaseMinSt2mm
import com.example.hpostesting.data.constant.Constants.sickleCellTraitMaxSt2mm
import com.example.hpostesting.data.constant.Constants.sickleCellTraitMinSt2mm
import com.example.hpostesting.data.constant.HemoCubeCommands
import com.example.hpostesting.data.constant.TestStatus
import com.example.hpostesting.data.model.TestState
@@ -1196,6 +1210,20 @@ class TrueHemeTestFragment : Fragment() {
binding.btnSamplestart.isEnabled = true
return
}
}else if(cuvetteSizeSP == "2mmSt"){
inRange2mmLed1 = led1Average in min2mmLed1..max2mmLed1
inRange2mmLed2 = led2Average in min2mmLed2..max2mmLed2
if(!inRange2mmLed1 || !inRange2mmLed2){
trueHemeTestViewModel.messages.postValue("Absorbance out-of-Range - Repeat test with adequate blood volume and Incubation time")
this.testStatusCode = TestStatus.BUFFER_PRINT_COMPLETED.code
binding.testing.visibility = View.GONE
binding.btnPlaceRefreshbuffer.visibility = View.GONE
binding.btnPlacebuffer.visibility = View.GONE
binding.btnSamplestart.visibility = View.VISIBLE
binding.btnSamplestart.isClickable = true
binding.btnSamplestart.isEnabled = true
return
}
}
}
}
@@ -1478,6 +1506,25 @@ class TrueHemeTestFragment : Fragment() {
return "Positive for Sickle Cell. Confirm with HPLC"
}
}
}else if(cuvetteSizeSP == "2mmSt"){
if (deviceRatioClass == "Negative Borderline") {
if (borderlineMetric < negativeBoderLineMetricCheck2mmStMin){//1.34
return "Sickle Cell Trait"
}else if(borderlineMetric > negativeBoderLineMetricCheck2mmStMax){
return "Normal"
}else if(borderlineMetric > negativeBoderLineMetricCheck2mmStMin && borderlineMetric < negativeBoderLineMetricCheck2mmStMax){
return "Negative borderline. Confirm with HPLC"
}
}
if (deviceRatioClass == "Positive for Sickle Cell. HPLC for Confirmation") {
if (borderlineMetric < positiveBoderLineMetricCheck2mmStMin){//1.34
return "Sickle Cell Disease"
}else if(borderlineMetric > positiveBoderLineMetricCheck2mmStMax){
return "Sickle Cell Trait"
}else if(borderlineMetric > positiveBoderLineMetricCheck2mmStMin && borderlineMetric < positiveBoderLineMetricCheck2mmStMax){
return "Positive for Sickle Cell. Confirm with HPLC"
}
}
}
}
} catch (e: Exception) {
@@ -1548,6 +1595,22 @@ class TrueHemeTestFragment : Fragment() {
if (ratio in sickleCellDiseaseMin2mm..sickleCellDiseaseMax2mm){
return "Sickle Cell Disease"
}
}else if(cuvetteSize == "2mmSt"){
if (ratio in normalMinSt2mm..normalMaxSt2mm) {
return "Normal"
}
if (ratio in negativeBorderlineMinSt2mm..negativeBorderlineMaxSt2mm){
return "Negative Borderline"
}
if (ratio in sickleCellTraitMinSt2mm..sickleCellTraitMaxSt2mm){
return "Sickle Cell Trait"
}
if (ratio in positiveForSickleCellMinSt2mm..positiveForSickleCellMaxSt2mm){//0.36
return "Positive for Sickle Cell. HPLC for Confirmation"
}
if (ratio in sickleCellDiseaseMinSt2mm..sickleCellDiseaseMaxSt2mm){
return "Sickle Cell Disease"
}
}
} else {

View File

@@ -27,7 +27,6 @@ import androidx.work.PeriodicWorkRequestBuilder
import androidx.work.WorkManager
import com.example.hpostesting.util.CsvWriter
import com.example.hpostesting.data.constant.DataHolder
import com.example.hpostesting.util.NetworkStatusLiveData
import com.example.hpostesting.util.Result
import com.example.hpostesting.data.constant.Constants
import com.example.hpostesting.data.dao.HemoCubeBufferDao
@@ -50,14 +49,19 @@ 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 com.example.hpostesting.util.NetworkMonitor
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import okhttp3.Headers
import okhttp3.MediaType.Companion.toMediaTypeOrNull
import okhttp3.MultipartBody
import okhttp3.RequestBody.Companion.asRequestBody
import okhttp3.ResponseBody
import java.io.BufferedWriter
import java.io.File
import java.io.FileWriter
import java.text.SimpleDateFormat
import java.util.Calendar
import java.util.Locale
@@ -72,6 +76,7 @@ class TrueHemeTestViewModel @Inject constructor(
private val repository: Repository,
private val logFileManager: LogFileManager,
private val localFileDataSource: LocalFileDataSource,
networkMonitor: NetworkMonitor,
context: Context,
) : ViewModel() {
private var testUpload: Boolean = false
@@ -87,6 +92,9 @@ class TrueHemeTestViewModel @Inject constructor(
// init {
// startPeriodicCheckUpdate()
// }
init {
networkMonitor.startMonitoring()
}
val loginResponse = MutableLiveData<Result<LoginResponse>>()
@@ -103,7 +111,7 @@ class TrueHemeTestViewModel @Inject constructor(
// 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)
private val _networkStatusLiveData =networkMonitor.isConnected
val allUserData = hemoCubeDao.getAll()
val allLocalData = hemoCubeDao.getAll()
val allPendingUserToUpload = hemoCubeDao.getPendingUser()
@@ -771,7 +779,44 @@ class TrueHemeTestViewModel @Inject constructor(
return maxCapacity
}
suspend fun createCSVNew(data: List<HemoCubeTestData>, context: Context): File? {
return withContext(Dispatchers.IO) {
try {
val timestamp = System.currentTimeMillis()
val fileName = "hemocube_data_$timestamp.csv"
// Create file in app's external files directory
val appFile = File(context.getExternalFilesDir(null), fileName)
// Ensure this file is used by localFileDataSource
val exportSuccess = localFileDataSource.exportDataToCSV(appFile.absolutePath, data)
if (exportSuccess) {
// Update database records
data.forEach { item ->
hemoCubeDao.updateCSVFieldById(
item._id,
true
)
}
// Verify file exists and has content
if (appFile.exists() && appFile.length() > 0) {
return@withContext appFile
} else {
Log.e("CSVCreation", "File creation verified failed: exists=${appFile.exists()}, size=${appFile.length()}")
return@withContext null
}
} else {
Log.e("CSVCreation", "exportDataToCSV returned false")
return@withContext null
}
} catch (e: Exception) {
Log.e("CSVCreation", "Error creating CSV", e)
return@withContext null
}
}
}
fun createCSV(hemoCubeTestData: List<HemoCubeTestData>, appContext: Context) =
viewModelScope.launch {
val fileName = "HPOS${getCurrentDate()}.csv"
@@ -797,4 +842,5 @@ class TrueHemeTestViewModel @Inject constructor(
hemoCubeDao.updateTest(testDetails!!)
}
}
}

View File

@@ -0,0 +1,99 @@
package com.example.hpostesting.util
import android.content.Context
import android.net.ConnectivityManager
import android.net.Network
import android.net.NetworkCapabilities
import android.net.NetworkRequest
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.cancel
import kotlinx.coroutines.launch
import java.net.HttpURLConnection
import java.net.URL
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
class NetworkMonitor @Inject constructor(
@ApplicationContext private val context: Context
) {
private val connectivityManager =
context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
private val _isConnected = MutableLiveData(false)
val isConnected: LiveData<Boolean> get() = _isConnected
private val networkScope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
private val networkCallback = object : ConnectivityManager.NetworkCallback() {
override fun onAvailable(network: Network) {
performPingCheck()
}
override fun onLost(network: Network) {
_isConnected.postValue(false)
}
override fun onCapabilitiesChanged(
network: Network, networkCapabilities: NetworkCapabilities
) {
// Recheck connectivity when capabilities change (e.g., network becomes validated)
performPingCheck()
}
}
fun startMonitoring() {
// Check initial connectivity status
val network = connectivityManager.activeNetwork
val capabilities = connectivityManager.getNetworkCapabilities(network)
val isInitiallyConnected = capabilities?.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) == true
if (isInitiallyConnected) {
performPingCheck()
} else {
_isConnected.postValue(false)
}
val networkRequest =
NetworkRequest.Builder().addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
.build()
connectivityManager.registerNetworkCallback(networkRequest, networkCallback)
}
fun stopMonitoring() { // never needed to call
try {
connectivityManager.unregisterNetworkCallback(networkCallback)
} catch (_: IllegalArgumentException) {
// Ignore if callback was not registered
}
networkScope.cancel()
}
private fun performPingCheck() {
networkScope.launch {
try {
val url = URL("https://www.google.com")
val connection = url.openConnection() as HttpURLConnection
connection.connectTimeout = 2000
connection.readTimeout = 2000
connection.setRequestProperty("Connection", "close")
connection.connect()
// Consider any 2xx response as successful
val isReachable = connection.responseCode in 200..299
_isConnected.postValue(isReachable)
connection.disconnect()
} catch (e: Exception) {
_isConnected.postValue(false)
}
}
}
}

View File

@@ -1,75 +0,0 @@
/*
* // Copyright (c) 2024 ShanMukha Innovations Pvt. Ltd. All rights reserved.
* // Notice: All information contained herein is, and remains
* // the property of ShanMukha Innovations Pvt. Ltd. and its suppliers,
* // if any. The intellectual and technical concepts contained
* // herein are proprietary to ShanMukha Innovations Pvt. Ltd.
* // and its suppliers and may be covered by Indian and Foreign Patents,
* // patents in process, and are protected by trade secret or copyright law.
* // Dissemination of this information or reproduction of this material
* // is strictly forbidden unless prior written permission is obtained
* // from ShanMukha Innovations Pvt. Ltd.
*/
package com.example.hpostesting.util
import android.content.Context
import android.net.ConnectivityManager
import android.net.Network
import android.net.NetworkCapabilities
import android.net.NetworkRequest
import android.os.Build
import androidx.annotation.RequiresApi
import androidx.lifecycle.LiveData
class NetworkStatusLiveData(context: Context) : LiveData<Boolean>() {
private val connectivityManager: ConnectivityManager =
context.applicationContext.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
private val networkCallback = object : ConnectivityManager.NetworkCallback() {
override fun onAvailable(network: Network) {
postValue(true)
}
override fun onLost(network: Network) {
postValue(false)
}
}
@RequiresApi(Build.VERSION_CODES.M)
override fun onActive() {
super.onActive()
postValue(isNetworkAvailable())
registerNetworkCallback()
}
override fun onInactive() {
super.onInactive()
unregisterNetworkCallback()
}
private fun registerNetworkCallback() {
val networkRequest = NetworkRequest.Builder()
.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
.addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
.addTransportType(NetworkCapabilities.TRANSPORT_ETHERNET)
.build()
connectivityManager.registerNetworkCallback(networkRequest, networkCallback)
}
private fun unregisterNetworkCallback() {
connectivityManager.unregisterNetworkCallback(networkCallback)
}
@RequiresApi(Build.VERSION_CODES.M)
private fun isNetworkAvailable(): Boolean {
val network = connectivityManager.activeNetwork
val networkCapabilities = connectivityManager.getNetworkCapabilities(network)
return networkCapabilities != null &&
networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) &&
networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED)
}
}

View File

@@ -0,0 +1,5 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#000000" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
<path android:fillColor="@android:color/white" android:pathData="M7,5h10v2h2L19,3c0,-1.1 -0.9,-1.99 -2,-1.99L7,1c-1.1,0 -2,0.9 -2,2v4h2L7,5zM15.41,16.59L20,12l-4.59,-4.59L14,8.83 17.17,12 14,15.17l1.41,1.42zM10,15.17L6.83,12 10,8.83 8.59,7.41 4,12l4.59,4.59L10,15.17zM17,19L7,19v-2L5,17v4c0,1.1 0.9,2 2,2h10c1.1,0 2,-0.9 2,-2v-4h-2v2z"/>
</vector>

View File

@@ -88,12 +88,22 @@
app:layout_constraintBottom_toBottomOf="@id/spinnerCuvette"
app:layout_constraintStart_toEndOf="@id/spinnerCuvette"
app:layout_constraintTop_toTopOf="@id/spinnerCuvette" />
<com.google.android.material.button.MaterialButton
android:id="@+id/btn_download"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="24dp"
android:text="Send Data To Server"
android:textColor="@color/white"
app:cornerRadius="16dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/spinnerCuvette"
/>
<FrameLayout
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintTop_toBottomOf="@+id/btn_add_size"
app:layout_constraintTop_toBottomOf="@+id/btn_download"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:layout_marginTop="10dp"/>

View File

@@ -1,38 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<!-- RadioGroup to select servers -->
<RadioGroup
android:id="@+id/radioGroupServers"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:orientation="vertical">
<!-- Radio Buttons for each server option -->
<RadioButton
android:id="@+id/radioButtonInternal"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Internal" />
<RadioButton
android:id="@+id/radioButtonClinical"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Clinical" />
<RadioButton
android:id="@+id/radioButtonCustomer"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Customer" />
</RadioGroup>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -16,12 +16,12 @@ package com.example.hpostesting
import android.content.Context
import android.content.SharedPreferences
import androidx.arch.core.executor.testing.InstantTaskExecutorRule
import com.example.hpostesting.util.NetworkStatusLiveData
import com.example.hpostesting.data.dao.HemoCubeDao
import com.example.hpostesting.data.model.Response
import com.example.hpostesting.data.model.diagnostics.DiagnosticsData
import com.example.hpostesting.data.repository.Repository
import com.example.hpostesting.presentation.autodac.AutoDacViewModel
import com.example.hpostesting.util.NetworkMonitor
import io.mockk.*
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -43,7 +43,7 @@ class AutoDacViewModelTest {
private val repository = mockk<Repository>()
private val context = mockk<Context>()
private val sharedPreferences = mockk<SharedPreferences>()
private val networkStatusLiveData = mockk<NetworkStatusLiveData>()
private val networkStatusLiveData = mockk<NetworkMonitor>()
@Before
fun setup() {
@@ -54,7 +54,7 @@ class AutoDacViewModelTest {
every { context.getSystemService(any()) } returns networkStatusLiveData
// Mocking the NetworkStatusLiveData
every { networkStatusLiveData.observe(any(), any()) } just Runs
every { networkStatusLiveData.startMonitoring() } just Runs
every { hemoCubeDao.getAll() } returns mockk()
// every { repository.addDiagnostics(any()) } returns Response.Success(Unit)