diff --git a/android-app/app/src/main/java/com/korexlabs/dhinspeccion/data/MobileActs.kt b/android-app/app/src/main/java/com/korexlabs/dhinspeccion/data/MobileActs.kt index 49ca613..d221dc2 100644 --- a/android-app/app/src/main/java/com/korexlabs/dhinspeccion/data/MobileActs.kt +++ b/android-app/app/src/main/java/com/korexlabs/dhinspeccion/data/MobileActs.kt @@ -4,15 +4,12 @@ import android.content.Context import com.korexlabs.dhinspeccion.BuildConfig import com.squareup.moshi.Moshi import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory -import kotlinx.coroutines.sync.Mutex -import kotlinx.coroutines.sync.withLock import okhttp3.MediaType.Companion.toMediaType import okhttp3.MultipartBody import okhttp3.OkHttpClient import okhttp3.RequestBody import okhttp3.RequestBody.Companion.asRequestBody import okhttp3.RequestBody.Companion.toRequestBody -import retrofit2.HttpException import retrofit2.Retrofit import retrofit2.converter.moshi.MoshiConverterFactory import retrofit2.http.Body @@ -303,14 +300,10 @@ private interface MobileActsApi { @Path("visitId") visitId: String, @Body request: MobileCloseVisitRequest, ): VisitDetail - - @POST("auth/mobile/refresh") - suspend fun refresh(@Body request: RefreshRequest): MobileSessionResponse } class MobileActsRepository(context: Context) { - private val store = SecureSessionStore(context.applicationContext) - private val refreshMutex = Mutex() + private val sessions = MobileSessionCoordinator.get(context.applicationContext) private val moshi = Moshi.Builder().addLast(KotlinJsonAdapterFactory()).build() private val api: MobileActsApi = Retrofit.Builder() .baseUrl(BuildConfig.API_BASE_URL) @@ -319,11 +312,11 @@ class MobileActsRepository(context: Context) { .build() .create(MobileActsApi::class.java) - suspend fun list(visitId: String): MobileActListResponse = authorized { session -> + suspend fun list(visitId: String): MobileActListResponse = sessions.authorized { session -> api.listActs("Bearer ${session.accessToken}", visitId) } - suspend fun get(actId: String): MobileActDetail = authorized { session -> + suspend fun get(actId: String): MobileActDetail = sessions.authorized { session -> api.act("Bearer ${session.accessToken}", actId) } @@ -332,7 +325,7 @@ class MobileActsRepository(context: Context) { assetId: String, visitCode: String, urgency: String = "NON_URGENT", - ): MobileActDetail = authorized { session -> + ): MobileActDetail = sessions.authorized { session -> api.createAct( "Bearer ${session.accessToken}", visitId, @@ -350,20 +343,20 @@ class MobileActsRepository(context: Context) { val detail = get(actId) if (detail.assets.any { it.id == assetId }) return detail val ids = (detail.assets.map { it.id } + assetId).distinct() - return authorized { session -> + return sessions.authorized { session -> api.updateAct("Bearer ${session.accessToken}", actId, UpdateMobileActRequest(ids)) } } - suspend fun closure(actId: String): MobileActClosure = authorized { session -> + suspend fun closure(actId: String): MobileActClosure = sessions.authorized { session -> api.closure("Bearer ${session.accessToken}", actId) } - suspend fun setResponsible(actId: String, request: MobileResponsibleRequest): MobileActClosure = authorized { session -> + suspend fun setResponsible(actId: String, request: MobileResponsibleRequest): MobileActClosure = sessions.authorized { session -> api.responsible("Bearer ${session.accessToken}", actId, request) } - suspend fun lock(actId: String): MobileActClosure = authorized { session -> + suspend fun lock(actId: String): MobileActClosure = sessions.authorized { session -> api.lock("Bearer ${session.accessToken}", actId) } @@ -394,7 +387,7 @@ class MobileActsRepository(context: Context) { statement = statement, ) - suspend fun companyOutcome(actId: String, status: String, reason: String): MobileActClosure = authorized { session -> + suspend fun companyOutcome(actId: String, status: String, reason: String): MobileActClosure = sessions.authorized { session -> api.companyOutcome( "Bearer ${session.accessToken}", actId, @@ -402,11 +395,11 @@ class MobileActsRepository(context: Context) { ) } - suspend fun seal(actId: String): MobileActClosure = authorized { session -> + suspend fun seal(actId: String): MobileActClosure = sessions.authorized { session -> api.sealAct("Bearer ${session.accessToken}", actId, MobileSealActRequest()) } - suspend fun closeVisit(visitId: String): VisitDetail = authorized { session -> + suspend fun closeVisit(visitId: String): VisitDetail = sessions.authorized { session -> api.closeVisit("Bearer ${session.accessToken}", visitId, MobileCloseVisitRequest()) } @@ -419,53 +412,35 @@ class MobileActsRepository(context: Context) { company: Boolean, manifestation: String? = null, statement: String? = null, - ): MobileActClosure = authorized { session -> - val text = "text/plain".toMediaType() - val file = MultipartBody.Part.createFormData( - "file", - png.name, - png.asRequestBody("image/png".toMediaType()), - ) - val consent = "true".toRequestBody(text) - val signedAt = Instant.now().toString().toRequestBody(text) - val device = "DH Android".toRequestBody(text) - val lat = latitude?.toString()?.toRequestBody(text) - val lon = longitude?.toString()?.toRequestBody(text) - val accuracy = accuracyM?.toString()?.toRequestBody(text) - if (company) { - api.signCompany( - "Bearer ${session.accessToken}", actId, file, consent, signedAt, - lat, lon, accuracy, device, - manifestation?.toRequestBody(text), - statement?.trim()?.takeIf { it.isNotBlank() }?.toRequestBody(text), - ) - } else { - api.signInspector( - "Bearer ${session.accessToken}", actId, file, consent, signedAt, - lat, lon, accuracy, device, + ): MobileActClosure { + val response = sessions.authorized { session -> + val text = "text/plain".toMediaType() + val filePart = MultipartBody.Part.createFormData( + "file", + png.name, + png.asRequestBody("image/png".toMediaType()), ) + val consent = "true".toRequestBody(text) + val signedAt = Instant.now().toString().toRequestBody(text) + val device = "DH Android".toRequestBody(text) + val lat = latitude?.toString()?.toRequestBody(text) + val lon = longitude?.toString()?.toRequestBody(text) + val accuracy = accuracyM?.toString()?.toRequestBody(text) + if (company) { + api.signCompany( + "Bearer ${session.accessToken}", actId, filePart, consent, signedAt, + lat, lon, accuracy, device, + manifestation?.toRequestBody(text), + statement?.trim()?.takeIf { it.isNotBlank() }?.toRequestBody(text), + ) + } else { + api.signInspector( + "Bearer ${session.accessToken}", actId, filePart, consent, signedAt, + lat, lon, accuracy, device, + ) + } } - } - - private suspend fun authorized(block: suspend (StoredSession) -> T): T { - var session = store.load() ?: throw IllegalStateException("Sesión no iniciada") - try { - return block(session) - } catch (error: HttpException) { - if (error.code() != 401) throw error - } - session = refresh(session.refreshToken) - return block(session) - } - - private suspend fun refresh(previousRefreshToken: String): StoredSession = refreshMutex.withLock { - val latest = store.load() ?: throw IllegalStateException("Sesión no iniciada") - if (latest.refreshToken != previousRefreshToken) return@withLock latest - try { - store.save(api.refresh(RefreshRequest(previousRefreshToken))) - } catch (error: Throwable) { - store.clear() - throw error - } + png.delete() + return response } }