Compare commits

..

20 Commits

Author SHA1 Message Date
Sathwik C S
b70c129b1b Edit README.md 2025-08-17 12:18:15 +05:30
Sathwik C S
a9e7d2e6e6 Edit README.md 2025-08-07 12:19:39 +05:30
Sathwik
dd887802bc updated settings, and report gen 2025-08-06 17:04:08 +05:30
Sathwik
92f426d7be auto smbt button now only shows up on admin mode 2025-07-29 15:51:19 +05:30
Sathwik
7a0064a0d3 Updted the string, added the sutosmbt button in settings 2025-07-28 16:50:44 +05:30
Sathwik
669cf00d60 added print option in panels, created , activity fragment , and viewmodel , added the xml views and cards 2025-07-24 11:31:47 +05:30
Sathwik
00b341169d Merge remote-tracking branch 'origin/dev-server-2.1.134-with-printer' into dev-server-2.1.134-with-printer 2025-07-24 11:24:42 +05:30
sathwik
4293e87f07 added print option in panels, created , activity fragment , and viewmodel , added the xml views and cards 2025-07-24 11:24:22 +05:30
sathwik
05ce1d8411 added print option in panels, created , activity fragment , and viewmodel , added the xml views and cards 2025-07-16 18:21:12 +05:30
sathwik
b6ae754f1b updated the uto manifest 2025-07-16 18:17:21 +05:30
sathwik
c3acb41590 updated the uto sumbit 2025-07-15 14:57:19 +05:30
sathwik
2ee6b65ca5 implementing the secure storage using ndk 2025-07-08 11:25:04 +05:30
sathwik
28b8409207 implementing the secure storage using ndk 2025-07-01 16:27:55 +05:30
sathwik
5c56ef7630 implementing the secure storage using ndk 2025-07-01 16:27:30 +05:30
sathwik
2fbc280b4a implementing the secure storage 2025-06-19 11:58:47 +05:30
sathwikcs
e9c8a9a887 R&D app 2025-05-15 19:04:23 +05:30
sathwikcs
e7ecf7eedf R&D app 2025-05-15 12:57:53 +05:30
sathwikcs
e3435f2182 131-STABLE-super-server 2025-03-13 15:50:04 +05:30
sathwikcs
f6cdd64ef6 131-STABLE-super-server 2025-03-13 15:49:25 +05:30
sathwikcs
7ad21ea5a9 131-STABLE-super-server 2025-03-13 15:23:15 +05:30
36 changed files with 1624 additions and 151 deletions

View File

@@ -1,7 +1,23 @@
# Latest hpos app version # Latest hpos app version
## current version : 2.1.132 ### current version : 2.1.133
### Release key ### Release key
key0: prime24 key0: prime24
#### **Change logs :**
- [✅] Auto Sumbmit
- [✅] Auto Sumbmit Toggle
- [✅] Genegrate report
- [✅] Report Selector
- [✅] Report Printer
- [✅] Report Disclaimer Editor
#### **Completed 👌**
**version of android studio used**
`Android Studio Koala Feature Drop | 2024.1.2 Patch 1`

View File

@@ -7,6 +7,7 @@ plugins {
id 'androidx.navigation.safeargs.kotlin' id 'androidx.navigation.safeargs.kotlin'
id 'kotlin-kapt' id 'kotlin-kapt'
id 'com.google.firebase.crashlytics' id 'com.google.firebase.crashlytics'
id 'kotlin-parcelize'
} }
//apply plugin: 'kotlin-android' //apply plugin: 'kotlin-android'
@@ -20,9 +21,14 @@ android {
applicationId "in.sminnovations.hpostesting.server" applicationId "in.sminnovations.hpostesting.server"
minSdk 30 minSdk 30
targetSdk 34 targetSdk 34
versionCode 134 versionCode 139
versionName "2.1.131" versionName "2.1.133"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
externalNativeBuild {
cmake {
cppFlags ''
}
}
} }
@@ -54,6 +60,12 @@ android {
packagingOptions { packagingOptions {
exclude 'mockito-extensions/org.mockito.plugins.MockMaker' exclude 'mockito-extensions/org.mockito.plugins.MockMaker'
} }
externalNativeBuild {
cmake {
path file('src/main/cpp/CMakeLists.txt')
version '3.22.1'
}
}
// testOptions { // testOptions {
// unitTests { // unitTests {
// includeAndroidResources = true // includeAndroidResources = true

View File

@@ -1,37 +0,0 @@
{
"version": 3,
"artifactType": {
"type": "APK",
"kind": "Directory"
},
"applicationId": "in.sminnovations.hpostesting.dev",
"variantName": "release",
"elements": [
{
"type": "SINGLE",
"filters": [],
"attributes": [],
"versionCode": 127,
"versionName": "2.1.127",
"outputFile": "app-release.apk"
}
],
"elementType": "File",
"baselineProfiles": [
{
"minApi": 28,
"maxApi": 30,
"baselineProfiles": [
"baselineProfiles/1/app-release.dm"
]
},
{
"minApi": 31,
"maxApi": 2147483647,
"baselineProfiles": [
"baselineProfiles/0/app-release.dm"
]
}
],
"minSdkVersionForDexing": 21
}

View File

@@ -24,13 +24,12 @@
<uses-feature android:name="android.hardware.usb.host" /> <uses-feature android:name="android.hardware.usb.host" />
<uses-permission android:name="android.permission.USB_PERMISSION" /> <uses-permission android:name="android.permission.USB_PERMISSION" />
<uses-permission android:name="android.permission.GET_ACCOUNTS" /> <uses-permission android:name="android.permission.GET_ACCOUNTS" />
<uses-permission android:name="android.permission.MANAGE_ACCOUNTS" /> <uses-permission android:name="android.permission.MANAGE_ACCOUNTS" />
<uses-permission android:name="android.permission.ACCOUNT_MANAGER" <uses-permission
android:name="android.permission.ACCOUNT_MANAGER"
tools:ignore="ProtectedPermissions" /> tools:ignore="ProtectedPermissions" />
<application <application
android:name="com.example.hpostesting.HPOSTestingApplication" android:name="com.example.hpostesting.HPOSTestingApplication"
android:allowBackup="true" android:allowBackup="true"
@@ -43,10 +42,13 @@
android:supportsRtl="true" android:supportsRtl="true"
android:theme="@style/Theme.HPOSTesting" android:theme="@style/Theme.HPOSTesting"
tools:targetApi="31"> tools:targetApi="31">
<activity
android:name="com.example.hpostesting.presentation.reportgen.ReportActivity"
android:exported="false" />
<activity <activity
android:name="com.example.hpostesting.presentation.main_base.UpdateValuesActivity" android:name="com.example.hpostesting.presentation.main_base.UpdateValuesActivity"
android:exported="false" android:exported="false"
android:screenOrientation="portrait"/> android:screenOrientation="portrait" />
<service <service
android:name="com.example.hpostesting.util.MyAuthenticatorService" android:name="com.example.hpostesting.util.MyAuthenticatorService"

View File

@@ -0,0 +1,24 @@
cmake_minimum_required(VERSION 3.18.1)
project("native-server")
# Create the shared library from server.cpp
add_library(
native-server
SHARED
server.cpp
)
# Find Android logging library
find_library(
log-lib
log
)
# Link logging library to our native lib
target_link_libraries(
native-server
${log-lib}
)

View File

@@ -0,0 +1,53 @@
#include <jni.h>
#include <string>
#include <unordered_map>
std::vector<std::pair<int, std::pair<std::string, std::unordered_map<std::string, std::string>>>> firebaseDataOrdered = {
{0, {"dev", {{"apiKey", "AIzaSyAVdNGCev_AFX0qmuZJF6kxzRzXUTeAW5I"}, {"appId", "1:650071678820:android:a53292637abb7c0d6c6471"}, {"projectId", "hpos-af3cc"}, {"storageBucket", "hpos-af3cc.appspot.com"}}}},
{1, {"qc-qa", {{"apiKey", "AIzaSyC4d4GhWHr_NMJAeBvnztQ_yQ3Qe9MAnLs"}, {"appId", "1:1004619739289:android:3dbcefb10ea99654e5c808"}, {"projectId", "hpos-qa"}, {"storageBucket", "hpos-qa.appspot.com"}}}},
{2, {"prod", {{"apiKey", "AIzaSyA_O0oZGzy3Kiw9fiLQ3OFPME-xQJP88vs"}, {"appId", "1:1012719870714:android:d8d7c962d96f4097de5f43"}, {"projectId", "hpos-prod"}, {"storageBucket", "hpos-prod.appspot.com"}}}},
{3, {"pre prod", {{"apiKey", "AIzaSyDYySi27LioZGNisP1NfnNU5inJX_0FT38"}, {"appId", "1:121176529204:android:e6841fdac57bdc95bbed61"}, {"projectId", "hpos-preprod"}, {"storageBucket", "hpos-preprod.appspot.com"}}}},
{4, {"SMI R&D", {{"apiKey", "AIzaSyAVxpilbB804iRCCpMBDjjmdZhQguctGHM"}, {"appId", "1:327771387914:android:a3b4ed2e42ea6e38dc87f3"}, {"projectId", "hposrandd"}, {"storageBucket", "hposrandd.firebasestorage.app"}}}},
{5, {"molbio", {{"apiKey", "molbio"}, {"appId", "molbio"}, {"projectId", "molbio"}, {"storageBucket", "molbio"}}}}
};
extern "C"
JNIEXPORT jobject JNICALL
Java_com_example_hpostesting_firebase_FirebaseManager_getFirebaseDataMap(JNIEnv *env,jobject thiz) {
jclass mapClass = env->FindClass("java/util/LinkedHashMap");
jmethodID init = env->GetMethodID(mapClass, "<init>", "()V");
jmethodID put = env->GetMethodID(mapClass, "put", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
jobject outerMap = env->NewObject(mapClass, init);
for (const auto &entry : firebaseDataOrdered) {
int order = entry.first;
const std::string &outerKeyStr = entry.second.first;
const auto &innerMapData = entry.second.second;
jobject innerMap = env->NewObject(mapClass, init);
// "int" field for order
jstring intKey = env->NewStringUTF("int");
jstring intValue = env->NewStringUTF(std::to_string(order).c_str());
env->CallObjectMethod(innerMap, put, intKey, intValue);
env->DeleteLocalRef(intKey);
env->DeleteLocalRef(intValue);
// actual firebase config keys
for (const auto &innerPair : innerMapData) {
jstring key = env->NewStringUTF(innerPair.first.c_str());
jstring value = env->NewStringUTF(innerPair.second.c_str());
env->CallObjectMethod(innerMap, put, key, value);
env->DeleteLocalRef(key);
env->DeleteLocalRef(value);
}
jstring outerKey = env->NewStringUTF(outerKeyStr.c_str());
env->CallObjectMethod(outerMap, put, outerKey, innerMap);
env->DeleteLocalRef(outerKey);
env->DeleteLocalRef(innerMap);
}
return outerMap;
}

View File

@@ -0,0 +1,254 @@
package com.example.hpostesting.FHIRFormater
import com.example.hpostesting.data.model.patient.HemoCubeTestData
import com.example.hpostesting.data.model.patient.UserData
import org.json.JSONArray
import org.json.JSONObject
class RecommendedFHIRConverter {
private fun createPatient(data: HemoCubeTestData, useStandardCodes: Boolean): JSONObject {
val patient = JSONObject()
patient.put("resourceType", "Patient")
patient.put("id", "patient-${data.sampleid}")
// Structured name
val nameArray = JSONArray()
val nameObj = JSONObject()
nameObj.put("use", "official")
nameObj.put("text", data.name)
nameArray.put(nameObj)
patient.put("name", nameArray)
// Gender (FHIR uses "male", "female", "other", "unknown")
patient.put("gender", data.gender.lowercase())
// Birth date estimation from age (optional: validate or remove if unknown)
val birthYear = try {
2025 - data.age.toInt()
} catch (e: Exception) {
null
}
if (birthYear != null) patient.put("birthDate", "$birthYear-01-01")
// ABHA ID as identifier
if (data.abhaId.isNotBlank()) {
val identifiers = JSONArray()
val identifier = JSONObject()
identifier.put("system", "https://healthid.ndhm.gov.in")
identifier.put("value", data.abhaId)
identifiers.put(identifier)
patient.put("identifier", identifiers)
}
return patient
}
private fun createDevice(data: HemoCubeTestData): JSONObject {
val device = JSONObject()
device.put("resourceType", "Device")
device.put("id", "device-${data.deviceSerialNumber}")
device.put("serialNumber", data.deviceSerialNumber)
// Device type as codeable concept
val type = JSONObject()
type.put("text", data.deviceType)
device.put("type", type)
// Device version
if (!data.appVersion.isNullOrBlank()) {
val versions = JSONArray()
val version = JSONObject()
version.put("type", JSONObject().put("text", "appVersion"))
version.put("value", data.appVersion)
versions.put(version)
device.put("version", versions)
}
return device
}
private fun createHemoglobinObservation(data: HemoCubeTestData, useStandardCodes: Boolean): JSONObject {
val observation = JSONObject()
observation.put("resourceType", "Observation")
observation.put("id", "hemoglobin-${data.sampleid}")
observation.put("status", if (data.testStatus == true) "final" else "preliminary")
// Standard LOINC code or fallback
val code = JSONObject()
val codingArray = JSONArray()
if (useStandardCodes) {
val coding = JSONObject()
coding.put("system", "http://loinc.org")
coding.put("code", "718-7") // LOINC for Hemoglobin [Mass/volume] in Blood
coding.put("display", "Hemoglobin [Mass/volume] in Blood")
codingArray.put(coding)
}
code.put("coding", codingArray)
code.put("text", "Hemoglobin Analysis")
observation.put("code", code)
// Subject reference to patient
observation.put("subject", JSONObject().put("reference", "Patient/patient-${data.sampleid}"))
// Device reference
observation.put("device", JSONObject().put("reference", "Device/device-${data.deviceSerialNumber}"))
// Time of observation
if (!data.testTime.isNullOrBlank()) {
observation.put("effectiveDateTime", data.testTime)
}
// Main result: if hb3 or hb4 exists, use as value
val hbValue = data.hb3 ?: data.hb4
if (hbValue != null) {
val valueQuantity = JSONObject()
valueQuantity.put("value", hbValue)
valueQuantity.put("unit", "g/dL")
valueQuantity.put("system", "http://unitsofmeasure.org")
valueQuantity.put("code", "g/dL")
observation.put("valueQuantity", valueQuantity)
}
// Optional extensions
val components = JSONArray()
fun addComponent(codeText: String, value: Double?) {
if (value != null) {
val comp = JSONObject()
comp.put("code", JSONObject().put("text", codeText))
comp.put("valueQuantity", JSONObject().put("value", value))
components.put(comp)
}
}
addComponent("HB3", data.hb3)
addComponent("HB4", data.hb4)
addComponent("Device Ratio", data.deviceRatio)
addComponent("Blood Group", null) // can't use non-numeric here unless coded properly
if (components.length() > 0) {
observation.put("component", components)
}
return observation
}
private fun createBundleEntry(resource: JSONObject): JSONObject {
val entry = JSONObject()
entry.put("fullUrl", "urn:uuid:${resource.getString("id")}")
entry.put("resource", resource)
entry.put("request", JSONObject().apply {
put("method", "POST")
put("url", resource.getString("resourceType"))
})
return entry
}
fun convertToFHIR(data: HemoCubeTestData, useStandardCodes: Boolean = false): String {
val bundle = JSONObject()
bundle.put("resourceType", "Bundle")
bundle.put("id", "hemocube-test-${data.sampleid}")
bundle.put("type", "collection")
val entries = JSONArray()
entries.put(createBundleEntry(createPatient(data, useStandardCodes)))
entries.put(createBundleEntry(createDevice(data)))
entries.put(createBundleEntry(createHemoglobinObservation(data, useStandardCodes)))
bundle.put("entry", entries)
return bundle.toString(2)
}
}
fun sus() {
val sampleData = HemoCubeTestData(
sampleid = 1234,
_id = "local-001",
name = "John Doe",
incubationTime = "15",
bloodGroup = "B+",
age = "30",
state = "Karnataka",
abhaId = "29-1234-4567",
userImageURL = "https://example.com/user.jpg",
location = UserData.Location(latitude = 12.9716, longitude = 77.5946),
reportUploadTime = "2025-07-15T09:30:00+05:30",
testType = "HEMOCUBE",
testTime = "2025-07-15T09:15:00+05:30",
testStatus = true,
gender = "male",
localFlag = true,
deviceId = "device-5678",
appVersion = "1.4.2",
deviceSerialNumber = "SN-001-XY",
deviceType = "HEMOCUBE",
kitSerial = "KIT-20250715",
resultData = "Raw data goes here",
led1Buffer = 0.123,
led2Buffer = 0.456,
led3Buffer = 0.789,
led4Buffer = 0.321,
led1Sample = 0.654,
led2Sample = 0.987,
led3Sample = 0.432,
led4Sample = 0.210,
led1Average = 0.300,
led2Average = 0.500,
led3Average = 0.400,
led4Average = 0.450,
abs1 = 0.12,
abs2 = 0.23,
abs3 = 0.34,
abs4 = 0.45,
hb3 = 13.5,
hb4 = 13.7,
led1Gain1 = 1.1,
led2Gain1 = 1.2,
led3Gain1 = 1.3,
led4Gain1 = 1.4,
deviceRatio = 0.87,
calculatedRatio = 0.89,
predictedDenovixRatio = 0.91,
slopeRatio = 0.93,
coefficients = "a=1.0,b=2.0,c=3.0",
classificationResult = "Normal",
prdClassification = "PRD1",
deviceRatioClass = "A",
slopeRatioClass = "B",
borderlineMethod2Class = "C",
errorMessages = "",
batteryLevel = "85%",
batteryCapacity = "2800mAh",
batteryMaxCapacity = "3000mAh",
batteryTemperature = "35C",
batteryVoltage = "3.7V",
molbioFlag = false,
quickCapture = false,
solution = "Blood Sample",
concentration = "13.5g/dL",
filter = "None",
volume = "20uL",
isCSVCreated = true,
labName = "ABC Labs",
cuvetteSize = "Standard",
district = "Bangalore Urban",
centerName = "Health Center 1",
ipAddress = "192.168.1.101",
configUpdatedRecent = "true"
)
val converter = RecommendedFHIRConverter()
val standardFhir = converter.convertToFHIR(sampleData, useStandardCodes = true)
println("FHIR Bundle Output:\n$standardFhir")
}

View File

@@ -15,7 +15,6 @@ package com.example.hpostesting
import android.app.Application import android.app.Application
import com.example.hpostesting.firebase.FirebaseManager import com.example.hpostesting.firebase.FirebaseManager
import com.google.firebase.firestore.FirebaseFirestore
import com.google.firebase.firestore.FirebaseFirestoreSettings import com.google.firebase.firestore.FirebaseFirestoreSettings
import dagger.hilt.android.HiltAndroidApp import dagger.hilt.android.HiltAndroidApp
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
@@ -23,8 +22,16 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.SupervisorJob
import javax.inject.Inject import javax.inject.Inject
@HiltAndroidApp @HiltAndroidApp
class HPOSTestingApplication : Application() { class HPOSTestingApplication : Application() {
companion object {
init {
System.loadLibrary("native-server")
}
}
@Inject @Inject
lateinit var firebaseManager: FirebaseManager lateinit var firebaseManager: FirebaseManager
override fun onCreate() { override fun onCreate() {
@@ -39,4 +46,5 @@ class HPOSTestingApplication : Application() {
val firestore = firebaseManager.getCurrentFirestore() val firestore = firebaseManager.getCurrentFirestore()
firestore.firestoreSettings = firestoreSettings firestore.firestoreSettings = firestoreSettings
} }
} }

View File

@@ -32,7 +32,6 @@ import retrofit2.http.PUT
import retrofit2.http.Part import retrofit2.http.Part
interface MolbioResultApi { interface MolbioResultApi {
@PUT("deviceService/results/HPOS") @PUT("deviceService/results/HPOS")
suspend fun uploadResults( suspend fun uploadResults(
@Body molbioV2ResultRequest: MolbioV2ResultRequest, @Body molbioV2ResultRequest: MolbioV2ResultRequest,
@@ -47,6 +46,7 @@ interface MolbioResultApi {
// suspend fun deviceUpdate( // suspend fun deviceUpdate(
// @Body deviceUpdateRequest: DeviceUpdateRequest, // @Body deviceUpdateRequest: DeviceUpdateRequest,
// ): ResponseBody // ): ResponseBody
@POST("deviceService/device/getUpdate") @POST("deviceService/device/getUpdate")
suspend fun deviceUpdate( suspend fun deviceUpdate(
@Body deviceUpdateRequest: DeviceUpdateRequest, @Body deviceUpdateRequest: DeviceUpdateRequest,
@@ -62,7 +62,6 @@ interface MolbioResultApi {
@Part logFile: MultipartBody.Part, @Part logFile: MultipartBody.Part,
): UploadLogsResponse ): UploadLogsResponse
@PUT("deviceService/device/uploadDeviceDiagnostics") @PUT("deviceService/device/uploadDeviceDiagnostics")
suspend fun deviceDiagnostics( suspend fun deviceDiagnostics(
@Body deviceDiagnosticsRequest: DeviceDiagnosticsRequest @Body deviceDiagnosticsRequest: DeviceDiagnosticsRequest

View File

@@ -28,8 +28,8 @@ object Constants {
const val DOCUMENT_ID_FOR_UPDATE ="2o71vBKLqdgEKYtFG8Lb" const val DOCUMENT_ID_FOR_UPDATE ="2o71vBKLqdgEKYtFG8Lb"
const val ABHA_APP_PACKAGE = "in.ndhm.phr" const val ABHA_APP_PACKAGE = "in.ndhm.phr"
const val MOLBIO_INTEGRATION = false var MOLBIO_INTEGRATION = true
const val FIREBASE_INTEGRATION = true var FIREBASE_INTEGRATION = false
const val deviceProvisionEmail = "HPOS_provisioner@bigtec.co.in" const val deviceProvisionEmail = "HPOS_provisioner@bigtec.co.in"
const val deviceProvisionPassword = "f2ab0e7f9d69" const val deviceProvisionPassword = "f2ab0e7f9d69"
const val DEVICE_ID_API = "deviceIDAPI" const val DEVICE_ID_API = "deviceIDAPI"
@@ -44,9 +44,9 @@ object Constants {
const val DELAY_BETWEEN_COMMANDS: Long = 1000 const val DELAY_BETWEEN_COMMANDS: Long = 1000
const val TEST_RIGHT_TOTAL_PIXEL = 3694 const val TEST_RIGHT_TOTAL_PIXEL = 3694
const val MAXIMUM_TEST_ALLOWED_BEFORE_REFERENCE = 34 const val MAXIMUM_TEST_ALLOWED_BEFORE_REFERENCE = 34
const val MAXIMUM_TEST_ALLOWED_BEFORE_REFERENCE10MM = 9 const val MAXIMUM_TEST_ALLOWED_BEFORE_REFERENCE10MM = 9
const val MAXIMUM_TEST_ALLOWED_BEFORE_REFERENCE10MM_RANDD= 129
const val RANGE_IN_RESULT_CALCULATIONS = 10 const val RANGE_IN_RESULT_CALCULATIONS = 10

View File

@@ -14,7 +14,9 @@ package com.example.hpostesting.firebase
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.os.Process import android.os.Process
import android.util.Log
import androidx.core.content.edit import androidx.core.content.edit
import com.example.hpostesting.data.constant.Constants
import com.google.firebase.FirebaseApp import com.google.firebase.FirebaseApp
import com.google.firebase.FirebaseOptions import com.google.firebase.FirebaseOptions
import com.google.firebase.firestore.FirebaseFirestore import com.google.firebase.firestore.FirebaseFirestore
@@ -27,20 +29,52 @@ import kotlinx.coroutines.launch
import javax.inject.Inject import javax.inject.Inject
class FirebaseManager @Inject constructor(private val context: Context) { class FirebaseManager @Inject constructor(private val context: Context) {
private var defaultServers = mutableListOf<FirebaseConfig>()
external fun getFirebaseDataMap(): Map<String, Map<String, String>>
private val map: Map<String, Map<String, String>> = getFirebaseDataMap()
companion object {
init {
System.loadLibrary("native-server") //use NDK ro secure somethings
}
}
private val sharedPreferencesServerConfig = 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 val sharedPreferencesDev = context.getSharedPreferences("DevMode",Context.MODE_PRIVATE)
private var currentFirestore: FirebaseFirestore = FirebaseFirestore.getInstance() private var currentFirestore: FirebaseFirestore = FirebaseFirestore.getInstance()
private var currentStorage: FirebaseStorage = FirebaseStorage.getInstance() private var currentStorage: FirebaseStorage = FirebaseStorage.getInstance()
var updateStatus = false private var updateStatus = false
private fun Map<String, String>.toFirebaseConfig(server: String): FirebaseConfig? {
val apiKey = this["apiKey"] ?: return null
val appId = this["appId"] ?: return null
val projectId = this["projectId"] ?: return null
val storageBucket = this["storageBucket"] ?: return null
return FirebaseConfig(
serverName = server,
projectId = projectId,
appId = appId,
apiKey = apiKey,
storageBucket = storageBucket
)
}
fun populate() {
defaultServers.clear()
map.forEach { (server, config) ->
config.toFirebaseConfig(server)?.let {
defaultServers.add(it)
} ?: Log.w("Firebase", "Skipping $server due to missing keys")
}
}
private val defaultServers = listOf(
// i have it in different place look down
firebaseConfigProd, firebaseConfigDev , firebaseConfigQaQc, firebaseConfigPreProd
)
init { init {
populate()
val lastSelectedServer = getLastSelectedServer() val lastSelectedServer = getLastSelectedServer()
switchServer(lastSelectedServer) switchServer(lastSelectedServer)
} }
@@ -61,11 +95,17 @@ class FirebaseManager @Inject constructor(private val context: Context) {
fun getCurrentStorage(): FirebaseStorage = currentStorage fun getCurrentStorage(): FirebaseStorage = currentStorage
fun switchServer(firebaseConfig: FirebaseConfig) { fun switchServer(firebaseConfig: FirebaseConfig) {
saveSelectedServer(firebaseConfig) if (firebaseConfig.serverName =="molbio" ||firebaseConfig.apiKey == "molbio"){
val firebaseApp = getOrInitializeFirebaseApp(firebaseConfig) saveSelectedServer(firebaseConfig)
currentFirestore = FirebaseFirestore.getInstance(firebaseApp) makeMolbioAsBackEnd()
currentStorage = FirebaseStorage.getInstance(firebaseApp) }else {
updateStatus = true makeSMIBackEnd()
saveSelectedServer(firebaseConfig)
val firebaseApp = getOrInitializeFirebaseApp(firebaseConfig)
currentFirestore = FirebaseFirestore.getInstance(firebaseApp)
currentStorage = FirebaseStorage.getInstance(firebaseApp)
updateStatus = true
}
} }
@@ -73,7 +113,6 @@ class FirebaseManager @Inject constructor(private val context: Context) {
val existingApp = FirebaseApp.getApps(context).find { it.name == firebaseConfig.serverName } val existingApp = FirebaseApp.getApps(context).find { it.name == firebaseConfig.serverName }
return if (existingApp != null) { return if (existingApp != null) {
existingApp existingApp
} else { } else {
@@ -125,6 +164,16 @@ class FirebaseManager @Inject constructor(private val context: Context) {
fun getAvailableServers(): List<FirebaseConfig> { fun getAvailableServers(): List<FirebaseConfig> {
return defaultServers return defaultServers
} }
private fun makeMolbioAsBackEnd(){
Constants.FIREBASE_INTEGRATION = false
Constants.MOLBIO_INTEGRATION = true
restartApp()
}
private fun makeSMIBackEnd(){
Constants.FIREBASE_INTEGRATION = true
Constants.MOLBIO_INTEGRATION = false
}
} }
fun restartApp(context: Context) { fun restartApp(context: Context) {
@@ -141,34 +190,51 @@ fun delayedRestart(context: Context) {
} }
} }
private val firebaseConfigDev = FirebaseConfig( //
serverName = "dev", //
apiKey = "AIzaSyAVdNGCev_AFX0qmuZJF6kxzRzXUTeAW5I", //private val firebaseConfigDev = FirebaseConfig(
appId = "1:650071678820:android:a53292637abb7c0d6c6471", // serverName = "dev",
projectId = "hpos-af3cc", // apiKey = "AIzaSyAVdNGCev_AFX0qmuZJF6kxzRzXUTeAW5I",
storageBucket = "hpos-af3cc.appspot.com" // appId = "1:650071678820:android:a53292637abb7c0d6c6471",
) // projectId = "hpos-af3cc",
// storageBucket = "hpos-af3cc.appspot.com"
private val firebaseConfigQaQc = FirebaseConfig( //)
serverName = "qc-qa", //private val firebaseConfigQaQc = FirebaseConfig(
apiKey = "AIzaSyC4d4GhWHr_NMJAeBvnztQ_yQ3Qe9MAnLs", // serverName = "qc-qa",
appId = "1:1004619739289:android:3dbcefb10ea99654e5c808", // apiKey = "AIzaSyC4d4GhWHr_NMJAeBvnztQ_yQ3Qe9MAnLs",
projectId = "hpos-qa", // appId = "1:1004619739289:android:3dbcefb10ea99654e5c808",
storageBucket = "hpos-qa.appspot.com" // projectId = "hpos-qa",
) // storageBucket = "hpos-qa.appspot.com"
//)
private val firebaseConfigProd = FirebaseConfig( //
serverName = "prod", //private val firebaseConfigProd = FirebaseConfig(
apiKey = "AIzaSyA_O0oZGzy3Kiw9fiLQ3OFPME-xQJP88vs", // serverName = "prod",
appId = "1:1012719870714:android:d8d7c962d96f4097de5f43", // apiKey = "AIzaSyA_O0oZGzy3Kiw9fiLQ3OFPME-xQJP88vs",
projectId = "hpos-prod", // appId = "1:1012719870714:android:d8d7c962d96f4097de5f43",
storageBucket = "hpos-prod.appspot.com" // projectId = "hpos-prod",
) // storageBucket = "hpos-prod.appspot.com"
//)
private val firebaseConfigPreProd = FirebaseConfig( //
serverName = "pre prod", //private val firebaseConfigPreProd = FirebaseConfig(
apiKey = "AIzaSyDYySi27LioZGNisP1NfnNU5inJX_0FT38", // serverName = "pre prod",
appId = "1:121176529204:android:e6841fdac57bdc95bbed61", // apiKey = "AIzaSyDYySi27LioZGNisP1NfnNU5inJX_0FT38",
projectId = "hpos-preprod", // appId = "1:121176529204:android:e6841fdac57bdc95bbed61",
storageBucket = "hpos-preprod.appspot.com" // projectId = "hpos-preprod",
) // storageBucket = "hpos-preprod.appspot.com"
//)
//
//private val firebaseConfigSmiRandD= FirebaseConfig(
// serverName = "SMI R&D",
// apiKey = "AIzaSyAVxpilbB804iRCCpMBDjjmdZhQguctGHM",
// appId = "1:327771387914:android:a3b4ed2e42ea6e38dc87f3",
// projectId = "hposrandd",
// storageBucket = "hposrandd.firebasestorage.app"
//)
//
//private val configMolbio = FirebaseConfig(
// serverName = "molbio",
// apiKey = "molbio",
// appId = "molbio",
// projectId = "molbio",
// storageBucket = "molbio"
//)

View File

@@ -22,10 +22,12 @@ import android.util.Log
import android.widget.EditText import android.widget.EditText
import android.widget.Toast import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContentProviderCompat.requireContext
import com.example.hpostesting.data.constant.DataHolder import com.example.hpostesting.data.constant.DataHolder
import com.example.hpostesting.data.constant.Constants import com.example.hpostesting.data.constant.Constants
import com.example.hpostesting.data.constant.LanguageManager import com.example.hpostesting.data.constant.LanguageManager
import com.example.hpostesting.data.model.test.TestType import com.example.hpostesting.data.model.test.TestType
import com.example.hpostesting.firebase.FirebaseManager
import com.example.hpostesting.presentation.main_base.DashboardActivity import com.example.hpostesting.presentation.main_base.DashboardActivity
import com.example.hpostesting.presentation.hb_test.HBTestActivity import com.example.hpostesting.presentation.hb_test.HBTestActivity
import com.example.hpostesting.presentation.trueheme_test.TrueHemeTestActivity import com.example.hpostesting.presentation.trueheme_test.TrueHemeTestActivity
@@ -52,7 +54,7 @@ class KitScanActivity : AppCompatActivity(), IDcsSdkApiDelegate {
private var fromWhere = "Home" private var fromWhere = "Home"
private val TAG = "KitScanActivity" private val TAG = "KitScanActivity"
private lateinit var binding: ActivityKitScanBinding private lateinit var binding: ActivityKitScanBinding
private lateinit var firebaseManager: FirebaseManager
private lateinit var sharedPreference: SharedPreferences private lateinit var sharedPreference: SharedPreferences
var sdkHandler: SDKHandler? = null var sdkHandler: SDKHandler? = null
@@ -71,21 +73,21 @@ class KitScanActivity : AppCompatActivity(), IDcsSdkApiDelegate {
} }
private fun processScannedData(contents: String) {//edited auto selection of cuvette size private fun processScannedData(contents: String) {//edited auto selection of cuvette size
if(contents.contains("SMI/SC/")){ if (contents.contains("SMI/SC/")) {
with(sharedPreference.edit()) { with(sharedPreference.edit()) {
putString(Constants.CUVETTE_SIZE, "2mm") putString(Constants.CUVETTE_SIZE, "2mm")
apply() apply()
} }
Toast.makeText(this, "Selected cuvette size: 2mm", Toast.LENGTH_SHORT).show() Toast.makeText(this, "Selected cuvette size: 2mm", Toast.LENGTH_SHORT).show()
binding.nameEditText.setText(contents) binding.nameEditText.setText(contents)
}else if(contents.contains("SMI/SC-2-D10/")){ } else if (contents.contains("SMI/SC-2-D10/")) {
with(sharedPreference.edit()) { with(sharedPreference.edit()) {
putString(Constants.CUVETTE_SIZE, "10mm") putString(Constants.CUVETTE_SIZE, "10mm")
apply() apply()
} }
Toast.makeText(this, "Selected cuvette size: 10mm", Toast.LENGTH_SHORT).show() Toast.makeText(this, "Selected cuvette size: 10mm", Toast.LENGTH_SHORT).show()
binding.nameEditText.setText(contents) binding.nameEditText.setText(contents)
}else{ } else {
Toast.makeText(this, R.string.invalid_kit, Toast.LENGTH_LONG).show() Toast.makeText(this, R.string.invalid_kit, Toast.LENGTH_LONG).show()
} }
} }
@@ -127,30 +129,42 @@ class KitScanActivity : AppCompatActivity(), IDcsSdkApiDelegate {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
firebaseManager = FirebaseManager(this)
val currentServer = firebaseManager.getLastSelectedServer().serverName
binding = ActivityKitScanBinding.inflate(layoutInflater) binding = ActivityKitScanBinding.inflate(layoutInflater)
sharedPreference = this.getSharedPreferences("HEMOCUBE", Context.MODE_PRIVATE) sharedPreference = this.getSharedPreferences("HEMOCUBE", Context.MODE_PRIVATE)
setContentView(binding.root) setContentView(binding.root)
binding.toolbar.title = "Kit Serial Number" binding.toolbar.title = "Kit Serial Number"
fromWhere = intent.getStringExtra("fromWhere").toString() fromWhere = intent.getStringExtra("fromWhere").toString()
val maxTest = if(sharedPreference.getString(Constants.CUVETTE_SIZE, "10mm").toString() == "2mm"){
Constants.MAXIMUM_TEST_ALLOWED_BEFORE_REFERENCE val maxTest = if (currentServer == "SMI R&D"){
}else{ Constants.MAXIMUM_TEST_ALLOWED_BEFORE_REFERENCE10MM_RANDD
Constants.MAXIMUM_TEST_ALLOWED_BEFORE_REFERENCE10MM
Log.d(TAG, "onCreate RandD mode")
}else {
if (sharedPreference.getString(Constants.CUVETTE_SIZE, "10mm").toString() == "2mm") {
Constants.MAXIMUM_TEST_ALLOWED_BEFORE_REFERENCE
} else {
Constants.MAXIMUM_TEST_ALLOWED_BEFORE_REFERENCE10MM
}
} }
val time = timeDifference(sharedPreference.getString(Constants.KIT_TIME, "").toString()) val time = timeDifference(sharedPreference.getString(Constants.KIT_TIME, "").toString())
val kitNum = sharedPreference.getString(Constants.KIT_NUMBER, "").toString() val kitNum = sharedPreference.getString(Constants.KIT_NUMBER, "").toString()
// Toast.makeText(this@KitScanActivity,"Test MAx time"+ time+"-Test count-"+kitNum,Toast.LENGTH_SHORT).show() // Toast.makeText(this@KitScanActivity,"Test MAx time"+ time+"-Test count-"+kitNum,Toast.LENGTH_SHORT).show()
// if (sharedPreference.getString(Constants.USER_ID, "").toString() == "ADMIN") { // if (sharedPreference.getString(Constants.USER_ID, "").toString() == "ADMIN") {
// moveToNext() // moveToNext()
// }else{ // }else{
if (sharedPreference.getString(Constants.USER_ID, "").toString() == "ADMIN") { if (sharedPreference.getString(Constants.USER_ID, "").toString() == "ADMIN") {
moveToNext() moveToNext()
}else{ } else {
if (DataHolder.sampleReadCounter <= maxTest && kitNum != "" && time < Constants.MAX_KIT_TIME) { if (DataHolder.sampleReadCounter <= maxTest && kitNum != "" && time < Constants.MAX_KIT_TIME) {
moveToNext() moveToNext()
} else { } else {
Toast.makeText(this@KitScanActivity, "Limit Reached, Use New KIT for testing", Toast.LENGTH_SHORT).show() Toast.makeText(
this@KitScanActivity,
"Limit Reached, Use New KIT for testing",
Toast.LENGTH_SHORT
).show()
DataHolder.sampleReadCounter = 0 DataHolder.sampleReadCounter = 0
DataHolder.kitSerial = "" DataHolder.kitSerial = ""
@@ -212,13 +226,13 @@ class KitScanActivity : AppCompatActivity(), IDcsSdkApiDelegate {
binding.btnGo.setOnClickListener { binding.btnGo.setOnClickListener {
val serialNumber = binding.nameEditText.text.toString().trim() val serialNumber = binding.nameEditText.text.toString().trim()
if (serialNumber.isNotEmpty() && isSerialValid(serialNumber)) { if (serialNumber.isNotEmpty() && isSerialValid(serialNumber)) {
if(serialNumber.contains("SMI/SC/")){ if (serialNumber.contains("SMI/SC/")) {
with(sharedPreference.edit()) { with(sharedPreference.edit()) {
putString(Constants.CUVETTE_SIZE, "2mm") putString(Constants.CUVETTE_SIZE, "2mm")
apply() apply()
} }
Toast.makeText(this, "Selected cuvette size: 2mm", Toast.LENGTH_SHORT).show() Toast.makeText(this, "Selected cuvette size: 2mm", Toast.LENGTH_SHORT).show()
}else if(serialNumber.contains("SMI/SC-2-D10/")){ } else if (serialNumber.contains("SMI/SC-2-D10/")) {
with(sharedPreference.edit()) { with(sharedPreference.edit()) {
putString(Constants.CUVETTE_SIZE, "10mm") putString(Constants.CUVETTE_SIZE, "10mm")
apply() apply()
@@ -301,7 +315,8 @@ class KitScanActivity : AppCompatActivity(), IDcsSdkApiDelegate {
if (mScannerInfoList.isNotEmpty()) { if (mScannerInfoList.isNotEmpty()) {
sdkHandler!!.dcssdkEstablishCommunicationSession(mScannerInfoList[0].scannerID) sdkHandler!!.dcssdkEstablishCommunicationSession(mScannerInfoList[0].scannerID)
} else { } else {
Toast.makeText(this,"Scanner Is not available In this device", Toast.LENGTH_LONG).show() Toast.makeText(this, "Scanner Is not available In this device", Toast.LENGTH_LONG)
.show()
} }
} }
@@ -339,7 +354,7 @@ class KitScanActivity : AppCompatActivity(), IDcsSdkApiDelegate {
runOnUiThread { runOnUiThread {
val editableResult: Editable = Editable.Factory.getInstance().newEditable(result) val editableResult: Editable = Editable.Factory.getInstance().newEditable(result)
processScannedData(result) processScannedData(result)
// binding.nameEditText.text = editableResult // binding.nameEditText.text = editableResult
} }
} }
@@ -352,27 +367,29 @@ class KitScanActivity : AppCompatActivity(), IDcsSdkApiDelegate {
private fun isSerialValid(s: String): Boolean { private fun isSerialValid(s: String): Boolean {
if (s.contains("SMI/SC/")) { if (s.contains("SMI/SC/")) {
if(s.length != 17){ if (s.length != 17) {
binding.nameEditText.error = "Invalid Kit Serial Number, correct example SMI/SC/000/00/000" binding.nameEditText.error =
"Invalid Kit Serial Number, correct example SMI/SC/000/00/000"
return false return false
} }
}else if(s.contains("SMI/SC-2-D10/")){ } else if (s.contains("SMI/SC-2-D10/")) {
if(s.length != 27){ if (s.length != 27) {
binding.nameEditText.error = "Invalid Kit Serial Number, correct example SMI/SC-2-D10/000000/000/000" binding.nameEditText.error =
"Invalid Kit Serial Number, correct example SMI/SC-2-D10/000000/000/000"
return false return false
} }
}else{ } else {
return false return false
} }
return true return true
} }
private fun moveToNext() { private fun moveToNext() {
if(fromWhere == "Main"){ if (fromWhere == "Main") {
if(DataHolder.selectedTestType == TestType.HB_EST){ if (DataHolder.selectedTestType == TestType.HB_EST) {
val i = Intent(applicationContext, HBTestActivity::class.java) val i = Intent(applicationContext, HBTestActivity::class.java)
startActivity(i) startActivity(i)
}else{ } else {
DataHolder.deviceType.observe(this) { deviceType -> DataHolder.deviceType.observe(this) { deviceType ->
when (deviceType) { when (deviceType) {
Constants.DEVICE_TYPE_HEMOCUBE -> { Constants.DEVICE_TYPE_HEMOCUBE -> {
@@ -425,6 +442,7 @@ class KitScanActivity : AppCompatActivity(), IDcsSdkApiDelegate {
true true
} }
} }
private fun timeDifference(createdAt: String): Long { private fun timeDifference(createdAt: String): Long {
val currentTime = Calendar.getInstance().time val currentTime = Calendar.getInstance().time
val formatter = SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault()) val formatter = SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault())

View File

@@ -14,6 +14,7 @@
package com.example.hpostesting.presentation package com.example.hpostesting.presentation
import android.Manifest import android.Manifest
import android.annotation.SuppressLint
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.pm.PackageManager import android.content.pm.PackageManager
@@ -33,6 +34,7 @@ import com.example.hpostesting.util.MyUtils
import `in`.sminnovations.hpostesting.R import `in`.sminnovations.hpostesting.R
import `in`.sminnovations.hpostesting.databinding.ActivitySplashBinding import `in`.sminnovations.hpostesting.databinding.ActivitySplashBinding
@SuppressLint("CustomSplashScreen")
class SplashActivity : AppCompatActivity() { class SplashActivity : AppCompatActivity() {
private lateinit var binding: ActivitySplashBinding private lateinit var binding: ActivitySplashBinding

View File

@@ -37,6 +37,7 @@ import com.example.hpostesting.presentation.main_base.DashboardActivity
import com.google.firebase.crashlytics.ktx.crashlytics import com.google.firebase.crashlytics.ktx.crashlytics
import com.google.firebase.ktx.Firebase import com.google.firebase.ktx.Firebase
import `in`.sminnovations.hpostesting.databinding.FragmentDeviceProvisionBinding import `in`.sminnovations.hpostesting.databinding.FragmentDeviceProvisionBinding
import okio.ByteString.Companion.decodeBase64
import java.io.File import java.io.File
class DeviceProvisionFragment : Fragment() { class DeviceProvisionFragment : Fragment() {
@@ -147,17 +148,18 @@ class DeviceProvisionFragment : Fragment() {
DashboardActivity::class.java DashboardActivity::class.java
) )
) )
} else { } else {
Toast.makeText( Toast.makeText(
activity, activity,
"An error in device provision: ${response.data.message}", "An error in device provision: ${response.data.message}",
Toast.LENGTH_LONG Toast.LENGTH_LONG
).show() ).show()
Log.d("Error in hpos network", "observeViewModel:${response.data.message} ")
binding.btnSubmit.visibility = View.VISIBLE binding.btnSubmit.visibility = View.VISIBLE
} }
} }
is Result.Error -> { is Result.Error -> {
response.exception.let { message -> response.exception.let { message ->
Toast.makeText( Toast.makeText(
@@ -165,7 +167,9 @@ class DeviceProvisionFragment : Fragment() {
"An error occurred in device provision: $message", "An error occurred in device provision: $message",
Toast.LENGTH_LONG Toast.LENGTH_LONG
).show() ).show()
Log.d("Error in hpos network 2", "observeViewModel:$message} ")
} }
Log.d("Error in hpos network 3", "observeViewModel: $response} ")
} }
is Result.Loading -> { is Result.Loading -> {

View File

@@ -25,6 +25,7 @@ import android.view.ViewGroup
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels import androidx.fragment.app.activityViewModels
import com.example.hpostesting.data.constant.Constants import com.example.hpostesting.data.constant.Constants
import com.example.hpostesting.presentation.reportgen.ReportActivity
import com.example.hpostesting.presentation.autodac.AutoDacActivity import com.example.hpostesting.presentation.autodac.AutoDacActivity
import com.example.hpostesting.presentation.buffercheck.HemocubeBufferCheckActivity import com.example.hpostesting.presentation.buffercheck.HemocubeBufferCheckActivity
import com.example.hpostesting.presentation.calibration.CalibrationActivity import com.example.hpostesting.presentation.calibration.CalibrationActivity
@@ -89,6 +90,7 @@ class PanelFragment : Fragment() {
binding.btnDeviceInfo.visibility = View.VISIBLE binding.btnDeviceInfo.visibility = View.VISIBLE
binding.btnResetPassword.visibility = View.VISIBLE binding.btnResetPassword.visibility = View.VISIBLE
binding.btnUpdateValues.visibility = View.VISIBLE binding.btnUpdateValues.visibility = View.VISIBLE
binding.btnPrintReport.visibility = View.VISIBLE
}else{ }else{
binding.btnResetPassword.visibility = View.GONE binding.btnResetPassword.visibility = View.GONE
binding.btnDeviceProvision.visibility = View.GONE binding.btnDeviceProvision.visibility = View.GONE
@@ -102,6 +104,7 @@ class PanelFragment : Fragment() {
binding.btnSubmit.visibility = View.GONE binding.btnSubmit.visibility = View.GONE
binding.btnDeviceInfo.visibility = View.VISIBLE binding.btnDeviceInfo.visibility = View.VISIBLE
binding.btnUpdateValues.visibility = View.GONE binding.btnUpdateValues.visibility = View.GONE
binding.btnPrintReport.visibility = View.VISIBLE
} }
binding.btnResetPassword.setOnClickListener{ binding.btnResetPassword.setOnClickListener{
@@ -136,12 +139,17 @@ class PanelFragment : Fragment() {
startActivity(Intent(requireContext(), DeviceActivity::class.java)) startActivity(Intent(requireContext(), DeviceActivity::class.java))
} }
binding.btnPrintReport.setOnClickListener{
startActivity(Intent(requireContext(), ReportActivity::class.java))
}
binding.btnFirefox.setOnClickListener { binding.btnFirefox.setOnClickListener {
val intent = Intent(Intent.ACTION_VIEW) val intent = Intent(Intent.ACTION_VIEW)
intent.component = ComponentName("org.mozilla.firefox", "org.mozilla.gecko.BrowserApp") intent.component = ComponentName("org.mozilla.firefox", "org.mozilla.gecko.BrowserApp")
startActivity(intent) startActivity(intent)
} }
binding.btnFiles.setOnClickListener { binding.btnFiles.setOnClickListener {
val intent = Intent(Intent.ACTION_GET_CONTENT) val intent = Intent(Intent.ACTION_GET_CONTENT)
intent.type = "file/*" intent.type = "file/*"

View File

@@ -17,6 +17,9 @@ import android.content.Context
import android.content.SharedPreferences import android.content.SharedPreferences
import android.content.pm.PackageManager import android.content.pm.PackageManager
import android.os.Bundle import android.os.Bundle
import android.text.InputFilter
import android.text.InputType
import android.view.Gravity
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
@@ -24,15 +27,14 @@ import android.widget.AdapterView
import android.widget.ArrayAdapter import android.widget.ArrayAdapter
import android.widget.Toast import android.widget.Toast
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.core.content.edit
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.preference.EditTextPreference
import androidx.preference.ListPreference import androidx.preference.ListPreference
import androidx.preference.Preference import androidx.preference.Preference
import androidx.preference.PreferenceFragmentCompat import androidx.preference.PreferenceFragmentCompat
import androidx.preference.PreferenceManager
import androidx.preference.SwitchPreferenceCompat import androidx.preference.SwitchPreferenceCompat
import com.example.hpostesting.data.constant.Constants import com.example.hpostesting.data.constant.Constants
import com.example.hpostesting.data.constant.Constants.FIREBASE_INTEGRATION
import com.example.hpostesting.data.constant.Constants.MOLBIO_INTEGRATION
import com.example.hpostesting.data.constant.DataHolder import com.example.hpostesting.data.constant.DataHolder
import com.example.hpostesting.data.constant.LanguageManager import com.example.hpostesting.data.constant.LanguageManager
import com.example.hpostesting.data.repository.DatabaseRepository import com.example.hpostesting.data.repository.DatabaseRepository
@@ -44,7 +46,12 @@ import `in`.sminnovations.hpostesting.databinding.FragmentSlideshowBinding
private var isLanguageChanged = false private var isLanguageChanged = false
private val DEFAULT_DISCLAIMER = """
DISCLAIMER: This report is generated by HemoCube automated system.
Results should be interpreted by qualified medical professionals.
This report is for diagnostic purposes only and should not be used as the sole basis for medical decisions.
Please consult with your healthcare provider for proper interpretation and treatment recommendations.
""".trimIndent()
class SlideshowFragment : Fragment() { class SlideshowFragment : Fragment() {
private var selectedItem = "10mm" private var selectedItem = "10mm"
private val values = arrayOf("10mm", "2mm") private val values = arrayOf("10mm", "2mm")
@@ -61,7 +68,6 @@ class SlideshowFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
binding.nameEditText.setText(sharedPreferences.getString(Constants.LABNAME, "")) binding.nameEditText.setText(sharedPreferences.getString(Constants.LABNAME, ""))
selectedItem = sharedPreferences.getString(Constants.CUVETTE_SIZE, "10mm").toString() selectedItem = sharedPreferences.getString(Constants.CUVETTE_SIZE, "10mm").toString()
binding.btnGo.setOnClickListener { binding.btnGo.setOnClickListener {
@@ -123,6 +129,8 @@ class PrefsFragment : PreferenceFragmentCompat() {
private lateinit var serverPreference: Preference private lateinit var serverPreference: Preference
private lateinit var currentServerPreference: Preference private lateinit var currentServerPreference: Preference
lateinit var firebaseManager: FirebaseManager lateinit var firebaseManager: FirebaseManager
var autoSmbtValue = false
var reportDisclaimerText = DEFAULT_DISCLAIMER
private var topDevMode = false private var topDevMode = false
lateinit var databaseRepository: DatabaseRepository lateinit var databaseRepository: DatabaseRepository
@@ -133,10 +141,12 @@ class PrefsFragment : PreferenceFragmentCompat() {
topDevMode = firebaseManager.getDevMode() topDevMode = firebaseManager.getDevMode()
val sharedPreference = val sharedPreference =
requireContext().getSharedPreferences("HEMOCUBE", Context.MODE_PRIVATE) requireContext().getSharedPreferences("HEMOCUBE", Context.MODE_PRIVATE)
autoSmbtValue = sharedPreference.getBoolean("AutSmbt", false)
reportDisclaimerText = sharedPreference.getString("report_dis_txt", DEFAULT_DISCLAIMER).toString()
val preferenceScreen = preferenceManager.createPreferenceScreen(requireContext()) val preferenceScreen = preferenceManager.createPreferenceScreen(requireContext())
val currentServer = firebaseManager.getLastSelectedServer().serverName val currentServer = firebaseManager.getLastSelectedServer().serverName
val arWeAdmin =sharedPreference.getString(Constants.USER_ID, "").toString() == "ADMIN"
val MAX_CHARS = 10_000
// Language Preference // Language Preference
val languagePreference = ListPreference(requireContext()).apply { val languagePreference = ListPreference(requireContext()).apply {
key = "language_preference" key = "language_preference"
@@ -203,43 +213,115 @@ class PrefsFragment : PreferenceFragmentCompat() {
} }
} }
val shouldEnableCurrentServerVersion = FIREBASE_INTEGRATION && !MOLBIO_INTEGRATION val disclaimerPreference = EditTextPreference(requireContext()).apply {
key = "Report_disclaimer"
title = "Report disclaimer Editor"
icon = ContextCompat.getDrawable(requireContext(), R.drawable.ic_add)
summaryProvider = Preference.SummaryProvider<EditTextPreference> { preference ->
val value = preference.text
if (value.isNullOrBlank()) {
"Current disclaimer: (not set)"
} else {
"Current disclaimer: $value"
}
}
setOnBindEditTextListener { edit ->
edit.inputType = InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_FLAG_MULTI_LINE
edit.minLines = 4
edit.maxLines = 10
edit.gravity = Gravity.TOP or Gravity.START
edit.filters = arrayOf(InputFilter.LengthFilter(MAX_CHARS))
}
setOnPreferenceChangeListener { _, newValue ->
val text = (newValue as? String)?.trim().orEmpty()
if (text.length > MAX_CHARS) {
Toast.makeText(
requireContext(),
"Text too long (${text.length}/$MAX_CHARS).",
Toast.LENGTH_SHORT
).show()
return@setOnPreferenceChangeListener false
}else{
sharedPreference.edit().putString("report_dis_txt", text).apply()
}
true
}
}
val resetDisclaimerPreference = Preference(requireContext()).apply {
title = "Reset to default Disclaimer"
summary = "Replace the current text with the apps default disclaimer"
icon = ContextCompat.getDrawable(requireContext(), R.drawable.baseline_restore_24)
setOnPreferenceClickListener {
sharedPreference.edit().putString("report_dis_txt", DEFAULT_DISCLAIMER).apply()
disclaimerPreference.text = DEFAULT_DISCLAIMER
Toast.makeText(requireContext(), "Disclaimer reset to default", Toast.LENGTH_SHORT).show()
true
}
}
val autosmbt = SwitchPreferenceCompat(requireContext()).apply {
title = "Auto submit"
summary = "Enable or disable Auto submit"
icon =
ContextCompat.getDrawable(requireContext(), R.drawable.baseline_motion_auto_24)
key = "auto_submit_key" // Unique key for SharedPreferences
// Load toggle state from SharedPreferences
isChecked = autoSmbtValue
setOnPreferenceChangeListener { _, newValue ->
val isEnabled = newValue as Boolean
sharedPreference.edit {
putBoolean("AutSmbt",isEnabled)
}
true
}
}
val appVersionPreference = Preference(requireContext()).apply { val appVersionPreference = Preference(requireContext()).apply {
title = "App Version" title = "App Version"
summary = summary =
getAppVersion(requireContext()) + " [ " + getAppEnvironment(requireContext()) + "->" + if (shouldEnableCurrentServerVersion) currentServer else "molbio"+ "]" getAppVersion(requireContext()) + " [ " + getAppEnvironment(requireContext()) + "->" + currentServer + "]"
icon = ContextCompat.getDrawable(requireContext(), R.drawable.baseline_info_24) icon = ContextCompat.getDrawable(requireContext(), R.drawable.baseline_info_24)
setOnPreferenceClickListener { setOnPreferenceClickListener {
tapManager.registerTap { tapManager.registerTap {
firebaseManager.setDevModeOn(true) if (arWeAdmin) {
firebaseManager.setDevModeOn(true)
val devConfig = servers.find { it.serverName == "dev" }
if (firebaseManager.getDevMode() && currentServer != "dev") { if (firebaseManager.getDevMode() && currentServer != "dev") {
Toast.makeText(requireContext(), "Now you are a Developer", Toast.LENGTH_SHORT).show() Toast.makeText(requireContext(), "Now you are a Developer", Toast.LENGTH_SHORT).show()
Toast.makeText(requireContext(), "Setting Up developer Environmnet", Toast.LENGTH_SHORT).show() Toast.makeText(requireContext(), "Setting Up developer Environment", Toast.LENGTH_SHORT).show()
switchFirebaseServer(devConfig!!)
} }
if (preferenceScreen.findPreference<SwitchPreferenceCompat>("developer_options") == null) { if (preferenceScreen.findPreference<SwitchPreferenceCompat>("developer_options") == null) {
preferenceScreen.addPreference(developerToggle) preferenceScreen.addPreference(developerToggle)
preferenceScreen.addPreference(autosmbt)
preferenceScreen.addPreference(disclaimerPreference)
preferenceScreen.addPreference(resetDisclaimerPreference)
} }
if (preferenceScreen.findPreference<ListPreference>("server_preference") == null) { if (preferenceScreen.findPreference<ListPreference>("server_preference") == null) {
preferenceScreen.addPreference(serverSelectorPreference) preferenceScreen.addPreference(serverSelectorPreference)
} }
developerToggle.isChecked = true developerToggle.isChecked = true
updateServerPreference(currentServer) updateServerPreference(currentServer)
}else{
Toast.makeText(requireContext(), "Works Only On Admin Mode", Toast.LENGTH_SHORT).show()
}
} }
true true
} }
} }
preferenceScreen.addPreference(appVersionPreference) preferenceScreen.addPreference(appVersionPreference)
if (firebaseManager.getDevMode()) { if (firebaseManager.getDevMode() && arWeAdmin) {
developerToggle.isChecked = true // Ensure it's checked developerToggle.isChecked = true // Ensure it's checked
preferenceScreen.addPreference(developerToggle) preferenceScreen.addPreference(developerToggle)
preferenceScreen.addPreference(serverSelectorPreference) preferenceScreen.addPreference(serverSelectorPreference)
preferenceScreen.addPreference(autosmbt)
preferenceScreen.addPreference(resetDisclaimerPreference)
preferenceScreen.addPreference(disclaimerPreference)
} }
setPreferenceScreen(preferenceScreen) setPreferenceScreen(preferenceScreen)
@@ -249,6 +331,12 @@ class PrefsFragment : PreferenceFragmentCompat() {
} }
} }
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
listView.setPadding(0, 0, 0, 300) // Add bottom padding
listView.clipToPadding = false
}
private fun updateServerPreference(newServer: String) { private fun updateServerPreference(newServer: String) {
(findPreference<CustomDialogPreference>("server_preference"))?.let { pref -> (findPreference<CustomDialogPreference>("server_preference"))?.let { pref ->

View File

@@ -0,0 +1,21 @@
package com.example.hpostesting.presentation.reportgen
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import com.example.hpostesting.presentation.reportgen.ui.report.ReportFragment
import dagger.hilt.android.AndroidEntryPoint
import `in`.sminnovations.hpostesting.R
@AndroidEntryPoint
class ReportActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.report_gen)
if (savedInstanceState == null) {
supportFragmentManager.beginTransaction()
.replace(R.id.container, ReportFragment()) //Direct constructor
.commitNow()
}
}
}

View File

@@ -0,0 +1,70 @@
package com.example.hpostesting.presentation.reportgen.ui.report
import android.graphics.Color
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.cardview.widget.CardView
import androidx.recyclerview.widget.RecyclerView
import com.example.hpostesting.data.dao.HemoCubeBufferDao
import com.example.hpostesting.data.model.patient.BufferCheckData
import com.example.hpostesting.data.model.patient.HemoCubeTestData
import `in`.sminnovations.hpostesting.R
class BufferAdapter(
private var dataList: List<HemoCubeTestData>,
private val onItemClick: (HemoCubeTestData) -> Unit
) : RecyclerView.Adapter<BufferAdapter.ViewHolder>() {
private var selectedItems: List<HemoCubeTestData> = emptyList()
inner class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {
val cardView: CardView = view.findViewById(R.id.cardView)
val sampleId: TextView = view.findViewById(R.id.sampleId)
val testTime: TextView = view.findViewById(R.id.testTime)
val result: TextView = view.findViewById(R.id.result)
val selectionIndicator: View = view.findViewById(R.id.selectionIndicator)
init {
cardView.setOnClickListener {
if (adapterPosition != RecyclerView.NO_POSITION) {
onItemClick(dataList[adapterPosition])
}
}
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ViewHolder(
LayoutInflater.from(parent.context).inflate(R.layout.item_buffer_card, parent, false)
)
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val item = dataList[position]
val isSelected = selectedItems.contains(item)
holder.sampleId.text = "Sample ID: ${item._id}"
holder.testTime.text = "Time: ${item.testTime}"
holder.result.text = "Result: ${item.classificationResult}"
// Update selection appearance
if (isSelected) {
holder.cardView.setCardBackgroundColor(Color.parseColor("#E3F2FD"))
holder.selectionIndicator.visibility = View.VISIBLE
} else {
holder.cardView.setCardBackgroundColor(Color.WHITE)
holder.selectionIndicator.visibility = View.GONE
}
}
override fun getItemCount() = dataList.size
fun updateList(newList: List<HemoCubeTestData>) {
dataList = newList
notifyDataSetChanged()
}
fun updateSelectedItems(newSelectedItems: List<HemoCubeTestData>) {
selectedItems = newSelectedItems
notifyDataSetChanged()
}
}

View File

@@ -0,0 +1,326 @@
package com.example.hpostesting.presentation.reportgen.ui.report
import android.content.ActivityNotFoundException
import android.content.Context
import android.content.Intent
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Paint
import android.graphics.Typeface
import android.graphics.pdf.PdfDocument
import android.util.Log
import android.widget.Toast
import androidx.core.content.ContextCompat
import androidx.core.content.FileProvider
import com.example.hpostesting.data.model.patient.HemoCubeTestData
import `in`.sminnovations.hpostesting.R
import java.io.File
import java.io.FileOutputStream
import java.text.SimpleDateFormat
import java.util.Date
import java.util.Locale
object ReportGen {
private const val PAGE_WIDTH = 595 // A4 width in points
private const val PAGE_HEIGHT = 842 // A4 height in points
private const val MARGIN_LEFT = 50f
private const val MARGIN_RIGHT = 50f
private const val MARGIN_TOP = 80f
private const val MARGIN_BOTTOM = 80f
fun generate(context: Context, dataList: List<HemoCubeTestData>,disclaimerText: String) {
if (dataList.isEmpty()) {
Toast.makeText(context, "No items selected for report generation", Toast.LENGTH_SHORT).show()
return
}
Log.d("ReportGen", "Generating report for ${dataList.size} items")
val message = if (dataList.size == 1) {
"Report for kit ${dataList.first()._id} started."
} else {
"Report for ${dataList.size} kits started."
}
Toast.makeText(context, message, Toast.LENGTH_SHORT).show()
val pdfFile = createProfessionalPdfReport(context, dataList, disclaimerText)
previewPdf(context, pdfFile)
}
private fun createProfessionalPdfReport(context: Context, dataList: List<HemoCubeTestData>,disclaimerText : String): File {
val timestamp = SimpleDateFormat("yyyyMMdd_HHmmss", Locale.getDefault()).format(Date())
val file = File(context.getExternalFilesDir(null), "HemoCube_Report_$timestamp.pdf")
val pdfDocument = PdfDocument()
// Paint styles
val headerPaint = Paint().apply {
typeface = Typeface.create(Typeface.DEFAULT_BOLD, Typeface.BOLD)
textSize = 20f
color = Color.rgb(0, 102, 153) // Professional blue
}
val titlePaint = Paint().apply {
typeface = Typeface.create(Typeface.DEFAULT_BOLD, Typeface.BOLD)
textSize = 18f
color = Color.BLACK
}
val sectionHeaderPaint = Paint().apply {
typeface = Typeface.create(Typeface.DEFAULT_BOLD, Typeface.BOLD)
textSize = 14f
color = Color.rgb(0, 102, 153)
}
val contentPaint = Paint().apply {
textSize = 11f
color = Color.DKGRAY
}
val boldContentPaint = Paint().apply {
typeface = Typeface.create(Typeface.DEFAULT_BOLD, Typeface.BOLD)
textSize = 11f
color = Color.BLACK
}
val disclaimerPaint = Paint().apply {
textSize = 9f
color = Color.GRAY
typeface = Typeface.create(Typeface.DEFAULT, Typeface.ITALIC)
}
// Create one page per test data
dataList.forEachIndexed { index, testData ->
val pageInfo = PdfDocument.PageInfo.Builder(PAGE_WIDTH, PAGE_HEIGHT, index + 1).create()
val page = pdfDocument.startPage(pageInfo)
val canvas = page.canvas
drawPage(canvas, testData, index + 1, dataList.size,
headerPaint, titlePaint, sectionHeaderPaint,
contentPaint, boldContentPaint, disclaimerPaint,disclaimerText,context)
pdfDocument.finishPage(page)
}
pdfDocument.writeTo(FileOutputStream(file))
pdfDocument.close()
return file
}
private fun drawPage(
canvas: Canvas,
testData: HemoCubeTestData,
pageNumber: Int,
totalPages: Int,
headerPaint: Paint,
titlePaint: Paint,
sectionHeaderPaint: Paint,
contentPaint: Paint,
boldContentPaint: Paint,
disclaimerPaint: Paint,
disclaimerText: String,
context: Context
) {
var currentY = MARGIN_TOP
// Header with logo placeholder and title
drawHeader(canvas, headerPaint, titlePaint, currentY, context)
currentY += 100f
// Sample Information Section
currentY = drawSection(canvas, "SAMPLE INFORMATION", sectionHeaderPaint, currentY)
currentY = drawKeyValuePair(canvas, "Sample ID:", testData.sampleid.toString(), boldContentPaint, contentPaint, currentY)
currentY = drawKeyValuePair(canvas, "Blood Group:", testData.bloodGroup, boldContentPaint, contentPaint, currentY)
currentY += 15f
// Test Information Section
currentY = drawSection(canvas, "TEST INFORMATION", sectionHeaderPaint, currentY)
currentY = drawKeyValuePair(canvas, "Test ID:", testData._id, boldContentPaint, contentPaint, currentY)
currentY = drawKeyValuePair(canvas, "Test Type:", testData.testType ?: "TrueHeme", boldContentPaint, contentPaint, currentY)
currentY = drawKeyValuePair(canvas, "Test Time:", testData.testTime ?: "N/A", boldContentPaint, contentPaint, currentY)
currentY = drawKeyValuePair(canvas, "Incubation Time:", testData.incubationTime, boldContentPaint, contentPaint, currentY)
currentY = drawKeyValuePair(canvas, "Kit Serial:", testData.kitSerial, boldContentPaint, contentPaint, currentY)
currentY = drawKeyValuePair(canvas, "Device Serial:", testData.deviceSerialNumber, boldContentPaint, contentPaint, currentY)
currentY += 15f
// Test Results Section
currentY = drawSection(canvas, "TEST RESULTS", sectionHeaderPaint, currentY)
currentY = drawKeyValuePair(canvas, "Classification Result:", testData.classificationResult, boldContentPaint, contentPaint, currentY)
currentY = drawKeyValuePair(canvas, "Device Ratio:", testData.deviceRatio?.toString() ?: "N/A", boldContentPaint, contentPaint, currentY)
currentY = drawKeyValuePair(canvas, "Calculated Ratio:", testData.calculatedRatio?.toString() ?: "N/A", boldContentPaint, contentPaint, currentY)
currentY = drawKeyValuePair(canvas, "Slope Ratio:", testData.slopeRatio?.toString() ?: "N/A", boldContentPaint, contentPaint, currentY)
currentY += 15f
// LED Measurements Section
currentY = drawSection(canvas, "LED MEASUREMENTS", sectionHeaderPaint, currentY)
currentY = drawLEDData(canvas, testData, boldContentPaint, contentPaint, currentY)
currentY += 15f
// Laboratory Information Section
currentY = drawSection(canvas, "LABORATORY INFORMATION", sectionHeaderPaint, currentY)
currentY = drawKeyValuePair(canvas, "Lab Name:", testData.labName ?: "N/A", boldContentPaint, contentPaint, currentY)
currentY = drawKeyValuePair(canvas, "Center Name:", testData.centerName ?: "N/A", boldContentPaint, contentPaint, currentY)
currentY = drawKeyValuePair(canvas, "District:", testData.district ?: "N/A", boldContentPaint, contentPaint, currentY)
currentY += 15f
// Footer with page number and disclaimer
drawFooter(canvas, pageNumber, totalPages, contentPaint, disclaimerPaint,disclaimerText)
}
private fun drawHeader(canvas: Canvas, headerPaint: Paint, titlePaint: Paint, startY: Float, context: Context) {
// Load and draw actual logo
try {
// Method 1: Load from drawable resources
val logoDrawable = ContextCompat.getDrawable(context, R.mipmap.hpos_icon) // Replace with your logo resource
logoDrawable?.let { drawable ->
val logoWidth = 70
val logoHeight = 60
drawable.setBounds(
MARGIN_LEFT.toInt(),
(startY - 20f).toInt(),
(MARGIN_LEFT + logoWidth).toInt(),
(startY + logoHeight - 20f).toInt()
)
drawable.draw(canvas)
}
} catch (e: Exception) {
// Fallback to placeholder if logo loading fails
val logoPaint = Paint().apply {
color = Color.rgb(0, 102, 153)
style = Paint.Style.FILL
}
canvas.drawRect(MARGIN_LEFT, startY - 20f, MARGIN_LEFT + 60f, startY + 20f, logoPaint)
// Draw "LOGO" text as placeholder
val placeholderPaint = Paint().apply {
color = Color.WHITE
textSize = 12f
typeface = Typeface.create(Typeface.DEFAULT_BOLD, Typeface.BOLD)
textAlign = Paint.Align.CENTER
}
canvas.drawText("LOGO", MARGIN_LEFT + 30f, startY, placeholderPaint)
}
// Company name and title
canvas.drawText("HemoCube", MARGIN_LEFT + 80f, startY + 10f, headerPaint)
canvas.drawText("Medical Test Report", MARGIN_LEFT, startY + 40f, titlePaint)
// Report generation date
val currentDate = SimpleDateFormat("dd MMM yyyy, HH:mm", Locale.getDefault()).format(Date())
canvas.drawText("Generated: $currentDate", PAGE_WIDTH - MARGIN_RIGHT - 150f, startY + 10f, Paint().apply {
textSize = 10f
color = Color.GRAY
})
// Line under header
val linePaint = Paint().apply {
color = Color.rgb(0, 102, 153)
strokeWidth = 2f
}
canvas.drawLine(MARGIN_LEFT, startY + 60f, PAGE_WIDTH - MARGIN_RIGHT, startY + 60f, linePaint)
}
private fun drawSection(canvas: Canvas, title: String, paint: Paint, y: Float): Float {
canvas.drawText(title, MARGIN_LEFT, y, paint)
// Underline for section
val linePaint = Paint().apply {
color = Color.rgb(0, 102, 153)
strokeWidth = 1f
}
canvas.drawLine(MARGIN_LEFT, y + 5f, MARGIN_LEFT + paint.measureText(title), y + 5f, linePaint)
return y + 25f
}
private fun drawKeyValuePair(canvas: Canvas, key: String, value: String, keyPaint: Paint, valuePaint: Paint, y: Float): Float {
canvas.drawText(key, MARGIN_LEFT + 10f, y, keyPaint)
canvas.drawText(value, MARGIN_LEFT + 150f, y, valuePaint)
return y + 18f
}
private fun drawLEDData(canvas: Canvas, testData: HemoCubeTestData, keyPaint: Paint, valuePaint: Paint, startY: Float): Float {
var currentY = startY
currentY = drawKeyValuePair(canvas, "LED1 Sample:", testData.led1Sample?.toString() ?: "N/A", keyPaint, valuePaint, currentY)
currentY = drawKeyValuePair(canvas, "LED2 Sample:", testData.led2Sample?.toString() ?: "N/A", keyPaint, valuePaint, currentY)
currentY = drawKeyValuePair(canvas, "LED3 Sample:", testData.led3Sample?.toString() ?: "N/A", keyPaint, valuePaint, currentY)
currentY = drawKeyValuePair(canvas, "LED4 Sample:", testData.led4Sample?.toString() ?: "N/A", keyPaint, valuePaint, currentY)
currentY = drawKeyValuePair(canvas, "ABS1:", testData.abs1?.toString() ?: "N/A", keyPaint, valuePaint, currentY)
currentY = drawKeyValuePair(canvas, "ABS2:", testData.abs2?.toString() ?: "N/A", keyPaint, valuePaint, currentY)
currentY = drawKeyValuePair(canvas, "ABS3:", testData.abs3?.toString() ?: "N/A", keyPaint, valuePaint, currentY)
currentY = drawKeyValuePair(canvas, "ABS4:", testData.abs4?.toString() ?: "N/A", keyPaint, valuePaint, currentY)
return currentY
}
private fun drawFooter(canvas: Canvas, pageNumber: Int, totalPages: Int, contentPaint: Paint, disclaimerPaint: Paint,disclaimerText: String) {
val footerY = PAGE_HEIGHT - MARGIN_BOTTOM + 20f
// Page number
canvas.drawText("Page $pageNumber of $totalPages", PAGE_WIDTH - MARGIN_RIGHT - 80f, footerY, contentPaint)
// Disclaimer
val disclaimerLines = wrapText(disclaimerText, disclaimerPaint, PAGE_WIDTH - MARGIN_LEFT - MARGIN_RIGHT)
var disclaimerY = footerY + 20f
disclaimerLines.forEach { line ->
canvas.drawText(line, MARGIN_LEFT, disclaimerY, disclaimerPaint)
disclaimerY += 12f
}
// Footer line
val linePaint = Paint().apply {
color = Color.rgb(0, 102, 153)
strokeWidth = 1f
}
canvas.drawLine(MARGIN_LEFT, footerY - 10f, PAGE_WIDTH - MARGIN_RIGHT, footerY - 10f, linePaint)
}
private fun wrapText(text: String, paint: Paint, maxWidth: Float): List<String> {
val words = text.split(" ")
val lines = mutableListOf<String>()
var currentLine = ""
for (word in words) {
val testLine = if (currentLine.isEmpty()) word else "$currentLine $word"
if (paint.measureText(testLine) <= maxWidth) {
currentLine = testLine
} else {
if (currentLine.isNotEmpty()) {
lines.add(currentLine)
}
currentLine = word
}
}
if (currentLine.isNotEmpty()) {
lines.add(currentLine)
}
return lines
}
private fun previewPdf(context: Context, file: File) {
val uri = FileProvider.getUriForFile(
context,
"${context.packageName}.fileprovider",
file
)
val intent = Intent(Intent.ACTION_VIEW).apply {
setDataAndType(uri, "application/pdf")
flags = Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_ACTIVITY_NO_HISTORY
}
try {
context.startActivity(intent)
} catch (e: ActivityNotFoundException) {
Toast.makeText(context, "No PDF viewer found", Toast.LENGTH_SHORT).show()
}
}
}

View File

@@ -0,0 +1,219 @@
package com.example.hpostesting.presentation.reportgen.ui.report
import android.app.DatePickerDialog
import android.content.Context
import android.content.SharedPreferences
import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.AdapterView
import android.widget.ArrayAdapter
import android.widget.Button
import android.widget.EditText
import android.widget.Spinner
import android.widget.TextView
import android.widget.Toast
import androidx.core.widget.addTextChangedListener
import androidx.fragment.app.Fragment
import androidx.fragment.app.viewModels
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.example.hpostesting.data.model.patient.HemoCubeTestData
import dagger.hilt.android.AndroidEntryPoint
import `in`.sminnovations.hpostesting.R
import java.text.SimpleDateFormat
import java.util.Calendar
import java.util.Date
import java.util.Locale
private val DEFAULT_DISCLAIMER = """
DISCLAIMER: This report is generated by HemoCube automated system.
Results should be interpreted by qualified medical professionals.
This report is for diagnostic purposes only and should not be used as the sole basis for medical decisions.
Please consult with your healthcare provider for proper interpretation and treatment recommendations.
""".trimIndent()
@AndroidEntryPoint
class ReportFragment : Fragment() {
companion object {
fun newInstance() = ReportFragment()
}
private lateinit var sharedPreferences: SharedPreferences
private val viewModel: ReportViewModel by viewModels()
private lateinit var recyclerView: RecyclerView
private lateinit var searchBar: EditText
private lateinit var filterSpinner: Spinner
private lateinit var generateBtn: Button
private lateinit var selectedCountText: TextView
private lateinit var calendarButton: Button
private lateinit var adapter: BufferAdapter
private var selectedItems: MutableList<HemoCubeTestData> = mutableListOf()
private var allData: List<HemoCubeTestData> = emptyList()
private var currentFilter = "Today"
private var customDate: String? = null
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?
): View = inflater.inflate(R.layout.fragment_report, container, false)
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
sharedPreferences = requireContext().getSharedPreferences("HEMOCUBE", Context.MODE_PRIVATE)
initViews(view)
setupRecyclerView()
setupFilterSpinner()
setupObservers()
setupListeners()
// Set default filter to Today
setDefaultFilter()
}
private fun initViews(view: View) {
recyclerView = view.findViewById(R.id.bufferRecyclerView)
searchBar = view.findViewById(R.id.searchBar)
filterSpinner = view.findViewById(R.id.filterSpinner)
generateBtn = view.findViewById(R.id.generateReportButton)
selectedCountText = view.findViewById(R.id.selectedCountText)
calendarButton = view.findViewById(R.id.calendarButton)
}
private fun setupRecyclerView() {
recyclerView.layoutManager = LinearLayoutManager(requireContext())
adapter = BufferAdapter(emptyList()) { item ->
toggleItemSelection(item)
updateUI()
}
recyclerView.adapter = adapter
}
private fun setupFilterSpinner() {
val filterOptions = arrayOf("Today", "Yesterday", "All", "Custom Date")
val spinnerAdapter = ArrayAdapter(requireContext(), android.R.layout.simple_spinner_item, filterOptions)
spinnerAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
filterSpinner.adapter = spinnerAdapter
}
private fun setDefaultFilter() {
filterSpinner.setSelection(0) // Today
currentFilter = "Today"
calendarButton.visibility = View.GONE
}
private fun setupObservers() {
viewModel.allKitTestData.observe(viewLifecycleOwner) { list ->
allData = list
filterList(searchBar.text.toString(), currentFilter)
}
}
private fun setupListeners() {
searchBar.addTextChangedListener { editable ->
filterList(editable.toString(), currentFilter)
}
filterSpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
override fun onItemSelected(parent: AdapterView<*>, view: View?, pos: Int, id: Long) {
currentFilter = parent.getItemAtPosition(pos).toString()
if (currentFilter == "Custom Date") {
calendarButton.visibility = View.VISIBLE
if (customDate == null) {
showDatePicker()
} else {
filterList(searchBar.text.toString(), currentFilter)
}
} else {
calendarButton.visibility = View.GONE
customDate = null
filterList(searchBar.text.toString(), currentFilter)
}
}
override fun onNothingSelected(p0: AdapterView<*>?) {}
}
calendarButton.setOnClickListener {
showDatePicker()
}
generateBtn.setOnClickListener {
if (selectedItems.isNotEmpty()) {
ReportGen.generate(requireContext(), selectedItems, disclaimerText = sharedPreferences.getString("report_dis_txt",
DEFAULT_DISCLAIMER).toString())
}
}
}
private fun showDatePicker() {
val calendar = Calendar.getInstance()
val year = calendar.get(Calendar.YEAR)
val month = calendar.get(Calendar.MONTH)
val day = calendar.get(Calendar.DAY_OF_MONTH)
DatePickerDialog(requireContext(), { _, selectedYear, selectedMonth, selectedDay ->
val sdf = SimpleDateFormat("yyyy-MM-dd", Locale.getDefault())
val selectedCalendar = Calendar.getInstance()
selectedCalendar.set(selectedYear, selectedMonth, selectedDay)
customDate = sdf.format(selectedCalendar.time)
// Update calendar button text
val displayFormat = SimpleDateFormat("MMM dd, yyyy", Locale.getDefault())
calendarButton.text = displayFormat.format(selectedCalendar.time)
filterList(searchBar.text.toString(), currentFilter)
}, year, month, day).show()
}
private fun toggleItemSelection(item: HemoCubeTestData) {
if (selectedItems.contains(item)) {
selectedItems.remove(item)
} else {
selectedItems.add(item)
}
adapter.updateSelectedItems(selectedItems)
}
private fun updateUI() {
selectedCountText.text = "Selected: ${selectedItems.size}"
generateBtn.visibility = if (selectedItems.isNotEmpty()) View.VISIBLE else View.GONE
}
private fun filterList(query: String, filter: String) {
val sdf = SimpleDateFormat("yyyy-MM-dd", Locale.getDefault())
val today = sdf.format(Date())
val calendar = Calendar.getInstance()
calendar.add(Calendar.DAY_OF_MONTH, -1)
val yesterday = sdf.format(calendar.time)
val filtered = allData.filter { item ->
val matchesSearch = item._id.contains(query, true) ||
item.classificationResult.contains(query, true)
val matchesDate = when (filter) {
"Today" -> item.testTime?.contains(today)
"Yesterday" -> item.testTime?.contains(yesterday)
"Custom Date" -> customDate?.let { item.testTime?.contains(it) } ?: true
"All" -> true
else -> true
}
matchesSearch && matchesDate ?: true
}
adapter.updateList(filtered)
// Clear selections if items are no longer visible
selectedItems.removeAll { selectedItem ->
!filtered.contains(selectedItem)
}
adapter.updateSelectedItems(selectedItems)
updateUI()
}
}

View File

@@ -0,0 +1,19 @@
package com.example.hpostesting.presentation.reportgen.ui.report
import androidx.lifecycle.LiveData
import androidx.lifecycle.ViewModel
import com.example.hpostesting.data.dao.HemoCubeBufferDao
import com.example.hpostesting.data.dao.HemoCubeDao
import com.example.hpostesting.data.model.patient.BufferCheckData
import com.example.hpostesting.data.model.patient.HemoCubeTestData
import dagger.hilt.android.lifecycle.HiltViewModel
import javax.inject.Inject
@HiltViewModel
class ReportViewModel @Inject constructor(
private val hemoCubeDao: HemoCubeDao,
) : ViewModel() {
val allKitTestData: LiveData<List<HemoCubeTestData>> = hemoCubeDao.getAll()
}

View File

@@ -593,7 +593,7 @@ class TrueHemeFragment : Fragment() {
binding.btnSubmit.visibility = View.VISIBLE binding.btnSubmit.visibility = View.VISIBLE
binding.btnSubmit.isEnabled = true binding.btnSubmit.isEnabled = true
binding.btnSubmit.isClickable = true binding.btnSubmit.isClickable = true
// binding.clParent.setBackgroundColor(Color.parseColor("#edfffd")) // binding.clParent.setBackgroundColor(Color.parseColor("#edfffd"))
binding.ivCheck.visibility = View.VISIBLE binding.ivCheck.visibility = View.VISIBLE
} }
} else { } else {
@@ -736,7 +736,7 @@ class TrueHemeFragment : Fragment() {
} }
} }
private fun processResult() { private fun processResult(){
try { try {
hemoCubeViewModel.messages.postValue(getString(R.string.processing_result)) hemoCubeViewModel.messages.postValue(getString(R.string.processing_result))
val deviceLog = resultData val deviceLog = resultData

View File

@@ -29,6 +29,8 @@ import androidx.core.content.ContextCompat
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels import androidx.fragment.app.activityViewModels
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.lifecycleScope
import androidx.transition.Visibility
import com.example.hpostesting.data.constant.DataHolder import com.example.hpostesting.data.constant.DataHolder
import com.example.hpostesting.util.Result import com.example.hpostesting.util.Result
import com.example.hpostesting.data.constant.Constants import com.example.hpostesting.data.constant.Constants
@@ -45,13 +47,19 @@ import com.google.firebase.crashlytics.ktx.crashlytics
import com.google.firebase.ktx.Firebase import com.google.firebase.ktx.Firebase
import `in`.sminnovations.hpostesting.R import `in`.sminnovations.hpostesting.R
import `in`.sminnovations.hpostesting.databinding.FragmentHemoCubeReferenceBinding import `in`.sminnovations.hpostesting.databinding.FragmentHemoCubeReferenceBinding
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlin.math.abs import kotlin.math.abs
import kotlin.math.log10 import kotlin.math.log10
import kotlin.random.Random import kotlin.random.Random
//This is TrueHemeTestFragment, where actual tests are done first buffer check and then sample check //This is TrueHemeTestFragment, where actual tests are done first buffer check and then sample check
//and finally on submit test data to database if online or store in offline database //and finally on submit test data to database if online or store in offline database
@Suppress("MemberVisibilityCanBePrivate") @Suppress("MemberVisibilityCanBePrivate")
class TrueHemeTestFragment : Fragment() { class TrueHemeTestFragment : Fragment() {
private var doWeNeedAutoSmbt = false
private var positiveBoderLine10mm1=Constants.positiveBoderLineMetricCheck10mmMin private var positiveBoderLine10mm1=Constants.positiveBoderLineMetricCheck10mmMin
private var positiveBoderLine10mm2=Constants.positiveBoderLineMetricCheck10mmMax private var positiveBoderLine10mm2=Constants.positiveBoderLineMetricCheck10mmMax
private var negativeBoderLine10mm1=Constants.negativeBoderLineMetricCheck10mmMin private var negativeBoderLine10mm1=Constants.negativeBoderLineMetricCheck10mmMin
@@ -85,6 +93,7 @@ class TrueHemeTestFragment : Fragment() {
private var temperature="" private var temperature=""
private var cuvetteSizeSP = "10mm" private var cuvetteSizeSP = "10mm"
private var checkCuvette = false private var checkCuvette = false
private var checkCuvettePresent = false
private var checkRefreshCuvette = false private var checkRefreshCuvette = false
private var checkCuvetteSam = false private var checkCuvetteSam = false
private var checkSubmit = false private var checkSubmit = false
@@ -132,7 +141,7 @@ class TrueHemeTestFragment : Fragment() {
binding = FragmentHemoCubeReferenceBinding.inflate(inflater, container, false) binding = FragmentHemoCubeReferenceBinding.inflate(inflater, container, false)
sharedPreferences = sharedPreferences =
requireContext().getSharedPreferences("HEMOCUBE", Context.MODE_PRIVATE) requireContext().getSharedPreferences("HEMOCUBE", Context.MODE_PRIVATE)
doWeNeedAutoSmbt = sharedPreferences.getBoolean("AutSmbt", false)
cuvetteSizeSP = sharedPreferences.getString(Constants.CUVETTE_SIZE,"10mm").toString() cuvetteSizeSP = sharedPreferences.getString(Constants.CUVETTE_SIZE,"10mm").toString()
testState = TestState( testState = TestState(
@@ -142,6 +151,7 @@ class TrueHemeTestFragment : Fragment() {
return binding.root return binding.root
} }
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
initViews() initViews()
@@ -150,10 +160,50 @@ class TrueHemeTestFragment : Fragment() {
observeViewModel() observeViewModel()
} }
private fun autoSmbt(ignoreCuvette : Boolean = true){
submitClick = true
checkSubmit = true
lifecycleScope.launch {
delay(100)
}
checkCuvettePresence()
lifecycleScope.launch {
delay(3000)
}
activity?.runOnUiThread {
with(sharedPreferences.edit()) {
putBoolean(Constants.QUICK_CAPTURE, false)
apply()
}
if (DataHolder.hemoCubeTestData!!.classificationResult != "Invalid") {
DataHolder.sampleReadCounter++
}
binding.btnSubmit.isEnabled = false
binding.btnSubmit.isClickable = false
activity?.runOnUiThread {
Log.d("HemoCubeFragment", "Test Process completed, result saved")
binding.progressBar.visibility = View.VISIBLE
binding.btnSubmit.visibility = View.GONE
}
trueHemeTestViewModel.uploadHemoCubeResultToDatabase(
isOnline,
true,
sharedPreferences.getString(Constants.KIT_NUMBER, ""),
quickCapture
)
Toast.makeText(requireContext(), "Auto Submitted", Toast.LENGTH_SHORT).show()
}
}
@SuppressLint("SetTextI18n") @SuppressLint("SetTextI18n")
private fun initViews() { private fun initViews() {
binding.tvSubtitle4.movementMethod = ScrollingMovementMethod() binding.tvSubtitle4.movementMethod = ScrollingMovementMethod()
binding.tvDeviceMessages.movementMethod = ScrollingMovementMethod() binding.tvDeviceMessages.movementMethod = ScrollingMovementMethod()
binding.btnGoback.setOnClickListener{
goback()
}
binding.btnSubmit.setOnClickListener { binding.btnSubmit.setOnClickListener {
// with(sharedPreferences.edit()) { // with(sharedPreferences.edit()) {
// putBoolean(Constants.QUICK_CAPTURE, false) // putBoolean(Constants.QUICK_CAPTURE, false)
@@ -200,6 +250,7 @@ class TrueHemeTestFragment : Fragment() {
binding.nameEditText.visibility = View.GONE binding.nameEditText.visibility = View.GONE
binding.tvTitle.visibility = View.GONE binding.tvTitle.visibility = View.GONE
binding.btnGo.visibility = View.GONE binding.btnGo.visibility = View.GONE
binding.btnGoback.visibility = View.GONE
// binding.btnPlacebuffer.visibility = View.GONE // binding.btnPlacebuffer.visibility = View.GONE
val cuvetteSize = sharedPreferences.getString(Constants.CUVETTE_SIZE,"10mm") val cuvetteSize = sharedPreferences.getString(Constants.CUVETTE_SIZE,"10mm")
val cuvetteText = "Cuvette Size: $cuvetteSize" val cuvetteText = "Cuvette Size: $cuvetteSize"
@@ -346,11 +397,34 @@ class TrueHemeTestFragment : Fragment() {
} }
if (result == "Local") { if (result == "Local") {
showToast(R.string.internt_not_local) showToast(R.string.internt_not_local)
startActivity(Intent(requireActivity(), DashboardActivity::class.java)) //got it
if (doWeNeedAutoSmbt) {
if (!checkCuvettePresent) {
Toast.makeText(context, "Gooing back 2", Toast.LENGTH_SHORT).show()
startActivity(Intent(requireActivity(), DashboardActivity::class.java))
}else{
makeRetryButtonVisible()
Toast.makeText(context, "Remove Cuvette", Toast.LENGTH_SHORT).show()
}
}else{
startActivity(Intent(requireActivity(), DashboardActivity::class.java))
}
} }
if (result == "Error") { if (result == "Error") {
showToast(R.string.error_local) showToast(R.string.error_local)
startActivity(Intent(requireActivity(), DashboardActivity::class.java)) //got it
if (doWeNeedAutoSmbt) {
if (!checkCuvettePresent) {
Toast.makeText(context, "Gooing back", Toast.LENGTH_SHORT).show()
startActivity(Intent(requireActivity(), DashboardActivity::class.java))
}else{
makeRetryButtonVisible()
Toast.makeText(context, "Remove Cuvette", Toast.LENGTH_SHORT).show()
}
}else{
startActivity(Intent(requireActivity(), DashboardActivity::class.java))
}
} }
binding.progressBar.visibility = View.GONE binding.progressBar.visibility = View.GONE
@@ -395,7 +469,18 @@ class TrueHemeTestFragment : Fragment() {
val i = Intent( val i = Intent(
requireContext().applicationContext, DashboardActivity::class.java requireContext().applicationContext, DashboardActivity::class.java
) )
startActivity(i) //got it
if (doWeNeedAutoSmbt){
if (!checkCuvettePresent){
Toast.makeText(context, "Gooing back 3", Toast.LENGTH_SHORT).show()
startActivity(i)
}else{
makeRetryButtonVisible()
Toast.makeText(context, "Remove Cuvette", Toast.LENGTH_SHORT).show()
}
}else {
startActivity(i)
}
} }
} else { } else {
trueHemeTestViewModel.messages.postValue("Reading $repeatReadingCount completed") trueHemeTestViewModel.messages.postValue("Reading $repeatReadingCount completed")
@@ -451,7 +536,7 @@ class TrueHemeTestFragment : Fragment() {
} }
} }
private fun showBufferAlertDialog() { private fun showBufferAlertDialog(){
val title = "WARNING" val title = "WARNING"
val message = getString(R.string.do_exist) val message = getString(R.string.do_exist)
val negativeText = getString(R.string.no) val negativeText = getString(R.string.no)
@@ -604,6 +689,7 @@ class TrueHemeTestFragment : Fragment() {
} }
} }
resultData.contains("#AIN") && sampleClick && this.testStatusCode <= TestStatus.CUVETTE_ABSENTS.code -> { resultData.contains("#AIN") && sampleClick && this.testStatusCode <= TestStatus.CUVETTE_ABSENTS.code -> {
checkCuvettePresent = false
trueHemeTestViewModel.messages.postValue(getString(R.string.cuvette_absent)) trueHemeTestViewModel.messages.postValue(getString(R.string.cuvette_absent))
this.testStatusCode = TestStatus.CUVETTE_ABSENTS.code this.testStatusCode = TestStatus.CUVETTE_ABSENTS.code
activity?.runOnUiThread { activity?.runOnUiThread {
@@ -616,6 +702,7 @@ class TrueHemeTestFragment : Fragment() {
this.testStatusCode = TestStatus.CUVETTE_PRESENTR.code this.testStatusCode = TestStatus.CUVETTE_PRESENTR.code
activity?.runOnUiThread { activity?.runOnUiThread {
checkRefreshCuvette = true checkRefreshCuvette = true
checkCuvettePresent =true
binding.testing.visibility = View.GONE binding.testing.visibility = View.GONE
binding.btnRetryCheckCuvette.visibility = View.GONE binding.btnRetryCheckCuvette.visibility = View.GONE
binding.btnPlaceRefreshbuffer.visibility = View.VISIBLE binding.btnPlaceRefreshbuffer.visibility = View.VISIBLE
@@ -626,6 +713,7 @@ class TrueHemeTestFragment : Fragment() {
} }
} }
resultData.contains("#AIN") && refreshClick && this.testStatusCode <= TestStatus.CUVETTE_ABSENTR.code -> { resultData.contains("#AIN") && refreshClick && this.testStatusCode <= TestStatus.CUVETTE_ABSENTR.code -> {
checkCuvettePresent = false
trueHemeTestViewModel.messages.postValue(getString(R.string.cuvette_absent)) trueHemeTestViewModel.messages.postValue(getString(R.string.cuvette_absent))
this.testStatusCode = TestStatus.CUVETTE_ABSENTR.code this.testStatusCode = TestStatus.CUVETTE_ABSENTR.code
activity?.runOnUiThread { activity?.runOnUiThread {
@@ -636,6 +724,7 @@ class TrueHemeTestFragment : Fragment() {
showRetryButtonForCuvette() showRetryButtonForCuvette()
} }
resultData.contains("#CIN") && this.testStatusCode < TestStatus.CUVETTE_PRESENT.code -> { resultData.contains("#CIN") && this.testStatusCode < TestStatus.CUVETTE_PRESENT.code -> {
checkCuvettePresent = true
trueHemeTestViewModel.messages.postValue(getString(R.string.cuvette_present)) trueHemeTestViewModel.messages.postValue(getString(R.string.cuvette_present))
this.testStatusCode = TestStatus.CUVETTE_PRESENT.code this.testStatusCode = TestStatus.CUVETTE_PRESENT.code
activity?.runOnUiThread { activity?.runOnUiThread {
@@ -649,6 +738,7 @@ class TrueHemeTestFragment : Fragment() {
} }
} }
resultData.contains("#AIN") && this.testStatusCode <= TestStatus.CUVETTE_ABSENT.code -> { resultData.contains("#AIN") && this.testStatusCode <= TestStatus.CUVETTE_ABSENT.code -> {
checkCuvettePresent = false
trueHemeTestViewModel.messages.postValue(getString(R.string.cuvette_absent)) trueHemeTestViewModel.messages.postValue(getString(R.string.cuvette_absent))
this.testStatusCode = TestStatus.CUVETTE_ABSENT.code this.testStatusCode = TestStatus.CUVETTE_ABSENT.code
activity?.runOnUiThread { activity?.runOnUiThread {
@@ -658,14 +748,24 @@ class TrueHemeTestFragment : Fragment() {
} }
showRetryButtonForCuvette() showRetryButtonForCuvette()
} }
//need to change where check submit
resultData.contains("#CIN") && this.submitClick && this.testStatusCode < TestStatus.CUVETTE_PRESENTT.code -> { resultData.contains("#CIN") && this.submitClick && this.testStatusCode < TestStatus.CUVETTE_PRESENTT.code -> {
trueHemeTestViewModel.messages.postValue(getString(R.string.cuvette_presentt)) checkCuvettePresent = true
if (doWeNeedAutoSmbt){
trueHemeTestViewModel.messages.postValue(getString(R.string.cuvette_presentnt))
}else {
trueHemeTestViewModel.messages.postValue(getString(R.string.cuvette_presentt))
}
this.testStatusCode = TestStatus.CUVETTE_PRESENTT.code this.testStatusCode = TestStatus.CUVETTE_PRESENTT.code
activity?.runOnUiThread { activity?.runOnUiThread {
binding.testing.visibility = View.GONE binding.testing.visibility = View.GONE
} }
} }
resultData.contains("#AIN") && this.submitClick && this.testStatusCode <= TestStatus.CUVETTE_ABSENTT.code -> { resultData.contains("#AIN") && this.submitClick && this.testStatusCode <= TestStatus.CUVETTE_ABSENTT.code -> {
checkCuvettePresent = false
trueHemeTestViewModel.messages.postValue(getString(R.string.cuvette_absentt)) trueHemeTestViewModel.messages.postValue(getString(R.string.cuvette_absentt))
this.testStatusCode = TestStatus.CUVETTE_ABSENTT.code this.testStatusCode = TestStatus.CUVETTE_ABSENTT.code
activity?.runOnUiThread { activity?.runOnUiThread {
@@ -957,11 +1057,18 @@ class TrueHemeTestFragment : Fragment() {
if (readingsPerSample == 1) { if (readingsPerSample == 1) {
activity?.runOnUiThread { activity?.runOnUiThread {
//changing to invisible
binding.btnSubmit.visibility = View.VISIBLE binding.btnSubmit.visibility = View.VISIBLE
binding.btnSubmit.isEnabled = true // binding.btnSubmit.isEnabled = true
binding.btnSubmit.isClickable = true // binding.btnSubmit.isClickable = true
// binding.clParent.setBackgroundColor(Color.parseColor("#edfffd")) // binding.clParent.setBackgroundColor(Color.parseColor("#edfffd"))
if (doWeNeedAutoSmbt) {
binding.btnSubmit.visibility = View.GONE
autoSmbt(doWeNeedAutoSmbt)
}
binding.ivCheck.visibility = View.VISIBLE binding.ivCheck.visibility = View.VISIBLE
} }
} else { } else {
trueHemeTestViewModel.uploadHemoCubeResultToDatabase( trueHemeTestViewModel.uploadHemoCubeResultToDatabase(
@@ -1647,7 +1754,6 @@ class TrueHemeTestFragment : Fragment() {
(activity as TrueHemeTestActivity).mService.sendAndListenToHemoCube(HemoCubeCommands.CHECK_CUVETTE_COMMAND, (activity as TrueHemeTestActivity).mService.sendAndListenToHemoCube(HemoCubeCommands.CHECK_CUVETTE_COMMAND,
object : UsbServiceListener { object : UsbServiceListener {
override fun onUsbRead(data: ByteArray?) {} override fun onUsbRead(data: ByteArray?) {}
override fun onUsbError(e: Exception?) { override fun onUsbError(e: Exception?) {
trueHemeTestViewModel.progressBar.postValue(false) trueHemeTestViewModel.progressBar.postValue(false)
} }
@@ -1667,7 +1773,22 @@ class TrueHemeTestFragment : Fragment() {
} }
}) })
} }
fun goback(){
checkCuvettePresence()
lifecycleScope.launch {
delay(4000)
}
if (!checkCuvettePresent) {
startActivity(Intent(requireActivity(), DashboardActivity::class.java))
}
}
fun makeRetryButtonVisible(){
activity?.runOnUiThread {
binding.btnGoback.visibility = View.VISIBLE
}
}
private fun startSampleProcess() { private fun startSampleProcess() {
trueHemeTestViewModel.progressBar.postValue(true) trueHemeTestViewModel.progressBar.postValue(true)
@@ -1769,4 +1890,7 @@ class TrueHemeTestFragment : Fragment() {
private fun generateTwoDigitRandomNumber(): Int { private fun generateTwoDigitRandomNumber(): Int {
return Random.nextInt(10, 100) return Random.nextInt(10, 100)
} }
} }

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="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM17,13h-4v4h-2v-4L7,13v-2h4L11,7h2v4h4v2z"/>
</vector>

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="M2.88,7.88l1.54,1.54C4.15,10.23 4,11.1 4,12c0,4.41 3.59,8 8,8s8,-3.59 8,-8s-3.59,-8 -8,-8c-0.9,0 -1.77,0.15 -2.58,0.42L7.89,2.89C9.15,2.32 10.54,2 12,2c5.52,0 10,4.48 10,10s-4.48,10 -10,10S2,17.52 2,12C2,10.53 2.32,9.14 2.88,7.88zM7,5.5C7,6.33 6.33,7 5.5,7S4,6.33 4,5.5S4.67,4 5.5,4S7,4.67 7,5.5zM12.03,8.99h-0.07l-1.16,3.31h2.39L12.03,8.99zM12,18c3.31,0 6,-2.69 6,-6s-2.69,-6 -6,-6s-6,2.69 -6,6S8.69,18 12,18zM11.29,7.5h1.43l3.01,8h-1.39l-0.72,-2.04h-3.23L9.66,15.5H8.28L11.29,7.5z"/>
</vector>

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="M13,3c-4.97,0 -9,4.03 -9,9L1,12l3.89,3.89 0.07,0.14L9,12L6,12c0,-3.87 3.13,-7 7,-7s7,3.13 7,7 -3.13,7 -7,7c-1.93,0 -3.68,-0.79 -4.94,-2.06l-1.42,1.42C8.27,19.99 10.51,21 13,21c4.97,0 9,-4.03 9,-9s-4.03,-9 -9,-9zM12,8v5l4.28,2.54 0.72,-1.21 -3.5,-2.08L13.5,8L12,8z"/>
</vector>

View File

@@ -55,7 +55,21 @@
app:layout_constraintTop_toBottomOf="@id/tv_subtitle4" /> app:layout_constraintTop_toBottomOf="@id/tv_subtitle4" />
<Button <Button
android:id="@+id/btn_print_report"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="16dp"
android:layout_marginTop="24dp"
android:clickable="false"
android:text="Print Report"
android:textColor="@color/white"
app:cornerRadius="16dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/btn_submit" />
<Button
android:id="@+id/btn_diagnostics" android:id="@+id/btn_diagnostics"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
@@ -67,7 +81,7 @@
app:cornerRadius="16dp" app:cornerRadius="16dp"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/btn_submit" /> app:layout_constraintTop_toBottomOf="@id/btn_print_report" />
<com.google.android.material.button.MaterialButton <com.google.android.material.button.MaterialButton
android:id="@+id/btn_auto_dac" android:id="@+id/btn_auto_dac"
@@ -153,6 +167,7 @@
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/btn_deviceProvision" /> app:layout_constraintTop_toBottomOf="@id/btn_deviceProvision" />
<com.google.android.material.button.MaterialButton <com.google.android.material.button.MaterialButton
android:id="@+id/btn_update_values" android:id="@+id/btn_update_values"
android:layout_width="match_parent" android:layout_width="match_parent"
@@ -160,12 +175,13 @@
android:layout_marginHorizontal="16dp" android:layout_marginHorizontal="16dp"
android:layout_marginTop="24dp" android:layout_marginTop="24dp"
android:clickable="false" android:clickable="false"
android:text="Update values" android:text="Update valueLs"
android:textColor="@color/white" android:textColor="@color/white"
app:cornerRadius="16dp" app:cornerRadius="16dp"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/btn_reset_password" /> app:layout_constraintTop_toBottomOf="@id/btn_reset_password" />
<com.google.android.material.button.MaterialButton <com.google.android.material.button.MaterialButton
android:id="@+id/btn_firefox" android:id="@+id/btn_firefox"
android:layout_width="match_parent" android:layout_width="match_parent"

View File

@@ -225,6 +225,19 @@
app:cornerRadius="16dp" app:cornerRadius="16dp"
/> />
<Button
android:id="@+id/btn_goback"
android:visibility="gone"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="16dp"
android:layout_marginTop="24dp"
android:clickable="false"
android:text="Go back"
android:textColor="@color/white"
app:cornerRadius="16dp"
/>
</LinearLayout> </LinearLayout>
<TextView <TextView

View File

@@ -0,0 +1,63 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:padding="12dp"
android:layout_width="match_parent"
android:layout_height="match_parent">
<EditText
android:id="@+id/searchBar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Search by Sample ID or result"
android:imeOptions="actionSearch"
android:inputType="text" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_marginTop="8dp">
<Spinner
android:id="@+id/filterSpinner"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:entries="@array/filter_options" />
<Button
android:id="@+id/calendarButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Select Date"
android:visibility="gone"
android:layout_marginStart="8dp"
style="@style/Widget.Material3.Button.OutlinedButton" />
</LinearLayout>
<TextView
android:id="@+id/selectedCountText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Selected: 0"
android:layout_marginTop="8dp"
android:textStyle="bold" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/bufferRecyclerView"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:layout_marginTop="8dp" />
<Button
android:id="@+id/generateReportButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Generate Report"
android:visibility="gone"/>
</LinearLayout>

View File

@@ -0,0 +1,54 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/cardView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="4dp"
app:cardCornerRadius="8dp"
app:cardElevation="4dp">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="16dp">
<TextView
android:id="@+id/sampleId"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Kit No: Sample123"
android:textSize="16sp"
android:textStyle="bold" />
<View
android:id="@+id/selectionIndicator"
android:layout_width="12dp"
android:layout_height="12dp"
android:layout_alignParentEnd="true"
android:layout_centerVertical="true"
android:background="@drawable/ic_baseline_check_circle_24"
android:visibility="gone" />
<TextView
android:id="@+id/testTime"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/sampleId"
android:layout_marginTop="4dp"
android:text="Time: 2024-01-15 10:30"
android:textSize="14sp" />
<TextView
android:id="@+id/result"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/testTime"
android:layout_marginTop="4dp"
android:text="Result: Positive"
android:textSize="14sp" />
</RelativeLayout>
</androidx.cardview.widget.CardView>

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent" />

View File

@@ -307,5 +307,6 @@
<string name="calibrate_device">Calibrate the device</string> <string name="calibrate_device">Calibrate the device</string>
<string name="cuvette_presentt">Cuvette Present , Please Remove Cuvette And Try Again</string> <string name="cuvette_presentt">Cuvette Present , Please Remove Cuvette And Try Again</string>
<string name="cuvette_absentt">Cuvette Absent,Now You Can Submit</string> <string name="cuvette_absentt">Cuvette Absent,Now You Can Submit</string>
<string name="cuvette_presentnt">Test Done, Cuvette Present, Please Remove Cuvette And Goback</string>
</resources> </resources>

View File

@@ -306,7 +306,7 @@
<string name="calibrate_device">ಸಾಧನವನ್ನು ಮಾಪನಾಂಕ ಮಾಡಿ</string> <string name="calibrate_device">ಸಾಧನವನ್ನು ಮಾಪನಾಂಕ ಮಾಡಿ</string>
<string name="cuvette_presentt">Cuvette Present , Please Remove Cuvette And Try Again</string> <string name="cuvette_presentt">Cuvette Present , Please Remove Cuvette And Try Again</string>
<string name="cuvette_absentt">Cuvette Absent,Now You Can Submit</string> <string name="cuvette_absentt">Cuvette Absent,Now You Can Submit</string>
<string name="cuvette_presentnt">Test Done, Cuvette Present, Please Remove Cuvette And Goback</string>
<!-- Add translations for other strings --> <!-- Add translations for other strings -->
</resources> </resources>

View File

@@ -33,4 +33,13 @@
<item>Other</item> <item>Other</item>
</string-array> </string-array>
<string-array name="filter_options">
<item>Today</item>
<item>Yesterday</item>
<item>All</item>
<item>Custom Date</item>
</string-array>
</resources> </resources>

View File

@@ -306,6 +306,7 @@
<string name="reset_password_for_this_device">Reset password for this device</string> <string name="reset_password_for_this_device">Reset password for this device</string>
<string name="calibrate_device">Calibrate Device</string> <string name="calibrate_device">Calibrate Device</string>
<string name="cuvette_presentt">Cuvette Present , Please Remove Cuvette And Try Again</string> <string name="cuvette_presentt">Cuvette Present , Please Remove Cuvette And Try Again</string>
<string name="cuvette_presentnt">Test Done, Cuvette Present, Please Remove Cuvette And Goback</string>
<string name="cuvette_absentt">Cuvette Absent,Now You Can Submit</string> <string name="cuvette_absentt">Cuvette Absent,Now You Can Submit</string>
</resources> </resources>

View File

@@ -25,4 +25,3 @@ plugins {
task clean(type: Delete) { task clean(type: Delete) {
delete rootProject.buildDir delete rootProject.buildDir
} }