Compare commits

...

8 Commits

18 changed files with 579 additions and 75 deletions

View File

@@ -1,13 +1,23 @@
# Latest hpos app version
## current version : 2.1.132
### current version : 2.1.133
### Release key
key0: prime24
### Version 133.3
Printer not added yet
//update it when it is completed
#### **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

@@ -21,8 +21,8 @@ android {
applicationId "in.sminnovations.hpostesting.server"
minSdk 30
targetSdk 34
versionCode 138
versionName "2.1.133.3"
versionCode 139
versionName "2.1.133"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
externalNativeBuild {
cmake {

View File

@@ -43,7 +43,7 @@
android:theme="@style/Theme.HPOSTesting"
tools:targetApi="31">
<activity
android:name="com.example.hpostesting.data.model.reportgen.ReportActivity"
android:name="com.example.hpostesting.presentation.reportgen.ReportActivity"
android:exported="false" />
<activity
android:name="com.example.hpostesting.presentation.main_base.UpdateValuesActivity"

View File

@@ -25,7 +25,7 @@ import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import com.example.hpostesting.data.constant.Constants
import com.example.hpostesting.data.model.reportgen.ReportActivity
import com.example.hpostesting.presentation.reportgen.ReportActivity
import com.example.hpostesting.presentation.autodac.AutoDacActivity
import com.example.hpostesting.presentation.buffercheck.HemocubeBufferCheckActivity
import com.example.hpostesting.presentation.calibration.CalibrationActivity
@@ -140,7 +140,7 @@ class PanelFragment : Fragment() {
}
binding.btnPrintReport.setOnClickListener{
startActivity(Intent(requireContext(),ReportActivity::class.java))
startActivity(Intent(requireContext(), ReportActivity::class.java))
}
binding.btnFirefox.setOnClickListener {

View File

@@ -17,6 +17,9 @@ import android.content.Context
import android.content.SharedPreferences
import android.content.pm.PackageManager
import android.os.Bundle
import android.text.InputFilter
import android.text.InputType
import android.view.Gravity
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
@@ -24,7 +27,9 @@ import android.widget.AdapterView
import android.widget.ArrayAdapter
import android.widget.Toast
import androidx.core.content.ContextCompat
import androidx.core.content.edit
import androidx.fragment.app.Fragment
import androidx.preference.EditTextPreference
import androidx.preference.ListPreference
import androidx.preference.Preference
import androidx.preference.PreferenceFragmentCompat
@@ -41,7 +46,12 @@ import `in`.sminnovations.hpostesting.databinding.FragmentSlideshowBinding
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() {
private var selectedItem = "10mm"
private val values = arrayOf("10mm", "2mm")
@@ -58,7 +68,6 @@ class SlideshowFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.nameEditText.setText(sharedPreferences.getString(Constants.LABNAME, ""))
selectedItem = sharedPreferences.getString(Constants.CUVETTE_SIZE, "10mm").toString()
binding.btnGo.setOnClickListener {
@@ -120,6 +129,8 @@ class PrefsFragment : PreferenceFragmentCompat() {
private lateinit var serverPreference: Preference
private lateinit var currentServerPreference: Preference
lateinit var firebaseManager: FirebaseManager
var autoSmbtValue = false
var reportDisclaimerText = DEFAULT_DISCLAIMER
private var topDevMode = false
lateinit var databaseRepository: DatabaseRepository
@@ -130,10 +141,12 @@ class PrefsFragment : PreferenceFragmentCompat() {
topDevMode = firebaseManager.getDevMode()
val sharedPreference =
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 currentServer = firebaseManager.getLastSelectedServer().serverName
val arWeAdmin =sharedPreference.getString(Constants.USER_ID, "").toString() == "ADMIN"
val MAX_CHARS = 10_000
// Language Preference
val languagePreference = ListPreference(requireContext()).apply {
key = "language_preference"
@@ -200,6 +213,70 @@ class PrefsFragment : PreferenceFragmentCompat() {
}
}
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 {
title = "App Version"
summary =
@@ -208,33 +285,43 @@ class PrefsFragment : PreferenceFragmentCompat() {
setOnPreferenceClickListener {
tapManager.registerTap {
firebaseManager.setDevModeOn(true)
if (arWeAdmin) {
firebaseManager.setDevModeOn(true)
if (firebaseManager.getDevMode() && currentServer != "dev") {
Toast.makeText(requireContext(), "Now you are a Developer", Toast.LENGTH_SHORT).show()
Toast.makeText(requireContext(), "Setting Up developer Environmnet", Toast.LENGTH_SHORT).show()
Toast.makeText(requireContext(), "Setting Up developer Environment", Toast.LENGTH_SHORT).show()
}
if (preferenceScreen.findPreference<SwitchPreferenceCompat>("developer_options") == null) {
preferenceScreen.addPreference(developerToggle)
preferenceScreen.addPreference(autosmbt)
preferenceScreen.addPreference(disclaimerPreference)
preferenceScreen.addPreference(resetDisclaimerPreference)
}
if (preferenceScreen.findPreference<ListPreference>("server_preference") == null) {
preferenceScreen.addPreference(serverSelectorPreference)
}
developerToggle.isChecked = true
updateServerPreference(currentServer)
}else{
Toast.makeText(requireContext(), "Works Only On Admin Mode", Toast.LENGTH_SHORT).show()
}
}
true
}
}
preferenceScreen.addPreference(appVersionPreference)
if (firebaseManager.getDevMode()) {
if (firebaseManager.getDevMode() && arWeAdmin) {
developerToggle.isChecked = true // Ensure it's checked
preferenceScreen.addPreference(developerToggle)
preferenceScreen.addPreference(serverSelectorPreference)
preferenceScreen.addPreference(autosmbt)
preferenceScreen.addPreference(resetDisclaimerPreference)
preferenceScreen.addPreference(disclaimerPreference)
}
setPreferenceScreen(preferenceScreen)
@@ -244,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) {
(findPreference<CustomDialogPreference>("server_preference"))?.let { pref ->

View File

@@ -1,8 +1,8 @@
package com.example.hpostesting.data.model.reportgen
package com.example.hpostesting.presentation.reportgen
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import com.example.hpostesting.data.model.reportgen.ui.report.ReportFragment
import com.example.hpostesting.presentation.reportgen.ui.report.ReportFragment
import dagger.hilt.android.AndroidEntryPoint
import `in`.sminnovations.hpostesting.R
@AndroidEntryPoint

View File

@@ -1,4 +1,4 @@
package com.example.hpostesting.data.model.reportgen.ui.report
package com.example.hpostesting.presentation.reportgen.ui.report
import android.graphics.Color
import android.view.LayoutInflater

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

@@ -1,7 +1,8 @@
package com.example.hpostesting.data.model.reportgen.ui.report
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
@@ -26,12 +27,21 @@ 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()
@@ -47,12 +57,14 @@ class ReportFragment : Fragment() {
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()
@@ -133,7 +145,8 @@ class ReportFragment : Fragment() {
generateBtn.setOnClickListener {
if (selectedItems.isNotEmpty()) {
ReportGen.generate(requireContext(), selectedItems)
ReportGen.generate(requireContext(), selectedItems, disclaimerText = sharedPreferences.getString("report_dis_txt",
DEFAULT_DISCLAIMER).toString())
}
}
}
@@ -204,31 +217,3 @@ class ReportFragment : Fragment() {
updateUI()
}
}
object ReportGen {
fun generate(context: Context, dataList: List<HemoCubeTestData>) {
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 kitNumbers = dataList.map { it._id }
val message = if (dataList.size == 1) {
"Report for kit ${kitNumbers.first()} started."
} else {
"Report for ${dataList.size} kits started."
}
Toast.makeText(context, message, Toast.LENGTH_SHORT).show()
// TODO: Add PDF generation, email, or preview logic here
// You can iterate through dataList to include all selected items in the report
dataList.forEach { item->
Log.d("ReportGen", "Processing kit: ${item._id}, Result: ${item.classificationResult}, Time: ${item.testTime}")
}
}
}

View File

@@ -1,4 +1,4 @@
package com.example.hpostesting.data.model.reportgen.ui.report
package com.example.hpostesting.presentation.reportgen.ui.report
import androidx.lifecycle.LiveData
import androidx.lifecycle.ViewModel

View File

@@ -55,8 +55,11 @@ import kotlin.math.log10
import kotlin.random.Random
//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
@Suppress("MemberVisibilityCanBePrivate")
class TrueHemeTestFragment : Fragment() {
private var doWeNeedAutoSmbt = false
private var positiveBoderLine10mm1=Constants.positiveBoderLineMetricCheck10mmMin
private var positiveBoderLine10mm2=Constants.positiveBoderLineMetricCheck10mmMax
private var negativeBoderLine10mm1=Constants.negativeBoderLineMetricCheck10mmMin
@@ -90,6 +93,7 @@ class TrueHemeTestFragment : Fragment() {
private var temperature=""
private var cuvetteSizeSP = "10mm"
private var checkCuvette = false
private var checkCuvettePresent = false
private var checkRefreshCuvette = false
private var checkCuvetteSam = false
private var checkSubmit = false
@@ -137,7 +141,7 @@ class TrueHemeTestFragment : Fragment() {
binding = FragmentHemoCubeReferenceBinding.inflate(inflater, container, false)
sharedPreferences =
requireContext().getSharedPreferences("HEMOCUBE", Context.MODE_PRIVATE)
doWeNeedAutoSmbt = sharedPreferences.getBoolean("AutSmbt", false)
cuvetteSizeSP = sharedPreferences.getString(Constants.CUVETTE_SIZE,"10mm").toString()
testState = TestState(
@@ -163,9 +167,11 @@ class TrueHemeTestFragment : Fragment() {
lifecycleScope.launch {
delay(100)
}
checkCuvettePresence()
lifecycleScope.launch {
delay(3000)
}
activity?.runOnUiThread {
if (checkSubmit) {
with(sharedPreferences.edit()) {
putBoolean(Constants.QUICK_CAPTURE, false)
apply()
@@ -186,22 +192,18 @@ class TrueHemeTestFragment : Fragment() {
sharedPreferences.getString(Constants.KIT_NUMBER, ""),
quickCapture
)
Toast.makeText(requireContext(), "Auto Sumbitted", Toast.LENGTH_SHORT).show()
} else {
checkCuvettePresence()
binding.btnSubmit.isEnabled = true
binding.btnSubmit.isClickable = true
Toast.makeText(requireContext(), "Auto Sumbit Failed ", Toast.LENGTH_SHORT).show()
binding.btnSubmit.visibility = View.VISIBLE
}
Toast.makeText(requireContext(), "Auto Submitted", Toast.LENGTH_SHORT).show()
}
}
@SuppressLint("SetTextI18n")
private fun initViews() {
binding.tvSubtitle4.movementMethod = ScrollingMovementMethod()
binding.tvDeviceMessages.movementMethod = ScrollingMovementMethod()
binding.btnGoback.setOnClickListener{
goback()
}
binding.btnSubmit.setOnClickListener {
// with(sharedPreferences.edit()) {
// putBoolean(Constants.QUICK_CAPTURE, false)
@@ -248,6 +250,7 @@ class TrueHemeTestFragment : Fragment() {
binding.nameEditText.visibility = View.GONE
binding.tvTitle.visibility = View.GONE
binding.btnGo.visibility = View.GONE
binding.btnGoback.visibility = View.GONE
// binding.btnPlacebuffer.visibility = View.GONE
val cuvetteSize = sharedPreferences.getString(Constants.CUVETTE_SIZE,"10mm")
val cuvetteText = "Cuvette Size: $cuvetteSize"
@@ -394,11 +397,34 @@ class TrueHemeTestFragment : Fragment() {
}
if (result == "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") {
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
@@ -443,7 +469,18 @@ class TrueHemeTestFragment : Fragment() {
val i = Intent(
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 {
trueHemeTestViewModel.messages.postValue("Reading $repeatReadingCount completed")
@@ -652,6 +689,7 @@ class TrueHemeTestFragment : Fragment() {
}
}
resultData.contains("#AIN") && sampleClick && this.testStatusCode <= TestStatus.CUVETTE_ABSENTS.code -> {
checkCuvettePresent = false
trueHemeTestViewModel.messages.postValue(getString(R.string.cuvette_absent))
this.testStatusCode = TestStatus.CUVETTE_ABSENTS.code
activity?.runOnUiThread {
@@ -664,6 +702,7 @@ class TrueHemeTestFragment : Fragment() {
this.testStatusCode = TestStatus.CUVETTE_PRESENTR.code
activity?.runOnUiThread {
checkRefreshCuvette = true
checkCuvettePresent =true
binding.testing.visibility = View.GONE
binding.btnRetryCheckCuvette.visibility = View.GONE
binding.btnPlaceRefreshbuffer.visibility = View.VISIBLE
@@ -674,6 +713,7 @@ class TrueHemeTestFragment : Fragment() {
}
}
resultData.contains("#AIN") && refreshClick && this.testStatusCode <= TestStatus.CUVETTE_ABSENTR.code -> {
checkCuvettePresent = false
trueHemeTestViewModel.messages.postValue(getString(R.string.cuvette_absent))
this.testStatusCode = TestStatus.CUVETTE_ABSENTR.code
activity?.runOnUiThread {
@@ -684,6 +724,7 @@ class TrueHemeTestFragment : Fragment() {
showRetryButtonForCuvette()
}
resultData.contains("#CIN") && this.testStatusCode < TestStatus.CUVETTE_PRESENT.code -> {
checkCuvettePresent = true
trueHemeTestViewModel.messages.postValue(getString(R.string.cuvette_present))
this.testStatusCode = TestStatus.CUVETTE_PRESENT.code
activity?.runOnUiThread {
@@ -697,6 +738,7 @@ class TrueHemeTestFragment : Fragment() {
}
}
resultData.contains("#AIN") && this.testStatusCode <= TestStatus.CUVETTE_ABSENT.code -> {
checkCuvettePresent = false
trueHemeTestViewModel.messages.postValue(getString(R.string.cuvette_absent))
this.testStatusCode = TestStatus.CUVETTE_ABSENT.code
activity?.runOnUiThread {
@@ -709,7 +751,12 @@ class TrueHemeTestFragment : Fragment() {
//need to change where check submit
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
activity?.runOnUiThread {
binding.testing.visibility = View.GONE
@@ -718,6 +765,7 @@ class TrueHemeTestFragment : Fragment() {
resultData.contains("#AIN") && this.submitClick && this.testStatusCode <= TestStatus.CUVETTE_ABSENTT.code -> {
checkCuvettePresent = false
trueHemeTestViewModel.messages.postValue(getString(R.string.cuvette_absentt))
this.testStatusCode = TestStatus.CUVETTE_ABSENTT.code
activity?.runOnUiThread {
@@ -1011,11 +1059,14 @@ class TrueHemeTestFragment : Fragment() {
activity?.runOnUiThread {
//changing to invisible
binding.btnSubmit.visibility = View.GONE
binding.btnSubmit.visibility = View.VISIBLE
// binding.btnSubmit.isEnabled = true
// binding.btnSubmit.isClickable = true
// binding.clParent.setBackgroundColor(Color.parseColor("#edfffd"))
autoSmbt(true)
if (doWeNeedAutoSmbt) {
binding.btnSubmit.visibility = View.GONE
autoSmbt(doWeNeedAutoSmbt)
}
binding.ivCheck.visibility = View.VISIBLE
}
@@ -1703,7 +1754,6 @@ class TrueHemeTestFragment : Fragment() {
(activity as TrueHemeTestActivity).mService.sendAndListenToHemoCube(HemoCubeCommands.CHECK_CUVETTE_COMMAND,
object : UsbServiceListener {
override fun onUsbRead(data: ByteArray?) {}
override fun onUsbError(e: Exception?) {
trueHemeTestViewModel.progressBar.postValue(false)
}
@@ -1723,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() {
trueHemeTestViewModel.progressBar.postValue(true)

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

@@ -225,6 +225,19 @@
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>
<TextView

View File

@@ -10,7 +10,7 @@
android:id="@+id/searchBar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Search by kitno or result"
android:hint="Search by Sample ID or result"
android:imeOptions="actionSearch"
android:inputType="text" />

View File

@@ -307,5 +307,6 @@
<string name="calibrate_device">Calibrate the device</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_presentnt">Test Done, Cuvette Present, Please Remove Cuvette And Goback</string>
</resources>

View File

@@ -306,7 +306,7 @@
<string name="calibrate_device">ಸಾಧನವನ್ನು ಮಾಪನಾಂಕ ಮಾಡಿ</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_presentnt">Test Done, Cuvette Present, Please Remove Cuvette And Goback</string>
<!-- Add translations for other strings -->
</resources>

View File

@@ -306,6 +306,7 @@
<string name="reset_password_for_this_device">Reset password for this 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_presentnt">Test Done, Cuvette Present, Please Remove Cuvette And Goback</string>
<string name="cuvette_absentt">Cuvette Absent,Now You Can Submit</string>
</resources>