Compare commits

...

9 Commits

Author SHA1 Message Date
Mariya
4b059bc953 fixed some issues 2024-03-11 17:33:34 +05:30
Mariya
320ceae26a fixed issues while merging 2024-03-11 17:23:28 +05:30
sanjay
08a781a706 Merge remote-tracking branch 'origin/dev-molbio-result-upload-jithu' into dev-molbio-result-upload-jithu
# Conflicts:
#	app/src/main/java/com/example/hpostesting/presentation/dashboard/HomeFragment.kt
2024-03-11 17:13:15 +05:30
sanjay
8c4fedfe28 code to decrypt and login using locally saved credentials 2024-03-11 17:08:02 +05:30
Mariya
71818e1115 Merge branch 'dev' into dev-molbio-result-upload-jithu
# Conflicts:
#	app/src/main/java/com/example/hpostesting/presentation/deviceprovision/DeviceProvisionFragment.kt
2024-03-11 15:14:44 +05:30
Mariya
d7c1508397 changed the condition for fetching device provision details from firebase 2024-03-11 13:01:58 +05:30
sanjay
3913aedefc encryption code integrated 2024-03-07 12:13:12 +05:30
sanjay
b554f64b51 setting up encryption 2024-03-07 09:55:56 +05:30
jithu
95bb428041 data upload duplication fixed-molbio & firebase 2024-03-05 17:20:18 +05:30
5 changed files with 474 additions and 251 deletions

View File

@@ -13,6 +13,12 @@ interface HemoCubeDao {
@Query("SELECT * from hemo_cube_test_table") @Query("SELECT * from hemo_cube_test_table")
fun getAll(): LiveData<List<HemoCubeTestData>> fun getAll(): LiveData<List<HemoCubeTestData>>
@Query("SELECT * from hemo_cube_test_table WHERE molbioFlag=false")
fun getMolbioPending(): List<HemoCubeTestData>
@Query("SELECT * from hemo_cube_test_table WHERE localFlag=false")
fun getFirebasePending():List<HemoCubeTestData>
@Insert(onConflict = OnConflictStrategy.REPLACE) @Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insertAll(hemoCubeTestData: HemoCubeTestData) suspend fun insertAll(hemoCubeTestData: HemoCubeTestData)

View File

@@ -0,0 +1,42 @@
package com.example.hpostesting.encryption
import android.util.Base64
import javax.crypto.Cipher
import javax.crypto.SecretKeyFactory
import javax.crypto.spec.IvParameterSpec
import javax.crypto.spec.PBEKeySpec
import javax.crypto.spec.SecretKeySpec
object Encryption {
private const val AES_MODE = "AES/CBC/PKCS5Padding"
private const val KEY_SPEC_ALGORITHM = "PBKDF2WithHmacSHA1"
private const val SALT = "Bigtec"
private const val ITERATION_COUNT = 10000
private const val KEY_LENGTH = 256
private val FIXED_IV = byteArrayOf(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15)
fun encrypt(textToEncrypt: String, password: String): String {
val salt = SALT.toByteArray()
val factory = SecretKeyFactory.getInstance(KEY_SPEC_ALGORITHM)
val spec = PBEKeySpec(password.toCharArray(), salt, ITERATION_COUNT, KEY_LENGTH)
val tmp = factory.generateSecret(spec)
val key = SecretKeySpec(tmp.encoded, "AES")
val cipher = Cipher.getInstance(AES_MODE)
cipher.init(Cipher.ENCRYPT_MODE, key, IvParameterSpec(FIXED_IV))
val encryptedBytes = cipher.doFinal(textToEncrypt.toByteArray(Charsets.UTF_8))
return Base64.encodeToString(encryptedBytes, Base64.NO_WRAP)
}
fun decrypt(encryptedText: String, password: String): String {
val salt = SALT.toByteArray()
val encryptedBytes = Base64.decode(encryptedText, Base64.NO_WRAP)
val factory = SecretKeyFactory.getInstance(KEY_SPEC_ALGORITHM)
val spec = PBEKeySpec(password.toCharArray(), salt, ITERATION_COUNT, KEY_LENGTH)
val tmp = factory.generateSecret(spec)
val key = SecretKeySpec(tmp.encoded, "AES")
val cipher = Cipher.getInstance(AES_MODE)
cipher.init(Cipher.DECRYPT_MODE, key, IvParameterSpec(FIXED_IV))
val decryptedBytes = cipher.doFinal(encryptedBytes)
return String(decryptedBytes, Charsets.UTF_8)
}
}

View File

@@ -15,6 +15,8 @@ import android.net.Uri
import android.os.BatteryManager import android.os.BatteryManager
import android.os.Build import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.os.Environment
import android.provider.Settings
import android.util.Base64 import android.util.Base64
import android.util.Log import android.util.Log
import android.view.LayoutInflater import android.view.LayoutInflater
@@ -28,6 +30,7 @@ import androidx.fragment.app.activityViewModels
import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.findNavController
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.presentation.utils.DeviceCommunicationHandler
import com.example.hpostesting.data.constant.Constants import com.example.hpostesting.data.constant.Constants
import com.example.hpostesting.data.constant.HemoCubeCommands import com.example.hpostesting.data.constant.HemoCubeCommands
import com.example.hpostesting.data.model.login.LoginRequest import com.example.hpostesting.data.model.login.LoginRequest
@@ -39,7 +42,7 @@ import com.example.hpostesting.data.model.patient.HemoCubeTestData
import com.example.hpostesting.data.model.patient.UserData import com.example.hpostesting.data.model.patient.UserData
import com.example.hpostesting.data.model.updates.CheckUpdateRequest import com.example.hpostesting.data.model.updates.CheckUpdateRequest
import com.example.hpostesting.data.model.updates.DeviceUpdateRequest import com.example.hpostesting.data.model.updates.DeviceUpdateRequest
import com.example.hpostesting.presentation.utils.DeviceCommunicationHandler import com.example.hpostesting.encryption.Encryption
import com.example.hpostesting.presentation.KitScanActivity import com.example.hpostesting.presentation.KitScanActivity
import com.example.hpostesting.presentation.utils.UsbServiceListener import com.example.hpostesting.presentation.utils.UsbServiceListener
import com.example.hpostesting.presentation.adapter.OfflineUserListAdapter import com.example.hpostesting.presentation.adapter.OfflineUserListAdapter
@@ -56,6 +59,7 @@ import com.google.firebase.perf.ktx.performance
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import `in`.sminnovations.hpostesting.R import `in`.sminnovations.hpostesting.R
import `in`.sminnovations.hpostesting.databinding.FragmentHomeBinding import `in`.sminnovations.hpostesting.databinding.FragmentHomeBinding
import kotlinx.coroutines.tasks.await
import okhttp3.ResponseBody import okhttp3.ResponseBody
import org.json.JSONObject import org.json.JSONObject
import java.io.BufferedOutputStream import java.io.BufferedOutputStream
@@ -121,8 +125,6 @@ class HomeFragment : Fragment() {
userData.forEach { userData.forEach {
if (it.testStatus == false) { if (it.testStatus == false) {
userList.add(it) userList.add(it)
}else if(it.testStatus == true){
userList.removeAll(userData)
} }
} }
val bm = requireContext().getSystemService(BATTERY_SERVICE) as BatteryManager val bm = requireContext().getSystemService(BATTERY_SERVICE) as BatteryManager
@@ -152,65 +154,71 @@ class HomeFragment : Fragment() {
} }
// Now re-subscribe to allUserData // Now re-subscribe to allUserData
hemoCubeViewModel.allLocalData.observe(viewLifecycleOwner) { originalUserDataList -> /* hemoCubeViewModel.allLocalData.observe(viewLifecycleOwner) { originalUserDataList ->
Log.d("LOCAL_DB OBSERVE", "OBSERVE CALLED") Log.d("LOCAL_DB OBSERVE", "OBSERVE CALLED")
val resultList = MolbioV2ResultRequest(mutableListOf()) val resultList = MolbioV2ResultRequest(mutableListOf())
originalUserDataList.forEach { userData -> originalUserDataList.forEach { userData ->
Log.d(": USER DATA", originalUserDataList.count().toString() + " : " + userData._id) Log.d(
var accessToken = sharedPreference.getString(Constants.ACCESS_TOKEN, "").toString() ": USER DATA",
// Upload results after processing all userData to avoid duplicates and ensure all modifications are done originalUserDataList.count().toString() + " : " + userData._id
if(accessToken.isNotEmpty()) { )
var accessToken = sharedPreference.getString(Constants.ACCESS_TOKEN, "").toString()
// Upload results after processing all userData to avoid duplicates and ensure all modifications are done
if(accessToken.isNotEmpty()) {
if (!userData.molbioFlag && isTokenAvailable && Constants.MOLBIO_INTEGRATION) {
val currentTimeFormatted = SimpleDateFormat(
"yyyy-MM-dd'T'HH:mm:ssZZZZZ",
Locale.getDefault()
).format(Calendar.getInstance().time)
val bufferIntensityThreshold =
Constants.BUFFER_INTENSITY_THRESHOLDS[userData.deviceId]?.toString()
?: "defaultThreshold" // Handle possible nulls safely
resultList.results?.add(
MolbioV2Result(
rawData = userData,
analysisId = userData._id,
analysisDate = currentTimeFormatted,
analysisStatus = userData.classificationResult
?: "defaultStatus", // Handle possible nulls
thresholds = bufferIntensityThreshold,
interpretation = userData.classificationResult
?: "defaultInterpretation", // Handle possible nulls
testId = userData._id,
testTime = currentTimeFormatted,
collectionTime = currentTimeFormatted,
expiryTime = currentTimeFormatted
)
)
}
}
}
Log.d("USER DATA LIST SIZE", resultList.results?.count().toString())
if (!userData.localFlag && userData.testStatus == true) { resultList.results?.forEach { result ->
hemoCubeViewModel.bulkAddResultTestToDb(userData) val userData = result.rawData
} Log.d("UserData", userData.toString())
if (userData != null) {
if (!userData.localFlag) {
hemoCubeViewModel.bulkAddResultTestToDb(userData)
if (!userData.molbioFlag && isTokenAvailable && Constants.MOLBIO_INTEGRATION && userData.testStatus == true) { }
val currentTimeFormatted = SimpleDateFormat( }
"yyyy-MM-dd'T'HH:mm:ssZZZZZ", }
Locale.getDefault() resultList.results?.forEach { result ->
).format(Calendar.getInstance().time) result.rawData?.let { sanitizeDoubleValues(it) }
val bufferIntensityThreshold = }
Constants.BUFFER_INTENSITY_THRESHOLDS[userData.deviceId]?.toString()
?: "defaultThreshold" // Handle possible nulls safely
resultList.results?.add(
MolbioV2Result(
rawData = userData,
analysisId = userData._id,
analysisDate = currentTimeFormatted,
analysisStatus = userData.classificationResult ?: "defaultStatus", // Handle possible nulls
thresholds = bufferIntensityThreshold,
interpretation = userData.classificationResult ?: "defaultInterpretation", // Handle possible nulls
testId = userData._id,
testTime = currentTimeFormatted,
collectionTime = currentTimeFormatted,
expiryTime = currentTimeFormatted
)
)
}
}
}
Log.d("USER DATA LIST SIZE", resultList.results?.count().toString()) // Then, check if there are any results to upload.
if (resultList.results?.isNotEmpty() == true) {
hemoCubeViewModel.uploadResult(resultList)
Log.d("resultcount1", "Uploading sanitized results")
}
resultList.results?.forEach { result -> }*/
val userData = result.rawData hemoCubeViewModel.sendDataToMolbio()
Log.d("UserData", userData.toString()) hemoCubeViewModel.sendDataToFirebase()
}
resultList.results?.forEach { result ->
result.rawData?.let { sanitizeDoubleValues(it) }
}
// Then, check if there are any results to upload.
if (resultList.results?.isNotEmpty() == true) {
hemoCubeViewModel.uploadResult(resultList)
Log.d("resultcount1", "Uploading sanitized results")
}
}
} else { } else {
binding.internetAvailableCL.visibility = View.GONE binding.internetAvailableCL.visibility = View.GONE
@@ -241,28 +249,27 @@ class HomeFragment : Fragment() {
binding.uploadData.setOnClickListener { binding.uploadData.setOnClickListener {
// showUploadDialog(requireContext()) // showUploadDialog(requireContext())
} }
hemoCubeViewModel.allUserData.observe(viewLifecycleOwner) { userData -> // hemoCubeViewModel.allUserData.observe(viewLifecycleOwner) { userData ->
if (sharedPreference.getString(Constants.USER_ID, "").toString() == "ADMIN") { //
val btnSaveLocalVisibility = // val btnSaveLocalVisibility =
if (userData.any { it.testStatus == true }) View.VISIBLE else View.GONE // if (userData.any { it.testStatus == true }) View.GONE else View.GONE
binding.downloadCSV.visibility = btnSaveLocalVisibility //
binding.downloadCSV.setOnClickListener { // binding.downloadCSV.visibility = btnSaveLocalVisibility
if (btnSaveLocalVisibility == View.VISIBLE) { //
// Execute the action when the button is visible (testStatus is true for at least one user) // binding.downloadCSV.setOnClickListener {
showDownloadDialog(requireContext()) // if (btnSaveLocalVisibility == View.VISIBLE) {
} else { // // Execute the action when the button is visible (testStatus is true for at least one user)
// Handle the case when the button is not visible // showDownloadDialog(requireContext())
Toast.makeText( // } else {
requireContext(), // // Handle the case when the button is not visible
"No test details stored locally", // Toast.makeText(
Toast.LENGTH_SHORT // requireContext(),
).show() // "No test details stored locally",
} // Toast.LENGTH_SHORT
} // ).show()
}else{ // }
binding.downloadCSV.visibility = View.GONE // }
} // }
}
binding.btnNewKit.setOnClickListener { binding.btnNewKit.setOnClickListener {
@@ -272,7 +279,7 @@ class HomeFragment : Fragment() {
apply() apply()
} }
startActivity(Intent(requireContext(), KitScanActivity::class.java)) startActivity(Intent(requireContext(), KitScanActivity::class.java))
// requireActivity().finish() requireActivity().finish()
} }
@@ -283,7 +290,7 @@ class HomeFragment : Fragment() {
apply() apply()
} }
startActivity(Intent(requireContext(), KitScanActivity::class.java)) startActivity(Intent(requireContext(), KitScanActivity::class.java))
// requireActivity().finish() requireActivity().finish()
} }
binding.btnQuickCapture.setOnClickListener { binding.btnQuickCapture.setOnClickListener {
DataHolder.hemoCubeTestData = HemoCubeTestData() DataHolder.hemoCubeTestData = HemoCubeTestData()
@@ -306,26 +313,52 @@ class HomeFragment : Fragment() {
var password = sharedPreference.getString(Constants.DEVICE_PASSWORD_API, "").toString() var password = sharedPreference.getString(Constants.DEVICE_PASSWORD_API, "").toString()
var userID = sharedPreference.getString(Constants.DEVICE_ID_API, "").toString() var userID = sharedPreference.getString(Constants.DEVICE_ID_API, "").toString()
deviceId = sharedPreference.getString(Constants.DEVICE_ID, "").toString() deviceId = sharedPreference.getString(Constants.DEVICE_ID, "").toString()
if(userID.isNotEmpty() && password.isNotEmpty()) {
Log.d("istoken",isTokenAvailable.toString())
if (!isTokenAvailable) {
Log.d("istoken1",isTokenAvailable.toString())
hemoCubeViewModel.login(createLoginRequestData(userID, password))
isTokenAvailable = true
}else if(isTokenAvailable){ val target = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)
Log.d("istoken7",isTokenAvailable.toString()) val file = File(target, "credentials.txt") //this file contains userID and password to communicate with API.
isTokenAvailable = true
hemoCubeViewModel.startPeriodicCheckUpdate() if (userID.isNotEmpty() && password.isNotEmpty()) {
hemoCubeViewModel.checkUpdate(createCheckUpdateRequestData()) Log.d("istoken", isTokenAvailable.toString())
}else{ if (!isTokenAvailable) {
if(isTokenExpired(accessToken)) { Log.d("istoken1", isTokenAvailable.toString())
Log.d("istoken8",isTokenAvailable.toString()) hemoCubeViewModel.login(createLoginRequestData(userID, password))
hemoCubeViewModel.login(createLoginRequestData(userID, password)) isTokenAvailable = true
}
} } else if (isTokenAvailable) {
Log.d("istoken7", isTokenAvailable.toString())
isTokenAvailable = true
hemoCubeViewModel.startPeriodicCheckUpdate()
hemoCubeViewModel.checkUpdate(createCheckUpdateRequestData())
} else {
if (isTokenExpired(accessToken)) {
Log.d("istoken8", isTokenAvailable.toString())
hemoCubeViewModel.login(createLoginRequestData(userID, password))
}
}
} else if (userID == "deviceIDAPI" && password == "devicePasswordAPI" && deviceId.isNotEmpty()) { } else if (userID == "deviceIDAPI" && password == "devicePasswordAPI" && deviceId.isNotEmpty()) {
fetchDeviceCredentials() fetchDeviceCredentials()
} else if (file.exists()) {
val encryptedString = file.readText()
val encryptionKey =
Settings.Secure.getString(context?.contentResolver, Settings.Secure.ANDROID_ID)
val decryptedMessage: String = Encryption.decrypt(encryptedString, encryptionKey)
val credentials = decryptedMessage.split("\n")
userID = credentials[0]
password = credentials[1]
Toast.makeText(context, "DECRYPTED: $credentials", Toast.LENGTH_SHORT).show()
if (!isTokenAvailable) {
hemoCubeViewModel.login(createLoginRequestData(userID, password))
isTokenAvailable = true
} else if (isTokenAvailable) {
isTokenAvailable = true
hemoCubeViewModel.startPeriodicCheckUpdate()
hemoCubeViewModel.checkUpdate(createCheckUpdateRequestData())
} else {
if (isTokenExpired(accessToken)) {
hemoCubeViewModel.login(createLoginRequestData(userID, password))
}
}
} else { } else {
Toast.makeText( Toast.makeText(
requireContext(), requireContext(),
@@ -340,7 +373,7 @@ class HomeFragment : Fragment() {
updateTokens(response) updateTokens(response)
response.data.data?.accessToken response.data.data?.accessToken
isTokenAvailable = true isTokenAvailable = true
Log.d("istoken2",isTokenAvailable.toString()) Log.d("istoken2", isTokenAvailable.toString())
} }
is Result.Error -> { is Result.Error -> {
@@ -366,7 +399,7 @@ class HomeFragment : Fragment() {
when (it) { when (it) {
is Result.Success -> { is Result.Success -> {
Log.d("success,","uploded") Log.d("success,", "uploded")
it.data.data?.forEach { id -> it.data.data?.forEach { id ->
id.rawData?.let { it1 -> id.rawData?.let { it1 ->
hemoCubeViewModel.updateMolbioFlag( hemoCubeViewModel.updateMolbioFlag(
@@ -374,7 +407,11 @@ class HomeFragment : Fragment() {
) )
} }
} }
Toast.makeText(activity, "Molbio Result is successfully uploaded", Toast.LENGTH_LONG) Toast.makeText(
activity,
"Molbio Result is successfully uploaded",
Toast.LENGTH_LONG
)
.show() .show()
} }
@@ -425,25 +462,29 @@ class HomeFragment : Fragment() {
hemoCubeViewModel.checkUpdate.observe(viewLifecycleOwner) { response -> hemoCubeViewModel.checkUpdate.observe(viewLifecycleOwner) { response ->
when (response) { when (response) {
is Result.Success -> { is Result.Success -> {
val updatedversion = response.data.data?.version.toString() val updatedversion = response.data.data?.version.toString()
val currentversion = val currentversion =
context?.let { ctx -> context?.let { ctx ->
val packageInfo = ctx.packageManager.getPackageInfo(ctx.packageName, 0) val packageInfo = ctx.packageManager.getPackageInfo(ctx.packageName, 0)
val versionName = packageInfo.versionName val versionName = packageInfo.versionName
val versionCode: Long = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { val versionCode: Long =
// From Android P (API level 28), versionCode is deprecated and you should use longVersionCode instead. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
packageInfo.longVersionCode // From Android P (API level 28), versionCode is deprecated and you should use longVersionCode instead.
} else { packageInfo.longVersionCode
// For older Android versions, use versionCode (cast it to Long for consistency). } else {
packageInfo.versionCode.toLong() // For older Android versions, use versionCode (cast it to Long for consistency).
} packageInfo.versionCode.toLong()
}
// Use versionName and versionCode as needed // Use versionName and versionCode as needed
Log.d("AppInfo", "Version Name: $versionName, Version Code: $versionCode") Log.d(
"AppInfo",
"Version Name: $versionName, Version Code: $versionCode"
)
} }
Log.d("versionnow",currentversion.toString()) Log.d("versionnow", currentversion.toString())
Log.d("versionnow",updatedversion.toString()) Log.d("versionnow", updatedversion.toString())
if(updatedversion > currentversion.toString()){ if (updatedversion > currentversion.toString()) {
hemoCubeViewModel.deviceUpdate(createDeviceUpdateRequestData()) hemoCubeViewModel.deviceUpdate(createDeviceUpdateRequestData())
Toast.makeText( Toast.makeText(
activity, activity,
@@ -451,7 +492,7 @@ class HomeFragment : Fragment() {
Toast.LENGTH_LONG Toast.LENGTH_LONG
) )
.show() .show()
}else{ } else {
Toast.makeText( Toast.makeText(
activity, activity,
"App is Up to date", "App is Up to date",
@@ -487,14 +528,19 @@ class HomeFragment : Fragment() {
val downloadDirectory = "NATS" val downloadDirectory = "NATS"
val fileName = "nats_certificate.zip" val fileName = "nats_certificate.zip"
val unzipDirectoryPath = requireContext().getExternalFilesDir(null)?.absolutePath + "/$downloadDirectory" val unzipDirectoryPath =
requireContext().getExternalFilesDir(null)?.absolutePath + "/$downloadDirectory"
// Check if the directory with extracted files exists. // Check if the directory with extracted files exists.
val directory = File(unzipDirectoryPath) val directory = File(unzipDirectoryPath)
if (directory.exists() && directory.isDirectory) { if (directory.exists() && directory.isDirectory) {
// Assuming if the directory exists, the certificate has been downloaded and extracted. // Assuming if the directory exists, the certificate has been downloaded and extracted.
// You can add more specific checks here, e.g., checking for specific files within the directory. // You can add more specific checks here, e.g., checking for specific files within the directory.
Toast.makeText(requireContext(), "NATS certificate already downloaded and extracted.", Toast.LENGTH_SHORT).show() Toast.makeText(
requireContext(),
"NATS certificate already downloaded and extracted.",
Toast.LENGTH_SHORT
).show()
return@observe return@observe
} }
val file = downloadFile(url, requireContext(), fileName, downloadDirectory) val file = downloadFile(url, requireContext(), fileName, downloadDirectory)
@@ -526,7 +572,7 @@ class HomeFragment : Fragment() {
private fun isTokenExpired(token: String): Boolean { private fun isTokenExpired(token: String): Boolean {
if(token.isNotEmpty()) { if (token.isNotEmpty()) {
val parts = token.split("\\.".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray() val parts = token.split("\\.".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
val decodedPayload = String(Base64.decode(parts[1], Base64.DEFAULT)) val decodedPayload = String(Base64.decode(parts[1], Base64.DEFAULT))
val jsonPayload = JSONObject(decodedPayload) val jsonPayload = JSONObject(decodedPayload)
@@ -535,7 +581,7 @@ class HomeFragment : Fragment() {
val currentTimeSeconds = System.currentTimeMillis() / 1000 val currentTimeSeconds = System.currentTimeMillis() / 1000
return exp <= currentTimeSeconds return exp <= currentTimeSeconds
}else{ } else {
return false return false
} }
} }
@@ -550,7 +596,7 @@ class HomeFragment : Fragment() {
apply() apply()
} }
isTokenAvailable = true isTokenAvailable = true
Log.d("istoken3",isTokenAvailable.toString()) Log.d("istoken3", isTokenAvailable.toString())
} }
private fun createLoginRequestData(userID: String, password: String): LoginRequest { private fun createLoginRequestData(userID: String, password: String): LoginRequest {
@@ -693,9 +739,9 @@ class HomeFragment : Fragment() {
putString(Constants.NATS_TOKEN, natsToken) putString(Constants.NATS_TOKEN, natsToken)
apply() apply()
} }
if (!isTokenAvailable ) { if (!isTokenAvailable) {
Log.d("istoken0", isTokenAvailable.toString()) Log.d("istoken0", isTokenAvailable.toString())
hemoCubeViewModel.login(createLoginRequestData(username, password)) hemoCubeViewModel.login(createLoginRequestData(username, password))
} }
} ?: Log.e("fetchDeviceCredentials", "Failed to parse device data.") } ?: Log.e("fetchDeviceCredentials", "Failed to parse device data.")
@@ -879,15 +925,11 @@ class HomeFragment : Fragment() {
} }
private fun checkUnprocessedCSVData() { private fun checkUnprocessedCSVData() {
hemoCubeViewModel.allUserData.observe(viewLifecycleOwner) { userDataList -> // hemoCubeViewModel.allUserData.observe(viewLifecycleOwner) { userDataList ->
if (sharedPreference.getString(Constants.USER_ID, "").toString() == "ADMIN") { // val downloadDataVisibility =
val downloadDataVisibility = // if (userDataList.any { !it.isCSVCreated && it.testStatus == true }) View.GONE else View.GONE
if (userDataList.any { !it.isCSVCreated && it.testStatus == true }) View.VISIBLE else View.GONE // binding.downloadCSV.visibility = downloadDataVisibility
binding.downloadCSV.visibility = downloadDataVisibility // }
}else{
binding.downloadCSV.visibility = View.GONE
}
}
} }
private fun sanitizeDoubleValues(hemoCubeTestData: HemoCubeTestData): HemoCubeTestData { private fun sanitizeDoubleValues(hemoCubeTestData: HemoCubeTestData): HemoCubeTestData {
@@ -904,7 +946,6 @@ class HomeFragment : Fragment() {
} }
private fun showUploadDialog(context: Context) { private fun showUploadDialog(context: Context) {
val builder = AlertDialog.Builder(context) val builder = AlertDialog.Builder(context)
builder.setTitle(R.string.upload_db_registration_title) builder.setTitle(R.string.upload_db_registration_title)
@@ -922,22 +963,22 @@ class HomeFragment : Fragment() {
dialog.show() dialog.show()
} }
private fun showDownloadDialog(context: Context) { // private fun showDownloadDialog(context: Context) {
val builder = AlertDialog.Builder(context) // val builder = AlertDialog.Builder(context)
builder.setTitle(R.string.download_db_registration_title) // builder.setTitle(R.string.download_db_registration_title)
builder.setMessage(R.string.download_db_registration_message) // builder.setMessage(R.string.download_db_registration_message)
//
builder.setPositiveButton(R.string.downloadcsv) { dialog, _ -> // builder.setPositiveButton(R.string.downloadcsv) { dialog, _ ->
downloadLocalDBData(dialog) // downloadLocalDBData(dialog)
} // }
//
builder.setNegativeButton(R.string.cancel) { dialog, _ -> // builder.setNegativeButton(R.string.cancel) { dialog, _ ->
dialog.dismiss() // dialog.dismiss()
} // }
//
val dialog = builder.create() // val dialog = builder.create()
dialog.show() // dialog.show()
} // }
private fun uploadLocalDBData(dialog: DialogInterface) { private fun uploadLocalDBData(dialog: DialogInterface) {
// viewModel.allUserData.observe(viewLifecycleOwner) { userDataList -> // viewModel.allUserData.observe(viewLifecycleOwner) { userDataList ->
@@ -999,25 +1040,27 @@ class HomeFragment : Fragment() {
} }
} }
private fun downloadLocalDBData(dialog: DialogInterface) { // private fun downloadLocalDBData(dialog: DialogInterface) {
hemoCubeViewModel.allUserData.observe(viewLifecycleOwner) { userDataList -> // hemoCubeViewModel.allUserData.observe(viewLifecycleOwner) { userDataList ->
val downloadList = mutableListOf<HemoCubeTestData>() // val downloadList = mutableListOf<HemoCubeTestData>()
//
userDataList.forEach { userData -> // userDataList.forEach { userData ->
if (!userData.isCSVCreated) { // if (!userData.isCSVCreated) {
downloadList.add(userData) // Add the userData to downloadList // downloadList.add(userData) // Add the userData to downloadList
} // }
} // }
//
if (downloadList.isNotEmpty()) { // if (downloadList.isNotEmpty()) {
// Call ViewModel function to create CSV with filtered data // // Call ViewModel function to create CSV with filtered data
hemoCubeViewModel.createCSV(downloadList, requireContext()) // hemoCubeViewModel.createCSV(downloadList, requireContext())
Toast.makeText(requireContext(), "Done", Toast.LENGTH_SHORT).show() // Toast.makeText(requireContext(), "Done", Toast.LENGTH_SHORT).show()
} // } else {
// Toast.makeText(requireContext(), "Failed", Toast.LENGTH_SHORT).show()
dialog.dismiss() // }
} //
} // dialog.dismiss()
// }
// }
private fun deleteIncompleteRegistrations(userDataList: List<UserData>) { private fun deleteIncompleteRegistrations(userDataList: List<UserData>) {
userDataList.forEach { userData -> userDataList.forEach { userData ->
@@ -1053,22 +1096,22 @@ class HomeFragment : Fragment() {
} }
} }
// private fun showDownloadDialog(context: Context) { private fun showDownloadDialog(context: Context) {
// val builder = AlertDialog.Builder(context) val builder = AlertDialog.Builder(context)
// builder.setTitle(R.string.download_db_registration_title) builder.setTitle(R.string.download_db_registration_title)
// builder.setMessage(R.string.download_db_registration_message) builder.setMessage(R.string.download_db_registration_message)
//
// builder.setPositiveButton(R.string.downloadcsv) { dialog, _ -> builder.setPositiveButton(R.string.downloadcsv) { dialog, _ ->
// downloadLocalDBData(dialog) // downloadLocalDBData(dialog)
// } }
//
// builder.setNegativeButton(R.string.cancel) { dialog, _ -> builder.setNegativeButton(R.string.cancel) { dialog, _ ->
// dialog.dismiss() dialog.dismiss()
// } }
//
// val dialog = builder.create() val dialog = builder.create()
// dialog.show() dialog.show()
// } }
// private fun downloadLocalDBData(dialog: DialogInterface) { // private fun downloadLocalDBData(dialog: DialogInterface) {
// var csvDownloaded = false // var csvDownloaded = false
@@ -1115,7 +1158,7 @@ class HomeFragment : Fragment() {
// } // }
private fun getDeviceId() { private fun getDeviceId() {
Log.d("HomeFragmentUSb","getDeviceId") Log.d("HomeFragmentUSb", "getDeviceId")
val handler = activity as? DeviceCommunicationHandler val handler = activity as? DeviceCommunicationHandler
handler?.sendAndListenToDevice( handler?.sendAndListenToDevice(
HemoCubeCommands.DEVICE_CONFIGURATION_COMMAND, HemoCubeCommands.DEVICE_CONFIGURATION_COMMAND,
@@ -1123,9 +1166,10 @@ class HomeFragment : Fragment() {
override fun onUsbRead(data: ByteArray?) { override fun onUsbRead(data: ByteArray?) {
data?.let { data?.let {
val receivedData = String(it, Charset.forName("UTF-8")) val receivedData = String(it, Charset.forName("UTF-8"))
Log.d("HomeFragment","USB data"+receivedData) Log.d("HomeFragment", "USB data" + receivedData)
// Assuming the device ID is the full content of the received data. Adjust if needed. // Assuming the device ID is the full content of the received data. Adjust if needed.
deviceId = extractDeviceId(receivedData) // Implement this method based on your data format. deviceId =
extractDeviceId(receivedData) // Implement this method based on your data format.
if (deviceId.isNotEmpty()) { if (deviceId.isNotEmpty()) {
// Store the deviceId in SharedPreferences // Store the deviceId in SharedPreferences
with(sharedPreference.edit()) { with(sharedPreference.edit()) {
@@ -1142,7 +1186,7 @@ class HomeFragment : Fragment() {
override fun onUsbError(e: Exception?) { override fun onUsbError(e: Exception?) {
// Handle USB communication error // Handle USB communication error
Log.d("HomeFragment","USB read error"+e.toString()) Log.d("HomeFragment", "USB read error" + e.toString())
} }
}) })
@@ -1154,94 +1198,103 @@ class HomeFragment : Fragment() {
val matchResult = regex.find(receivedData) val matchResult = regex.find(receivedData)
return matchResult?.groups?.get(1)?.value ?: "" return matchResult?.groups?.get(1)?.value ?: ""
} }
@SuppressLint("SuspiciousIndentation") @SuppressLint("SuspiciousIndentation")
private fun checkForUpdate() { private fun checkForUpdate() {
try { try {
val db = Firebase.firestore val db = Firebase.firestore
//val deviceId = deviceId //val deviceId = deviceId
val deviceRef = db.collection("deviceUpdate").document(Constants.DOCUMENT_ID_FOR_UPDATE) val deviceRef = db.collection("deviceUpdate").document(Constants.DOCUMENT_ID_FOR_UPDATE)
deviceRef.get().addOnSuccessListener { documentSnapshot -> deviceRef.get().addOnSuccessListener { documentSnapshot ->
if (documentSnapshot.exists()) { if (documentSnapshot.exists()) {
val deviceData = val deviceData =
documentSnapshot.toObject(DeviceData::class.java) documentSnapshot.toObject(DeviceData::class.java)
// deviceData?.let { data -> // deviceData?.let { data ->
val deviceVersion = deviceData!!.deviceVersion val deviceVersion = deviceData!!.deviceVersion
val deviceUpdateAvailableGlobal= deviceData.deviceUpdateAvailable val deviceUpdateAvailableGlobal = deviceData.deviceUpdateAvailable
val updatePathGlobal= deviceData.updatePath val updatePathGlobal = deviceData.updatePath
db.collection("devices").whereEqualTo("deviceId", deviceId).get().addOnSuccessListener { documentSnapshotNew -> db.collection("devices").whereEqualTo("deviceId", deviceId).get()
if (documentSnapshotNew.documents.isNotEmpty()) { .addOnSuccessListener { documentSnapshotNew ->
documentSnapshotNew.documents.forEach{ if (documentSnapshotNew.documents.isNotEmpty()) {
val documentIn = it.toObject(DeviceData::class.java) documentSnapshotNew.documents.forEach {
val documentIn = it.toObject(DeviceData::class.java)
val globalUpdateIgnore = documentIn!!.globalUpdateIgnore val globalUpdateIgnore = documentIn!!.globalUpdateIgnore
val deviceUpdateAvailable = documentIn.deviceUpdateAvailable val deviceUpdateAvailable = documentIn.deviceUpdateAvailable
val globalUpdateDone = documentIn.globalUpdateDone val globalUpdateDone = documentIn.globalUpdateDone
val updatePath = documentIn.updatePath val updatePath = documentIn.updatePath
if(globalUpdateIgnore){ if (globalUpdateIgnore) {
if(deviceUpdateAvailable){ if (deviceUpdateAvailable) {
val update = db.collection("devices").document(it.id).update("deviceUpdateAvailable",false) val update = db.collection("devices").document(it.id)
update.addOnSuccessListener { .update("deviceUpdateAvailable", false)
Log.d("HomeFragmentUpdate","Device local update done") update.addOnSuccessListener {
Log.d(
"HomeFragmentUpdate",
"Device local update done"
)
initiateUpdate(updatePath) initiateUpdate(updatePath)
}.addOnFailureListener{ }.addOnFailureListener {
Log.e("fetchDeviceUpdate", "update fail.") Log.e("fetchDeviceUpdate", "update fail.")
} }
}else{ } else {
Log.d("HomeFragmentUpdate","Device update not available") Log.d(
} "HomeFragmentUpdate",
}else{ "Device update not available"
if(deviceUpdateAvailableGlobal) { )
if (!globalUpdateDone) { }
val update = } else {
db.collection("devices").document(it.id) if (deviceUpdateAvailableGlobal) {
.update("globalUpdateDone", true) if (!globalUpdateDone) {
update.addOnSuccessListener { val update =
Log.d( db.collection("devices").document(it.id)
"HomeFragmentUpdate", .update("globalUpdateDone", true)
"Device global update done" update.addOnSuccessListener {
) Log.d(
"HomeFragmentUpdate",
"Device global update done"
)
initiateUpdate(updatePathGlobal) initiateUpdate(updatePathGlobal)
}.addOnFailureListener { }.addOnFailureListener {
Log.e( Log.e(
"fetchDeviceUpdate", "fetchDeviceUpdate",
"update fail." "update fail."
) )
}
}
} }
} }
} }
} else {
Log.e("fetchDeviceUpdate", "Document does not exist.")
} }
}.addOnFailureListener { exception ->
Log.e("fetchDeviceUpdate", "Error fetching device data", exception)
} }
} else {
// Toast.makeText(requireActivity()," true -version."+deviceVersion+"updatePath.."+updatePath,Toast.LENGTH_LONG).show() Log.e("fetchDeviceUpdate", "Document does not exist.")
}
}.addOnFailureListener { exception ->
Log.e("fetchDeviceUpdate", "Error fetching device data", exception)
}
// Log for debugging // Toast.makeText(requireActivity()," true -version."+deviceVersion+"updatePath.."+updatePath,Toast.LENGTH_LONG).show()
Log.d(
"fetchDeviceCredentials",
"deviceVersion: $deviceVersion, Password: $deviceUpdateAvailableGlobal, updatePath: $updatePathGlobal" // Log for debugging
) Log.d(
"fetchDeviceCredentials",
"deviceVersion: $deviceVersion, Password: $deviceUpdateAvailableGlobal, updatePath: $updatePathGlobal"
)
// ?: Log.e("fetchDeviceUpdate", "Failed to parse device data.") // ?: Log.e("fetchDeviceUpdate", "Failed to parse device data.")
} else { } else {
Log.e("fetchDeviceUpdate", "Document does not exist.") Log.e("fetchDeviceUpdate", "Document does not exist.")
}
} }
}
.addOnFailureListener { exception -> .addOnFailureListener { exception ->
Log.e("fetchDeviceUpdate", "Error fetching device data", exception) Log.e("fetchDeviceUpdate", "Error fetching device data", exception)
} }
@@ -1263,7 +1316,8 @@ class HomeFragment : Fragment() {
request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED) request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED)
request.setDestinationInExternalFilesDir(requireActivity(), "Updates", "update.apk") request.setDestinationInExternalFilesDir(requireActivity(), "Updates", "update.apk")
val downloadManager = requireActivity().getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager val downloadManager =
requireActivity().getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager
downloadId = downloadManager.enqueue(request) downloadId = downloadManager.enqueue(request)
// Register a BroadcastReceiver to receive the download complete event // Register a BroadcastReceiver to receive the download complete event
@@ -1273,12 +1327,15 @@ class HomeFragment : Fragment() {
requireActivity().registerReceiver(downloadReceiver, filter, RECEIVER_EXPORTED) requireActivity().registerReceiver(downloadReceiver, filter, RECEIVER_EXPORTED)
} }
} }
private fun extractApkUrl(responseBody: ResponseBody): String { private fun extractApkUrl(responseBody: ResponseBody): String {
return responseBody.string() return responseBody.string()
} }
private fun isValidHttpUrl(url: String): Boolean { private fun isValidHttpUrl(url: String): Boolean {
return url.startsWith("http://") || url.startsWith("https://") return url.startsWith("http://") || url.startsWith("https://")
} }
private val downloadReceiver = object : BroadcastReceiver() { private val downloadReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) { override fun onReceive(context: Context?, intent: Intent?) {
val id = intent?.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1) val id = intent?.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1)
@@ -1292,8 +1349,11 @@ class HomeFragment : Fragment() {
val file = File(requireActivity().getExternalFilesDir("Updates"), "update.apk") val file = File(requireActivity().getExternalFilesDir("Updates"), "update.apk")
file.setReadable(true, false) // Ensure the file is readable file.setReadable(true, false) // Ensure the file is readable
val pInfo = requireActivity().baseContext.packageManager.getPackageInfo(requireActivity().baseContext.packageName, 0) val pInfo = requireActivity().baseContext.packageManager.getPackageInfo(
Log.d("HomeFragmentShowInfo",pInfo.packageName.toString()) requireActivity().baseContext.packageName,
0
)
Log.d("HomeFragmentShowInfo", pInfo.packageName.toString())
val uri: Uri = FileProvider.getUriForFile( val uri: Uri = FileProvider.getUriForFile(
requireActivity(), requireActivity(),
"${pInfo.packageName}.fileprovider", "${pInfo.packageName}.fileprovider",
@@ -1316,7 +1376,7 @@ class HomeFragment : Fragment() {
} }
override fun onDestroy() { override fun onDestroy() {
if(isRegistered) { if (isRegistered) {
try { try {
requireActivity().unregisterReceiver(downloadReceiver) requireActivity().unregisterReceiver(downloadReceiver)
} catch (e: Exception) { } catch (e: Exception) {

View File

@@ -4,6 +4,8 @@ import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.SharedPreferences import android.content.SharedPreferences
import android.os.Bundle import android.os.Bundle
import android.os.Environment
import android.provider.Settings
import android.util.Log import android.util.Log
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
@@ -17,11 +19,15 @@ import com.example.hpostesting.data.constant.Constants
import com.example.hpostesting.data.constant.HemoCubeCommands import com.example.hpostesting.data.constant.HemoCubeCommands
import com.example.hpostesting.data.model.deviceprovision.DeviceProvisionRequest import com.example.hpostesting.data.model.deviceprovision.DeviceProvisionRequest
import com.example.hpostesting.data.model.patient.DeviceData import com.example.hpostesting.data.model.patient.DeviceData
import com.example.hpostesting.encryption.Encryption
import com.example.hpostesting.presentation.utils.UsbServiceListener import com.example.hpostesting.presentation.utils.UsbServiceListener
import com.example.hpostesting.presentation.dashboard.DashboardActivity import com.example.hpostesting.presentation.dashboard.DashboardActivity
import com.google.android.gms.ads.identifier.AdvertisingIdClient
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 java.io.File
import java.io.FileOutputStream
class DeviceProvisionFragment : Fragment() { class DeviceProvisionFragment : Fragment() {
private var resultData: String = "" private var resultData: String = ""
@@ -122,6 +128,10 @@ class DeviceProvisionFragment : Fragment() {
deviceUpdateAvailable = false deviceUpdateAvailable = false
) )
) )
encryptAndSaveToFile(
response.data.data?.credentials?.username.toString(),
response.data.data?.credentials?.password.toString()
)
// viewModel.addDeviceId(DeviceData(deviceId = sharedPreferences.getString(Constants.DEVICE_ID, "").toString())) // viewModel.addDeviceId(DeviceData(deviceId = sharedPreferences.getString(Constants.DEVICE_ID, "").toString()))
Log.e("idpass", response.toString()) Log.e("idpass", response.toString())
Log.e("idpass", response.data.data?.credentials?.username.toString()) Log.e("idpass", response.data.data?.credentials?.username.toString())
@@ -221,4 +231,25 @@ class DeviceProvisionFragment : Fragment() {
} }
} }
} }
private fun encryptAndSaveToFile(username: String, password: String) {
val messageToEncrypt = "$username\n$password"
val encryptionKey =
Settings.Secure.getString(context?.contentResolver, Settings.Secure.ANDROID_ID)
val encryptedString = Encryption.encrypt(messageToEncrypt, encryptionKey)
Log.d("DEVICE ID/encryptionKey", encryptionKey)
val target = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)
val file = File(target, "credentials.txt")
if (!file.exists()) {
file.createNewFile()
}
file.writeText(encryptedString)
Log.d("Encrypted Message", encryptedString)
}
} }

View File

@@ -38,6 +38,7 @@ import com.example.hpostesting.data.repository.Repository
import com.example.hpostesting.domain.CheckUpdateWorker import com.example.hpostesting.domain.CheckUpdateWorker
import com.example.hpostesting.domain.LogFileManager import com.example.hpostesting.domain.LogFileManager
import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import okhttp3.MediaType.Companion.toMediaTypeOrNull import okhttp3.MediaType.Companion.toMediaTypeOrNull
import okhttp3.MultipartBody import okhttp3.MultipartBody
@@ -152,6 +153,89 @@ class HemoCubeViewModel @Inject constructor(
} }
} }
fun sendDataToMolbio() = viewModelScope.launch(Dispatchers.IO) {
Log.d("molbio","getting in sendMolbio")
val resultList = MolbioV2ResultRequest(mutableListOf())
val pendingData = hemoCubeDao.getMolbioPending()
Log.d("molbio","pending size"+pendingData.size)
pendingData.forEach{
userData ->
val currentTimeFormatted = SimpleDateFormat(
"yyyy-MM-dd'T'HH:mm:ssZZZZZ",
Locale.getDefault()
).format(Calendar.getInstance().time)
val bufferIntensityThreshold =
Constants.BUFFER_INTENSITY_THRESHOLDS[userData.deviceId]?.toString()
?: "defaultThreshold" // Handle possible nulls safely
resultList.results?.add(
MolbioV2Result(
rawData = userData,
analysisId = userData._id,
analysisDate = currentTimeFormatted,
analysisStatus = userData.classificationResult
?: "defaultStatus", // Handle possible nulls
thresholds = bufferIntensityThreshold,
interpretation = userData.classificationResult
?: "defaultInterpretation", // Handle possible nulls
testId = userData._id,
testTime = currentTimeFormatted,
collectionTime = currentTimeFormatted,
expiryTime = currentTimeFormatted
)
)
}
resultList.results?.forEach { result ->
result.rawData?.let { sanitizeDoubleValues(it) }
}
if (resultList.results?.isNotEmpty() == true) {
uploadMoblioBulkResults(resultList)
}
}
fun uploadMoblioBulkResults(molbioV2ResultRequest: MolbioV2ResultRequest) = viewModelScope.launch {
Log.d("molbio","upload called")
repository.uploadResults(molbioV2ResultRequest).let {
when (it) {
is Result.Success -> {
it.data.data?.forEach { id ->
id.rawData?.let { it1 ->
updateMolbioFlag(
it1._id
)
}
}
}
is Result.Error -> {
Log.d("result","result upload error")
}
else -> {
Log.d("result","result upload else")
}
}
}
}
fun sendDataToFirebase() = viewModelScope.launch ( Dispatchers.IO ) {
val pendingData = hemoCubeDao.getFirebasePending()
pendingData.forEach{
userData ->
if (userData != null) {
if (!userData.localFlag) {
bulkAddResultTestToDb(userData)
}
}
}
}
fun checkUpdate(checkUpdateRequest: CheckUpdateRequest) = viewModelScope.launch { fun checkUpdate(checkUpdateRequest: CheckUpdateRequest) = viewModelScope.launch {
checkUpdate.postValue(Result.Loading()) checkUpdate.postValue(Result.Loading())
repository.checkUpdate(checkUpdateRequest).let { repository.checkUpdate(checkUpdateRequest).let {