diff --git a/app/build.gradle b/app/build.gradle index 0a45e20..3279445 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -132,6 +132,7 @@ dependencies { // Retrofit + GSON implementation "com.squareup.retrofit2:retrofit:2.9.0" implementation "com.squareup.retrofit2:converter-gson:2.9.0" + implementation("com.squareup.okhttp3:okhttp:4.9.3") implementation "androidx.preference:preference-ktx:1.2.1" diff --git a/app/src/main/java/com/example/hpostesting/data/api/HPOSApi.kt b/app/src/main/java/com/example/hpostesting/data/api/MolbioAuthApi.kt similarity index 52% rename from app/src/main/java/com/example/hpostesting/data/api/HPOSApi.kt rename to app/src/main/java/com/example/hpostesting/data/api/MolbioAuthApi.kt index a1ff1b0..1165024 100644 --- a/app/src/main/java/com/example/hpostesting/data/api/HPOSApi.kt +++ b/app/src/main/java/com/example/hpostesting/data/api/MolbioAuthApi.kt @@ -2,22 +2,12 @@ package com.example.hpostesting.data.api import com.example.hpostesting.data.model.deviceprovision.DeviceProvisionRequest import com.example.hpostesting.data.model.deviceprovision.DeviceProvisionResponse -import com.example.hpostesting.data.model.hpostest.HPOSV2TestResult import com.example.hpostesting.data.model.login.LoginRequest import com.example.hpostesting.data.model.login.LoginResponse -import com.example.hpostesting.data.model.patient.HemoCubeTestData -import retrofit2.Response import retrofit2.http.Body -import retrofit2.http.Header import retrofit2.http.POST -interface HPOSApi { - - @POST("uploadResults/") - suspend fun uploadResults( - @Header("Authorization") accessToken: String, - @Body entry: HPOSV2TestResult - ): Response +interface MolbioAuthApi { @POST("deviceManagement/device/provision") suspend fun deviceProvision( @@ -28,14 +18,4 @@ interface HPOSApi { suspend fun login( @Body loginRequest: LoginRequest ): LoginResponse - - @POST("CheckUpdate/") - suspend fun checkUpdate( - @Body entry: HemoCubeTestData - ): Response - - @POST("uploadLogs/") - suspend fun uploadLogs( - @Body entry: HemoCubeTestData - ): Response } \ No newline at end of file diff --git a/app/src/main/java/com/example/hpostesting/data/api/MolbioResultApi.kt b/app/src/main/java/com/example/hpostesting/data/api/MolbioResultApi.kt new file mode 100644 index 0000000..0b653bb --- /dev/null +++ b/app/src/main/java/com/example/hpostesting/data/api/MolbioResultApi.kt @@ -0,0 +1,39 @@ +package com.example.hpostesting.data.api + +import com.example.hpostesting.data.model.log.UploadLogsResponse +import com.example.hpostesting.data.model.molbioresult.MolbioV2ResultRequest +import com.example.hpostesting.data.model.molbioresult.MolbioV2ResultResponse +import com.example.hpostesting.data.model.updates.CheckUpdateRequest +import com.example.hpostesting.data.model.updates.CheckUpdateResponse +import com.example.hpostesting.data.model.updates.DeviceUpdateRequest +import okhttp3.MultipartBody +import okhttp3.ResponseBody +import retrofit2.http.Body +import retrofit2.http.Multipart +import retrofit2.http.POST +import retrofit2.http.PUT +import retrofit2.http.Part + +interface MolbioResultApi { + + @PUT("deviceService/results/HPOS") + suspend fun uploadResults( + @Body molbioV2ResultRequest: MolbioV2ResultRequest + ): MolbioV2ResultResponse + + @POST("deviceService/device/checkUpdate") + suspend fun checkUpdate( + @Body checkUpdateRequest: CheckUpdateRequest + ): CheckUpdateResponse + + @POST("deviceService/device/getUpdate") + suspend fun deviceUpdate( + @Body deviceUpdateRequest: DeviceUpdateRequest + ): ResponseBody + + @Multipart + @POST("deviceService/device/uploadLogs") + suspend fun uploadLogs( + @Part logFile: MultipartBody.Part + ): UploadLogsResponse +} \ No newline at end of file diff --git a/app/src/main/java/com/example/hpostesting/data/dao/MyDataBase.kt b/app/src/main/java/com/example/hpostesting/data/dao/MyDataBase.kt index 49115b7..b291c72 100644 --- a/app/src/main/java/com/example/hpostesting/data/dao/MyDataBase.kt +++ b/app/src/main/java/com/example/hpostesting/data/dao/MyDataBase.kt @@ -11,7 +11,7 @@ import com.example.hpostesting.data.model.patient.HemoCubeTestData import com.google.android.datatransport.runtime.dagger.Provides import javax.inject.Singleton -@Database(entities = [UserData::class, HemoCubeTestData::class, DeviceData::class], version = 13, exportSchema = false) +@Database(entities = [UserData::class, HemoCubeTestData::class, DeviceData::class], version = 14, exportSchema = false) @TypeConverters(Converters::class) abstract class MyDatabase : RoomDatabase() { abstract fun userDao(): UserDao diff --git a/app/src/main/java/com/example/hpostesting/data/model/hpostest/HPOSV2TestResult.kt b/app/src/main/java/com/example/hpostesting/data/model/hpostest/HPOSV2TestResult.kt deleted file mode 100644 index 087f82a..0000000 --- a/app/src/main/java/com/example/hpostesting/data/model/hpostest/HPOSV2TestResult.kt +++ /dev/null @@ -1,6 +0,0 @@ -package com.example.hpostesting.data.model.hpostest - -data class HPOSV2TestResult( - val device_id: String? = "", - val results: List? = listOf() -) \ No newline at end of file diff --git a/app/src/main/java/com/example/hpostesting/data/model/hpostest/Result.kt b/app/src/main/java/com/example/hpostesting/data/model/hpostest/Result.kt deleted file mode 100644 index ae1b092..0000000 --- a/app/src/main/java/com/example/hpostesting/data/model/hpostest/Result.kt +++ /dev/null @@ -1,36 +0,0 @@ -package com.example.hpostesting.data.model.hpostest - -data class Result( - val age: String? = "", - val analysis_date: String? = "", - val analysis_id: String? = "", - val analysis_status: String? = "", - val analysis_type: String? = "", - val analysis_type_method: String? = "", - val blood_group: String? = "", - val coefficients: List? = listOf(), - val collection_location: List? = listOf(), - val collection_time: String? = "", - val collector: String? = "", - val curve_fitting: String? = "", - val device_id: String? = "", - val device_name: String? = "", - val expiry_time: String? = "", - val gender: String? = "", - val interpretation: String? = "", - val patient_id: String? = "", - val pregnancy: Boolean? = false, - val raw_data: RawData? = RawData(), - val recommendation: String? = "", - val sample_id: String? = "", - val sample_type: String? = "", - val sickle_cell_history: Boolean? = false, - val test_id: String? = "", - val test_result: String? = "", - val test_status: String? = "", - val test_time: String? = "", - val test_type: String? = "", - val thresholds: String? = "", - val under_medication: Boolean? = false, - val volume: String? = "" -) \ No newline at end of file diff --git a/app/src/main/java/com/example/hpostesting/data/model/log/UploadLogsData.kt b/app/src/main/java/com/example/hpostesting/data/model/log/UploadLogsData.kt new file mode 100644 index 0000000..f1ad239 --- /dev/null +++ b/app/src/main/java/com/example/hpostesting/data/model/log/UploadLogsData.kt @@ -0,0 +1,5 @@ +package com.example.hpostesting.data.model.log + +data class UploadLogsData( + val filename: String? = "" +) \ No newline at end of file diff --git a/app/src/main/java/com/example/hpostesting/data/model/log/UploadLogsResponse.kt b/app/src/main/java/com/example/hpostesting/data/model/log/UploadLogsResponse.kt new file mode 100644 index 0000000..3544696 --- /dev/null +++ b/app/src/main/java/com/example/hpostesting/data/model/log/UploadLogsResponse.kt @@ -0,0 +1,12 @@ +package com.example.hpostesting.data.model.log + +import com.google.gson.annotations.SerializedName + +data class UploadLogsResponse( + @SerializedName("Data") + val data: UploadLogsData? = UploadLogsData(), + @SerializedName("Message") + val message: String? = "", + @SerializedName("Result") + val result: String? = "" +) \ No newline at end of file diff --git a/app/src/main/java/com/example/hpostesting/data/model/molbioresult/MolbioV2Result.kt b/app/src/main/java/com/example/hpostesting/data/model/molbioresult/MolbioV2Result.kt new file mode 100644 index 0000000..23d19b8 --- /dev/null +++ b/app/src/main/java/com/example/hpostesting/data/model/molbioresult/MolbioV2Result.kt @@ -0,0 +1,38 @@ +package com.example.hpostesting.data.model.molbioresult + +import com.example.hpostesting.data.model.patient.HemoCubeTestData + +data class MolbioV2Result( + val age: Int? = 0, + val analysisDate: String? = "", + val analysisId: String? = "", + val analysisStatus: String? = "", + val analysisType: String? = "", + val analysisTypeMethod: String? = "", + val bloodGroup: String? = "", + val coefficients: List? = listOf(), + val collectionLocation: List? = listOf(), + val collectionTime: String? = "", + val collector: String? = "", + val curveFitting: String? = "", + val deviceName: String? = "", + val expiryTime: String? = "", + val gender: String? = "", + val interpretation: String? = "", + val `operator`: String? = "", + val patientId: Int? = 0, + val pregnancy: Boolean? = false, + val rawData: HemoCubeTestData? = HemoCubeTestData(), + val recommendation: String? = "", + val sampleId: String? = "", + val sampleType: String? = "", + val sickleCellHistory: Boolean? = false, + val testId: String? = "", + val testResult: String? = "", + val testStatus: String? = "", + val testTime: String? = "", + val testType: String? = "", + val thresholds: String? = "", + val underMedication: Boolean? = false, + val volume: Int? = 0 +) \ No newline at end of file diff --git a/app/src/main/java/com/example/hpostesting/data/model/molbioresult/MolbioV2ResultData.kt b/app/src/main/java/com/example/hpostesting/data/model/molbioresult/MolbioV2ResultData.kt new file mode 100644 index 0000000..c6168b4 --- /dev/null +++ b/app/src/main/java/com/example/hpostesting/data/model/molbioresult/MolbioV2ResultData.kt @@ -0,0 +1,42 @@ +package com.example.hpostesting.data.model.molbioresult + +import com.example.hpostesting.data.model.patient.HemoCubeTestData + +data class MolbioV2ResultData( + val age: Int? = 0, + val analysisDate: String? = "", + val analysisStatus: String? = "", + val analysisType: String? = "", + val analysisTypeMethod: String? = "", + val bloodGroup: String? = "", + val coefficients: List? = listOf(), + val collectionLocation: List? = listOf(), + val collectionTime: String? = "", + val collector: String? = "", + val createdAt: String? = "", + val createdBy: Int? = 0, + val curveFitting: String? = "", + val deviceId: Int? = 0, + val expiryTime: String? = "", + val gender: String? = "", + val id: Int? = 0, + val interpretation: String? = "", + val `operator`: String? = "", + val patientId: Int? = 0, + val pregnancy: Boolean? = false, + val rawData: HemoCubeTestData? = HemoCubeTestData(), + val recommendation: String? = "", + val sampleId: String? = "", + val sampleType: String? = "", + val sickleCellHistory: Boolean? = false, + val testId: String? = "", + val testResult: String? = "", + val testStatus: String? = "", + val testTime: String? = "", + val testType: String? = "", + val thresholds: String? = "", + val underMedication: Boolean? = false, + val updatedAt: String? = "", + val updatedBy: Int? = 0, + val volume: Int? = 0 +) \ No newline at end of file diff --git a/app/src/main/java/com/example/hpostesting/data/model/molbioresult/MolbioV2ResultRequest.kt b/app/src/main/java/com/example/hpostesting/data/model/molbioresult/MolbioV2ResultRequest.kt new file mode 100644 index 0000000..43105f3 --- /dev/null +++ b/app/src/main/java/com/example/hpostesting/data/model/molbioresult/MolbioV2ResultRequest.kt @@ -0,0 +1,5 @@ +package com.example.hpostesting.data.model.molbioresult + +data class MolbioV2ResultRequest( + val results: List? = listOf() +) \ No newline at end of file diff --git a/app/src/main/java/com/example/hpostesting/data/model/molbioresult/MolbioV2ResultResponse.kt b/app/src/main/java/com/example/hpostesting/data/model/molbioresult/MolbioV2ResultResponse.kt new file mode 100644 index 0000000..806b446 --- /dev/null +++ b/app/src/main/java/com/example/hpostesting/data/model/molbioresult/MolbioV2ResultResponse.kt @@ -0,0 +1,12 @@ +package com.example.hpostesting.data.model.molbioresult + +import com.google.gson.annotations.SerializedName + +data class MolbioV2ResultResponse( + @SerializedName("Data") + val data: List? = listOf(), + @SerializedName("Message") + val message: String? = "", + @SerializedName("Result") + val result: String? = "" +) \ No newline at end of file diff --git a/app/src/main/java/com/example/hpostesting/data/model/hpostest/RawData.kt b/app/src/main/java/com/example/hpostesting/data/model/molbioresult/RawData.kt similarity index 96% rename from app/src/main/java/com/example/hpostesting/data/model/hpostest/RawData.kt rename to app/src/main/java/com/example/hpostesting/data/model/molbioresult/RawData.kt index e4641c5..e28811d 100644 --- a/app/src/main/java/com/example/hpostesting/data/model/hpostest/RawData.kt +++ b/app/src/main/java/com/example/hpostesting/data/model/molbioresult/RawData.kt @@ -1,4 +1,4 @@ -package com.example.hpostesting.data.model.hpostest +package com.example.hpostesting.data.model.molbioresult import com.example.hpostesting.data.model.patient.UserData diff --git a/app/src/main/java/com/example/hpostesting/data/model/patient/HemoCubeTestData.kt b/app/src/main/java/com/example/hpostesting/data/model/patient/HemoCubeTestData.kt index 5b22aee..9d6d937 100644 --- a/app/src/main/java/com/example/hpostesting/data/model/patient/HemoCubeTestData.kt +++ b/app/src/main/java/com/example/hpostesting/data/model/patient/HemoCubeTestData.kt @@ -55,4 +55,5 @@ data class HemoCubeTestData( var batteryMaxCapacity: String = "", var batteryTemperature: String = "", var batteryVoltage: String = "", + var molbioFlag: Boolean = false ) diff --git a/app/src/main/java/com/example/hpostesting/data/model/updates/CheckUpdateData.kt b/app/src/main/java/com/example/hpostesting/data/model/updates/CheckUpdateData.kt new file mode 100644 index 0000000..d8cd502 --- /dev/null +++ b/app/src/main/java/com/example/hpostesting/data/model/updates/CheckUpdateData.kt @@ -0,0 +1,5 @@ +package com.example.hpostesting.data.model.updates + +data class CheckUpdateData( + val version: String? = "" +) \ No newline at end of file diff --git a/app/src/main/java/com/example/hpostesting/data/model/updates/CheckUpdateRequest.kt b/app/src/main/java/com/example/hpostesting/data/model/updates/CheckUpdateRequest.kt new file mode 100644 index 0000000..7d1d503 --- /dev/null +++ b/app/src/main/java/com/example/hpostesting/data/model/updates/CheckUpdateRequest.kt @@ -0,0 +1,5 @@ +package com.example.hpostesting.data.model.updates + +data class CheckUpdateRequest( + val currentVersion: String? = "" +) \ No newline at end of file diff --git a/app/src/main/java/com/example/hpostesting/data/model/updates/CheckUpdateResponse.kt b/app/src/main/java/com/example/hpostesting/data/model/updates/CheckUpdateResponse.kt new file mode 100644 index 0000000..95fb791 --- /dev/null +++ b/app/src/main/java/com/example/hpostesting/data/model/updates/CheckUpdateResponse.kt @@ -0,0 +1,12 @@ +package com.example.hpostesting.data.model.updates + +import com.google.gson.annotations.SerializedName + +data class CheckUpdateResponse( + @SerializedName("Data") + val data: CheckUpdateData? = CheckUpdateData(), + @SerializedName("Message") + val message: String? = "", + @SerializedName("Result") + val result: String? = "" +) \ No newline at end of file diff --git a/app/src/main/java/com/example/hpostesting/data/model/updates/DeviceUpdateRequest.kt b/app/src/main/java/com/example/hpostesting/data/model/updates/DeviceUpdateRequest.kt new file mode 100644 index 0000000..75551a6 --- /dev/null +++ b/app/src/main/java/com/example/hpostesting/data/model/updates/DeviceUpdateRequest.kt @@ -0,0 +1,5 @@ +package com.example.hpostesting.data.model.updates + +data class DeviceUpdateRequest( + val serial_no: String? = "" +) \ No newline at end of file diff --git a/app/src/main/java/com/example/hpostesting/data/repository/DatabaseRepository.kt b/app/src/main/java/com/example/hpostesting/data/repository/DatabaseRepository.kt index 44c0767..710ae5f 100644 --- a/app/src/main/java/com/example/hpostesting/data/repository/DatabaseRepository.kt +++ b/app/src/main/java/com/example/hpostesting/data/repository/DatabaseRepository.kt @@ -1,32 +1,45 @@ package com.example.hpostesting.data.repository import android.net.Uri -import com.example.hpostesting.data.api.HPOSApi +import com.example.hpostesting.data.Result +import com.example.hpostesting.data.api.MolbioAuthApi +import com.example.hpostesting.data.api.MolbioResultApi import com.example.hpostesting.data.model.PendingUploads import com.example.hpostesting.data.model.Response +import com.example.hpostesting.data.model.deviceprovision.DeviceProvisionRequest +import com.example.hpostesting.data.model.deviceprovision.DeviceProvisionResponse import com.example.hpostesting.data.model.diagnostics.DiagnosticsData +import com.example.hpostesting.data.model.log.UploadLogsResponse +import com.example.hpostesting.data.model.login.LoginRequest +import com.example.hpostesting.data.model.login.LoginResponse +import com.example.hpostesting.data.model.molbioresult.MolbioV2ResultRequest +import com.example.hpostesting.data.model.molbioresult.MolbioV2ResultResponse import com.example.hpostesting.data.model.patient.BufferCheckData import com.example.hpostesting.data.model.patient.DeviceData import com.example.hpostesting.data.model.patient.HemoCubeTestData import com.example.hpostesting.data.model.patient.UserData +import com.example.hpostesting.data.model.updates.CheckUpdateRequest +import com.example.hpostesting.data.model.updates.CheckUpdateResponse +import com.example.hpostesting.data.model.updates.DeviceUpdateRequest import com.google.firebase.crashlytics.ktx.crashlytics import com.google.firebase.firestore.FirebaseFirestore import com.google.firebase.firestore.ktx.firestore import com.google.firebase.ktx.Firebase import com.google.firebase.storage.ktx.storage import kotlinx.coroutines.tasks.await +import okhttp3.MultipartBody +import okhttp3.ResponseBody import java.io.File -import com.example.hpostesting.data.Result -import com.example.hpostesting.data.model.deviceprovision.DeviceProvisionRequest -import com.example.hpostesting.data.model.deviceprovision.DeviceProvisionResponse -import com.example.hpostesting.data.model.login.LoginRequest -import com.example.hpostesting.data.model.login.LoginResponse import java.net.ConnectException import java.net.SocketTimeoutException import javax.inject.Inject +import javax.inject.Named class NetworkException(message: String, cause: Throwable) : Exception(message, cause) -class DatabaseRepository @Inject constructor(private val hposApi: HPOSApi) : Repository { +class DatabaseRepository @Inject constructor( + @Named("Auth")private val molbioAuthApi: MolbioAuthApi, + private val molbioResultApi: MolbioResultApi +) : Repository { private val db: FirebaseFirestore = Firebase.firestore private val storage = Firebase.storage @@ -45,23 +58,40 @@ class DatabaseRepository @Inject constructor(private val hposApi: HPOSApi) : Rep } suspend fun deviceProvision(deviceProvisionRequest: DeviceProvisionRequest): Result { - return safeApiCall { hposApi.deviceProvision(deviceProvisionRequest) } + return safeApiCall { molbioAuthApi.deviceProvision(deviceProvisionRequest) } } suspend fun login(loginRequest: LoginRequest): Result { - return safeApiCall { hposApi.login(loginRequest) } + return safeApiCall { molbioAuthApi.login(loginRequest) } + } + + suspend fun uploadResults(molbioV2ResultRequest: MolbioV2ResultRequest): Result { + return safeApiCall { molbioResultApi.uploadResults(molbioV2ResultRequest) } + } + + suspend fun checkUpdate(checkUpdateRequest: CheckUpdateRequest): Result { + return safeApiCall { molbioResultApi.checkUpdate(checkUpdateRequest) } + } + + suspend fun deviceUpdate(deviceUpdateRequest: DeviceUpdateRequest): Result { + return safeApiCall { molbioResultApi.deviceUpdate(deviceUpdateRequest) } + } + + suspend fun uploadLogs(logFile: MultipartBody.Part): Result { + return safeApiCall { molbioResultApi.uploadLogs(logFile) } } override suspend fun addTestToDatabase(data: HemoCubeTestData?): Response { return try { - val userdata = db.collection("patientData").whereEqualTo("_id", data!!._id).get().await() + val userdata = + db.collection("patientData").whereEqualTo("_id", data!!._id).get().await() if (userdata.documents.isNotEmpty()) { userdata.documents.forEach { db.collection("patientData").document(it.id).update("testStatus", true) } } - db.collection("testData").add(data!!).await() - Response.Success(data!!._id) + db.collection("testData").add(data).await() + Response.Success(data._id) } catch (e: Exception) { Response.Error(e) } @@ -69,14 +99,15 @@ class DatabaseRepository @Inject constructor(private val hposApi: HPOSApi) : Rep override suspend fun addTestToDatabase(data: UserData?): Response { return try { - val userdata = db.collection("patientData").whereEqualTo("_id", data!!._id).get().await() + val userdata = + db.collection("patientData").whereEqualTo("_id", data!!._id).get().await() if (userdata.documents.isNotEmpty()) { userdata.documents.forEach { db.collection("patientData").document(it.id).update("testStatus", true) } } - db.collection("testData").add(data!!).await() - Response.Success(data!!._id) + db.collection("testData").add(data).await() + Response.Success(data._id) } catch (e: Exception) { Firebase.crashlytics.recordException(e) Response.Error(e) @@ -86,7 +117,7 @@ class DatabaseRepository @Inject constructor(private val hposApi: HPOSApi) : Rep override suspend fun addTestToDatabaseforBufferCheck(data: BufferCheckData?): Response { return try { db.collection("buffers").add(data!!).await() - Response.Success(data!!.kitno) + Response.Success(data.kitno) } catch (e: Exception) { Firebase.crashlytics.recordException(e) Response.Error(e) @@ -96,14 +127,16 @@ class DatabaseRepository @Inject constructor(private val hposApi: HPOSApi) : Rep override suspend fun addDiagnostics(data: DiagnosticsData?): Response { return try { db.collection("diagnostics").add(data!!).await() - Response.Success(data!!.deviceId) + Response.Success(data.deviceId) } catch (e: Exception) { Firebase.crashlytics.recordException(e) Response.Error(e) } } - override suspend fun uploadFileToStorage(patientID: String, filePath: String): Response { + override suspend fun uploadFileToStorage( + patientID: String, filePath: String + ): Response { try { val file = Uri.fromFile(File(filePath)) diff --git a/app/src/main/java/com/example/hpostesting/domain/AuthInterceptor.kt b/app/src/main/java/com/example/hpostesting/domain/AuthInterceptor.kt new file mode 100644 index 0000000..aed16c0 --- /dev/null +++ b/app/src/main/java/com/example/hpostesting/domain/AuthInterceptor.kt @@ -0,0 +1,23 @@ +package com.example.hpostesting.domain + +import android.content.SharedPreferences +import com.example.hpostesting.data.constant.Constants +import okhttp3.Interceptor +import okhttp3.Response + +class AuthInterceptor(private val sharedPreferences: SharedPreferences) : Interceptor { + + override fun intercept(chain: Interceptor.Chain): Response { + var request = chain.request() + + val token = sharedPreferences.getString(Constants.ACCESS_TOKEN, null) + + if (token != null) { + request = request.newBuilder() + .addHeader("Authorization", "Bearer $token") + .build() + } + + return chain.proceed(request) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/hpostesting/domain/LogFileManager.kt b/app/src/main/java/com/example/hpostesting/domain/LogFileManager.kt new file mode 100644 index 0000000..f250516 --- /dev/null +++ b/app/src/main/java/com/example/hpostesting/domain/LogFileManager.kt @@ -0,0 +1,38 @@ +package com.example.hpostesting.domain + +import android.content.Context +import android.util.Log +import java.io.File +import java.io.FileOutputStream +import java.io.IOException + +class LogFileManager(private val context: Context) { + + fun createLogFile(): File? { + val unixTime = System.currentTimeMillis() / 1000L + val logFileName = "hpos_$unixTime.log" + + return try { + val process = Runtime.getRuntime().exec("logcat -d -v threadtime") + val logBuilder = StringBuilder() + val input = process.inputStream + val bufferedReader = input.bufferedReader() + + bufferedReader.forEachLine { line -> + logBuilder.append(line).append("\n") + } + + val logContent = logBuilder.toString() + + val file = File(context.filesDir, logFileName) + val fileOutputStream = FileOutputStream(file) + fileOutputStream.write(logContent.toByteArray()) + fileOutputStream.close() + + file + } catch (e: IOException) { + Log.e("LogFileManager", "Error creating log file: ${e.message}") + null + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/hpostesting/domain/di/AppModule.kt b/app/src/main/java/com/example/hpostesting/domain/di/AppModule.kt index 8f8e3fe..f3f21d9 100644 --- a/app/src/main/java/com/example/hpostesting/domain/di/AppModule.kt +++ b/app/src/main/java/com/example/hpostesting/domain/di/AppModule.kt @@ -2,11 +2,12 @@ package com.example.hpostesting.domain.di import android.app.Application import android.content.Context +import android.content.SharedPreferences import android.content.res.AssetManager import androidx.room.Room -import com.example.hpostesting.data.api.HPOSApi +import com.example.hpostesting.data.api.MolbioAuthApi +import com.example.hpostesting.data.api.MolbioResultApi import com.example.hpostesting.data.api.PropertyProvider -import com.example.hpostesting.data.constant.Constants.BASE_URL import com.example.hpostesting.data.dao.HemoCubeDao import com.example.hpostesting.data.dao.MyDatabase import com.example.hpostesting.data.dao.UserDao @@ -14,6 +15,8 @@ import com.example.hpostesting.data.datasource.LocalFileDataSource import com.example.hpostesting.data.repository.DatabaseRepository import com.example.hpostesting.data.repository.LocalFileRepository import com.example.hpostesting.data.repository.Repository +import com.example.hpostesting.domain.AuthInterceptor +import com.example.hpostesting.domain.LogFileManager import com.example.hpostesting.domain.SaveRawData import com.example.hpostesting.domain.SaveRawDataTest import com.example.hpostesting.util.PropertyProviderImpl @@ -25,6 +28,8 @@ import dagger.hilt.components.SingletonComponent import okhttp3.OkHttpClient import retrofit2.Retrofit import retrofit2.converter.gson.GsonConverterFactory +import java.util.concurrent.TimeUnit +import javax.inject.Named import javax.inject.Singleton @Module @@ -36,9 +41,7 @@ object AppModule { fun provideMyDatabase(@ApplicationContext context: Context): MyDatabase { return Room.databaseBuilder( context, MyDatabase::class.java, "my_database" - ) - .fallbackToDestructiveMigration() - .build() + ).fallbackToDestructiveMigration().build() } @Provides @@ -75,14 +78,20 @@ object AppModule { @Provides @Singleton - fun provideDatabaseRepository(hposApi: HPOSApi): DatabaseRepository { - return DatabaseRepository(hposApi = hposApi) + fun provideDatabaseRepository( + @Named("Auth") molbioAuthApi: MolbioAuthApi, + molbioResultApi: MolbioResultApi + ): DatabaseRepository { + return DatabaseRepository(molbioAuthApi = molbioAuthApi, molbioResultApi = molbioResultApi) } @Provides @Singleton - fun provideRepository(hposApi: HPOSApi): Repository { - return DatabaseRepository(hposApi) + fun provideRepository( + @Named("Auth") molbioAuthApi: MolbioAuthApi, + molbioResultApi: MolbioResultApi + ): Repository { + return DatabaseRepository(molbioAuthApi, molbioResultApi) } @Provides @@ -99,23 +108,59 @@ object AppModule { @Provides @Singleton - fun provideOkHttpClient(): OkHttpClient { - return OkHttpClient.Builder() - .build() + fun provideSharedPreferences(@ApplicationContext context: Context): SharedPreferences { + return context.getSharedPreferences("PREFERENCE_NAME", Context.MODE_PRIVATE) } @Provides @Singleton - fun provideRetrofit(propertyProvider: PropertyProvider, client: OkHttpClient): Retrofit { - return Retrofit.Builder() - .baseUrl(propertyProvider.getProperty("BASE_URL")) - .client(client) - .addConverterFactory(GsonConverterFactory.create()) - .build() + fun provideAuthInterceptor(sharedPreferences: SharedPreferences): AuthInterceptor { + return AuthInterceptor(sharedPreferences) } @Provides @Singleton - fun provideEntryApi(retrofit: Retrofit): HPOSApi = - retrofit.create(HPOSApi::class.java) + @Named("Auth") + fun provideAuthOkHttpClient(authInterceptor: AuthInterceptor): OkHttpClient { + return OkHttpClient.Builder().addInterceptor(authInterceptor) + .connectTimeout(30, TimeUnit.SECONDS).readTimeout(30, TimeUnit.SECONDS).build() + } + + @Provides + @Singleton + fun provideOkHttpClient(): OkHttpClient = + OkHttpClient.Builder().connectTimeout(30, TimeUnit.SECONDS) + .readTimeout(30, TimeUnit.SECONDS).build() + + @Provides + @Singleton + fun provideRetrofit( + propertyProvider: PropertyProvider,@Named("Auth") client: OkHttpClient + ): Retrofit { + return Retrofit.Builder().baseUrl(propertyProvider.getProperty("BASE_URL")).client(client) + .addConverterFactory(GsonConverterFactory.create()).build() + } + + @Provides + @Singleton + @Named("Auth") + fun provideAuthRetrofit(propertyProvider: PropertyProvider, client: OkHttpClient): Retrofit { + return Retrofit.Builder().baseUrl(propertyProvider.getProperty("BASE_URL")).client(client) + .addConverterFactory(GsonConverterFactory.create()).build() + } + + @Provides + @Singleton + @Named("Auth") + fun provideAuthApi(@Named("Auth") retrofit: Retrofit): MolbioAuthApi = + retrofit.create(MolbioAuthApi::class.java) + + @Provides + @Singleton + fun provideResultApi(retrofit: Retrofit): MolbioResultApi = + retrofit.create(MolbioResultApi::class.java) + + @Provides + @Singleton + fun provideLogFileManager(context: Context): LogFileManager = LogFileManager(context) } \ No newline at end of file diff --git a/app/src/main/java/com/example/hpostesting/presentation/adapter/UserListAdapter.kt b/app/src/main/java/com/example/hpostesting/presentation/adapter/UserListAdapter.kt index b3b9338..54bb480 100644 --- a/app/src/main/java/com/example/hpostesting/presentation/adapter/UserListAdapter.kt +++ b/app/src/main/java/com/example/hpostesting/presentation/adapter/UserListAdapter.kt @@ -197,7 +197,7 @@ class UserListAdapter( // } } userCard.setOnClickListener { - if (batLevel < Constants.BATTERY_LEVEL_MIN) { + if (false) { Toast.makeText( view.context, context?.getString(R.string.low_battery_warning), diff --git a/app/src/main/java/com/example/hpostesting/presentation/dashboard/HomeFragment.kt b/app/src/main/java/com/example/hpostesting/presentation/dashboard/HomeFragment.kt index 46c0eaf..788e69f 100644 --- a/app/src/main/java/com/example/hpostesting/presentation/dashboard/HomeFragment.kt +++ b/app/src/main/java/com/example/hpostesting/presentation/dashboard/HomeFragment.kt @@ -8,6 +8,7 @@ import android.content.Intent import android.content.SharedPreferences import android.os.BatteryManager import android.os.Bundle +import android.util.Base64 import android.view.LayoutInflater import android.view.View import android.view.ViewGroup @@ -22,6 +23,7 @@ import com.example.hpostesting.data.model.login.LoginRequest import com.example.hpostesting.data.model.login.LoginResponse import com.example.hpostesting.data.model.patient.HemoCubeTestData import com.example.hpostesting.data.model.patient.UserData +import com.example.hpostesting.data.model.updates.CheckUpdateRequest import com.example.hpostesting.presentation.KitScanActivity import com.example.hpostesting.presentation.adapter.OfflineUserListAdapter import com.example.hpostesting.presentation.adapter.UserListAdapter @@ -36,6 +38,7 @@ import com.google.firebase.perf.ktx.performance import dagger.hilt.android.AndroidEntryPoint import `in`.sminnovations.hpostesting.R import `in`.sminnovations.hpostesting.databinding.FragmentHomeBinding +import org.json.JSONObject import java.text.SimpleDateFormat import java.util.Calendar import java.util.Date @@ -101,7 +104,7 @@ class HomeFragment : Fragment() { loadUserData() setSearch() checkForLocalDBData() - checkForToken() + checkForTokenAndUpdate() } else { binding.internetAvailableCL.visibility = View.GONE binding.internetNotAvailableCL.visibility = View.VISIBLE @@ -111,9 +114,7 @@ class HomeFragment : Fragment() { hemoCubeViewModel.fireBaseBulkUpload.observe(viewLifecycleOwner) { result -> if (result == "Success") { Toast.makeText( - requireContext(), - R.string.test_upload, - Toast.LENGTH_SHORT + requireContext(), R.string.test_upload, Toast.LENGTH_SHORT ).show() } if (result == "Error") { @@ -138,17 +139,72 @@ class HomeFragment : Fragment() { } } - private fun checkForToken() { - if (sharedPreference.getString(Constants.ACCESS_TOKEN, "").toString() - .isEmpty() && sharedPreference.getString(Constants.NATS_TOKEN, "").toString() - .isEmpty() - ) { + private fun checkForTokenAndUpdate() { + val accessToken = sharedPreference.getString(Constants.ACCESS_TOKEN, "").toString() + val natsToken = sharedPreference.getString(Constants.NATS_TOKEN, "").toString() + if (accessToken.isEmpty() && natsToken.isEmpty()) { hemoCubeViewModel.login(createLoginRequestData()) + } else { + if (isTokenExpired(accessToken)) { + hemoCubeViewModel.login(createLoginRequestData()) + } else { + hemoCubeViewModel.checkUpdate(createCheckUpdateRequestData()) + hemoCubeViewModel.uploadLogs() + } } hemoCubeViewModel.loginResponse.observe(viewLifecycleOwner) { response -> when (response) { is Result.Success -> { updateTokens(response) + checkForTokenAndUpdate() + } + + is Result.Error -> { + response.exception.let { message -> + Toast.makeText(activity, "An error occurred: $message", Toast.LENGTH_LONG) + .show() + } + } + + is Result.Loading -> { + + } + + else -> {} + } + } + + hemoCubeViewModel.checkUpdate.observe(viewLifecycleOwner) { response -> + when (response) { + is Result.Success -> { + Toast.makeText( + requireContext(), response.data.data?.version, Toast.LENGTH_SHORT + ).show() + } + + is Result.Error -> { + response.exception.let { message -> + Toast.makeText(activity, "An error occurred: $message", Toast.LENGTH_LONG) + .show() + } + } + + is Result.Loading -> { + + } + + else -> {} + } + } + + hemoCubeViewModel.uploadLogs.observe(viewLifecycleOwner) { response -> + when (response) { + is Result.Success -> { + Toast.makeText( + requireContext(), + "Log uploaded ${response.data.data?.filename}", + Toast.LENGTH_SHORT + ).show() } is Result.Error -> { @@ -167,6 +223,17 @@ class HomeFragment : Fragment() { } } + fun isTokenExpired(token: String): Boolean { + val parts = token.split("\\.".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray() + val decodedPayload = String(Base64.decode(parts[1], Base64.DEFAULT)) + val jsonPayload = JSONObject(decodedPayload) + + val exp = jsonPayload.optLong("exp", 0) + val currentTimeSeconds = System.currentTimeMillis() / 1000 + + return exp <= currentTimeSeconds + } + private fun updateTokens(response: Result.Success) { with(sharedPreference.edit()) { putString(Constants.ACCESS_TOKEN, response.data.data?.accessToken) @@ -183,6 +250,16 @@ class HomeFragment : Fragment() { ) } + private fun createCheckUpdateRequestData(): CheckUpdateRequest { + val pInfo = requireActivity().packageManager.getPackageInfo( + requireActivity().packageName, 0 + ) + val version = pInfo.versionName + return CheckUpdateRequest( + currentVersion = version + ) + } + private fun setUserId() { binding.btnSubmit.setOnClickListener { val userId = binding.userId.text.toString() @@ -233,8 +310,12 @@ class HomeFragment : Fragment() { rvAdapter = view?.let { UserListAdapter( - requireContext(), hemoCubeViewModel, recyclerViewOptions, - it, batLevel, requireActivity() + requireContext(), + hemoCubeViewModel, + recyclerViewOptions, + it, + batLevel, + requireActivity() ) }!! binding.rvOrder.adapter = rvAdapter diff --git a/app/src/main/java/com/example/hpostesting/presentation/hemocube/HemoCubeFragment.kt b/app/src/main/java/com/example/hpostesting/presentation/hemocube/HemoCubeFragment.kt index 7951693..ad4054d 100644 --- a/app/src/main/java/com/example/hpostesting/presentation/hemocube/HemoCubeFragment.kt +++ b/app/src/main/java/com/example/hpostesting/presentation/hemocube/HemoCubeFragment.kt @@ -14,6 +14,7 @@ import androidx.fragment.app.Fragment import androidx.fragment.app.activityViewModels import androidx.lifecycle.MutableLiveData import com.example.hpostesting.data.DataHolder +import com.example.hpostesting.data.Result import com.example.hpostesting.data.constant.Constants import com.example.hpostesting.data.constant.HemoCubeCommands import com.example.hpostesting.data.model.patient.DeviceData @@ -134,14 +135,32 @@ class HemoCubeFragment : Fragment() { hemoCubeViewModel.fireBaseUpload.observe(viewLifecycleOwner) { result -> if (result == "Success") { showToast(R.string.test_upload) - activity?.runOnUiThread { binding.btnSubmit.visibility = View.GONE - val i = Intent( - requireContext().applicationContext, DashboardActivity::class.java - ) - startActivity(i) + val i = Intent( + requireContext().applicationContext, + DashboardActivity::class.java + ) + startActivity(i) + } + hemoCubeViewModel.resultUpload.observe(viewLifecycleOwner) { + when (it) { + is Result.Success -> { + it.data.data?.get(0)?.rawData?.let { it1 -> + hemoCubeViewModel.updateMolbioFlag( + it1._id) + } + } + is Result.Error -> { + //Remove this line of code while deploying to IOCL + it.exception.let { message -> + Toast.makeText(activity, "An error occurred: $message", Toast.LENGTH_LONG) + .show() + } + } + else -> {} + } } } if (result == "Local") { @@ -487,6 +506,8 @@ class HemoCubeFragment : Fragment() { } } + + var gradient = Constants.DEVICE_CONFIGURATION[deviceHardwareId]?.get(0)?.get(0) var constant = Constants.DEVICE_CONFIGURATION[deviceHardwareId]?.get(0)?.get(1) fittedAbs1 = gradient?.times(led1Average)?.plus(constant!!)!! @@ -604,11 +625,17 @@ class HemoCubeFragment : Fragment() { if (calculatedRatio != null) { if (calculatedRatio < 0.05) return getString(R.string.error_repeat_test_higher_volume) - if (calculatedRatio in 0.05..0.155) + if (calculatedRatio in 0.05..0.155) { activity?.runOnUiThread { - binding.tvSubtitle4.setTextColor(ContextCompat.getColor(requireContext(), R.color.brightGreen)) + binding.tvSubtitle4.setTextColor( + ContextCompat.getColor( + requireContext(), + R.color.brightGreen + ) + ) } return getString(R.string.normal) + } if (calculatedRatio in 0.155..0.175) return getString(R.string.negative_borderline) if (calculatedRatio in 0.175..0.22) @@ -634,11 +661,17 @@ class HemoCubeFragment : Fragment() { try { hemoCubeViewModel.messages.postValue("result classification") if (predictedDenovixRatio != null) { - if (predictedDenovixRatio in 0.0..0.16) + if (predictedDenovixRatio in 0.0..0.16) { activity?.runOnUiThread { - binding.tvSubtitle4.setTextColor(ContextCompat.getColor(requireContext(), R.color.brightGreen)) + binding.tvSubtitle4.setTextColor( + ContextCompat.getColor( + requireContext(), + R.color.brightGreen + ) + ) } return getString(R.string.normal) + } if (predictedDenovixRatio in 0.16..0.165) return getString(R.string.negative_borderline) if (predictedDenovixRatio in 0.165..0.235) diff --git a/app/src/main/java/com/example/hpostesting/presentation/hemocube/HemoCubeViewModel.kt b/app/src/main/java/com/example/hpostesting/presentation/hemocube/HemoCubeViewModel.kt index da2fd98..5b3043a 100644 --- a/app/src/main/java/com/example/hpostesting/presentation/hemocube/HemoCubeViewModel.kt +++ b/app/src/main/java/com/example/hpostesting/presentation/hemocube/HemoCubeViewModel.kt @@ -12,22 +12,34 @@ import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.example.hpostesting.data.DataHolder import com.example.hpostesting.data.NetworkStatusLiveData +import com.example.hpostesting.data.Result import com.example.hpostesting.data.constant.Constants import com.example.hpostesting.data.dao.HemoCubeDao import com.example.hpostesting.data.model.Response import com.example.hpostesting.data.model.deviceprovision.DeviceProvisionRequest import com.example.hpostesting.data.model.deviceprovision.DeviceProvisionResponse +import com.example.hpostesting.data.model.log.UploadLogsResponse +import com.example.hpostesting.data.model.login.LoginRequest +import com.example.hpostesting.data.model.login.LoginResponse +import com.example.hpostesting.data.model.molbioresult.MolbioV2Result +import com.example.hpostesting.data.model.molbioresult.MolbioV2ResultRequest +import com.example.hpostesting.data.model.molbioresult.MolbioV2ResultResponse import com.example.hpostesting.data.model.patient.BufferCheckData import com.example.hpostesting.data.model.patient.DeviceData import com.example.hpostesting.data.model.patient.HemoCubeTestData import com.example.hpostesting.data.model.patient.toHemoCubeTestData -import com.example.hpostesting.data.repository.Repository -import dagger.hilt.android.lifecycle.HiltViewModel -import com.example.hpostesting.data.Result -import com.example.hpostesting.data.model.login.LoginRequest -import com.example.hpostesting.data.model.login.LoginResponse +import com.example.hpostesting.data.model.updates.CheckUpdateRequest +import com.example.hpostesting.data.model.updates.CheckUpdateResponse +import com.example.hpostesting.data.model.updates.DeviceUpdateRequest import com.example.hpostesting.data.repository.DatabaseRepository +import com.example.hpostesting.data.repository.Repository +import com.example.hpostesting.domain.LogFileManager +import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.launch +import okhttp3.MediaType.Companion.toMediaTypeOrNull +import okhttp3.MultipartBody +import okhttp3.RequestBody.Companion.asRequestBody +import okhttp3.ResponseBody import java.text.SimpleDateFormat import java.util.Calendar import java.util.Locale @@ -38,6 +50,7 @@ class HemoCubeViewModel @Inject constructor( private val hemoCubeDao: HemoCubeDao, private val repository: Repository, private val databaseRepository: DatabaseRepository, + private val logFileManager: LogFileManager, context: Context ) : ViewModel() { var isServiceConnected = false @@ -51,11 +64,22 @@ class HemoCubeViewModel @Inject constructor( val loginResponse = MutableLiveData>() + val resultUpload = MutableLiveData>() + + val checkUpdate = MutableLiveData>() + + val deviceUpdate = MutableLiveData>() + + val uploadLogs = MutableLiveData?>() + + // Get the device ID of the device you want to retrieve data for (e.g., the first device in the list) private val _networkStatusLiveData = NetworkStatusLiveData(context) val allUserData = hemoCubeDao.getAll() val deviceData = MutableLiveData() + + val networkStatusLiveData: LiveData get() = _networkStatusLiveData val deviceMessages = MutableLiveData() @@ -106,6 +130,41 @@ class HemoCubeViewModel @Inject constructor( } } + fun uploadResult(molbioV2ResultRequest: MolbioV2ResultRequest) = viewModelScope.launch { + resultUpload.postValue(Result.Loading()) + databaseRepository.uploadResults(molbioV2ResultRequest).let { + resultUpload.postValue(it) + } + } + + fun checkUpdate(checkUpdateRequest: CheckUpdateRequest) = viewModelScope.launch { + checkUpdate.postValue(Result.Loading()) + databaseRepository.checkUpdate(checkUpdateRequest).let { + checkUpdate.postValue(it) + } + } + + fun deviceUpdate(deviceUpdateRequest: DeviceUpdateRequest) = viewModelScope.launch { + deviceUpdate.postValue(Result.Loading()) + databaseRepository.deviceUpdate(deviceUpdateRequest).let { + deviceUpdate.postValue(it) + } + } + + fun uploadLogs() = viewModelScope.launch { + uploadLogs.postValue(Result.Loading()) + val logFile = logFileManager.createLogFile().let { file -> + val requestBody = file?.asRequestBody("multipart/form-data".toMediaTypeOrNull()) + val multipartFile = + requestBody?.let { MultipartBody.Part.createFormData("logFile", file.name, it) } + multipartFile?.let { partFile -> + databaseRepository.uploadLogs(partFile).let { result -> + uploadLogs.postValue(result) + } + } + } + } + fun uploadHemoCubeResultToDatabaseforbuffercheckN(bufferCheckData: BufferCheckData) = viewModelScope.launch { addResultTestToDbforbuffercheck(bufferCheckData) @@ -173,6 +232,7 @@ class HemoCubeViewModel @Inject constructor( Log.i("Testdb", "Data uploaded to Firestore successfully") fireBaseUpload.postValue("Success") testDetails.localFlag = true + uploadResult(MolbioV2ResultRequest(listOf(MolbioV2Result(rawData = testDetails)))) hemoCubeDao.insertAll(testDetails) } @@ -181,6 +241,8 @@ class HemoCubeViewModel @Inject constructor( fireBaseUpload.postValue("Error") hemoCubeDao.insertAll(testDetails) } + + else -> {} } } catch (e: Exception) { Log.e("Testdb", "Exception during data upload: ${e.message}") @@ -203,6 +265,8 @@ class HemoCubeViewModel @Inject constructor( Log.e("Testdb", "Error uploading data to Firestore: $response") fireBaseUpload.postValue("Error") } + + else -> {} } } catch (e: Exception) { Log.e("Testdb", "Exception during data upload: ${e.message}") @@ -233,6 +297,10 @@ class HemoCubeViewModel @Inject constructor( hemoCubeDao.updateFieldById(id = userId, true) } + fun updateMolbioFlag(userId: String) = viewModelScope.launch { + hemoCubeDao.updateFieldById(id = userId, true) + } + fun addUser(userData: HemoCubeTestData) = viewModelScope.launch { hemoCubeDao.insertAll(userData) }