Commit 5137c6cf by Aleksandr

pass messages between modules while rendering and sending pages to server

parent 716c048f
...@@ -2,8 +2,12 @@ package com.isidroid.c23.domain.use_case ...@@ -2,8 +2,12 @@ package com.isidroid.c23.domain.use_case
import android.content.Context import android.content.Context
import com.isidroid.c23.data.mapper.createListItem import com.isidroid.c23.data.mapper.createListItem
import com.isidroid.c23.domain.dto.PrintJobListItem
import com.isidroid.c23.ext.getPrintJobStatus
import com.isidroid.c23.ext.getPrintJobStatusColor
import com.isidroid.core.FlowResult import com.isidroid.core.FlowResult
import com.isidroid.job.repository.JobRepository import com.isidroid.job.repository.JobRepository
import com.isidroid.job_sender.domain.dto.JobSenderResult
import com.isidroid.spot.repository.SpotRepository import com.isidroid.spot.repository.SpotRepository
import dagger.hilt.android.qualifiers.ApplicationContext import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.flow
...@@ -20,9 +24,11 @@ class PrintJobsUseCase @Inject constructor( ...@@ -20,9 +24,11 @@ class PrintJobsUseCase @Inject constructor(
emit(FlowResult.Loading) emit(FlowResult.Loading)
val jobList = jobRepository.readLocalList() val jobList = jobRepository.readLocalList()
val spots = jobList val spots = jobList
.asSequence()
.sortedByDescending { it.createdAt }
.map { it.spotId } .map { it.spotId }
.distinct() .distinct()
.let { spotRepository.findLocalRichSpots(it) } .let { spotRepository.findLocalRichSpots(it.toList()) }
?.associateBy({ it.spot.id }, { it }) ?.associateBy({ it.spot.id }, { it })
val result = jobList.map { job -> job.createListItem(context = context, richSpot = spots?.get(job.spotId)) } val result = jobList.map { job -> job.createListItem(context = context, richSpot = spots?.get(job.spotId)) }
...@@ -34,4 +40,19 @@ class PrintJobsUseCase @Inject constructor( ...@@ -34,4 +40,19 @@ class PrintJobsUseCase @Inject constructor(
val result = spotRepository.findRichSpot(spotCode)?.spot val result = spotRepository.findRichSpot(spotCode)?.spot
emit(FlowResult.Success(result)) emit(FlowResult.Success(result))
} }
fun updateStatuses(jobs: Collection<PrintJobListItem>?, info: JobSenderResult.Statuses): List<PrintJobListItem>? {
val list = jobs?.toMutableList() ?: return null
for (index in list.indices) {
val job = list[index]
if (job.id in info.jobIds)
list[index] = job.copy(
statusName = context.getString(getPrintJobStatus(info.status)),
statusColor = getPrintJobStatusColor(info.status)
)
}
return list
}
} }
\ No newline at end of file
...@@ -21,6 +21,10 @@ fun getPrintJobStatus(@JobStatus status: Int) = when (status) { ...@@ -21,6 +21,10 @@ fun getPrintJobStatus(@JobStatus status: Int) = when (status) {
JobStatus.RENDER_UPLOAD -> R.string.print_job_status_render_upload JobStatus.RENDER_UPLOAD -> R.string.print_job_status_render_upload
JobStatus.READY_TO_PRINT -> R.string.print_job_status_ready_to_print JobStatus.READY_TO_PRINT -> R.string.print_job_status_ready_to_print
JobStatus.UPLOAD_ERROR -> R.string.print_job_status_upload_error JobStatus.UPLOAD_ERROR -> R.string.print_job_status_upload_error
JobStatus.RENDERED -> R.string.print_job_status_rendered
JobStatus.SENDING -> R.string.print_job_status_sending
else -> R.string.empty else -> R.string.empty
} }
...@@ -39,5 +43,7 @@ fun getPrintJobStatusColor(@JobStatus status: Int) = when (status) { ...@@ -39,5 +43,7 @@ fun getPrintJobStatusColor(@JobStatus status: Int) = when (status) {
JobStatus.RENDER_UPLOAD -> ColorBlue JobStatus.RENDER_UPLOAD -> ColorBlue
JobStatus.READY_TO_PRINT -> ColorBlue JobStatus.READY_TO_PRINT -> ColorBlue
JobStatus.UPLOAD_ERROR -> ColorRed JobStatus.UPLOAD_ERROR -> ColorRed
JobStatus.RENDERED -> ColorOrange
JobStatus.SENDING -> ColorOrange
else -> Color.Transparent else -> Color.Transparent
} }
\ No newline at end of file
package com.isidroid.sync_service.ext package com.isidroid.c23.ext
import androidx.work.ListenableWorker import androidx.work.ListenableWorker
import androidx.work.WorkInfo import androidx.work.WorkInfo
......
...@@ -4,6 +4,7 @@ import com.isidroid.c23.domain.dto.PrintJobListItem ...@@ -4,6 +4,7 @@ import com.isidroid.c23.domain.dto.PrintJobListItem
import com.isidroid.core.vm.ViewEvent import com.isidroid.core.vm.ViewEvent
import com.isidroid.core.vm.ViewSideEffect import com.isidroid.core.vm.ViewSideEffect
import com.isidroid.core.vm.ViewState import com.isidroid.core.vm.ViewState
import com.isidroid.job_sender.domain.dto.JobSenderResult
class PrintJobsContract { class PrintJobsContract {
sealed interface Event : ViewEvent { sealed interface Event : ViewEvent {
...@@ -22,6 +23,6 @@ class PrintJobsContract { ...@@ -22,6 +23,6 @@ class PrintJobsContract {
data class State( data class State(
val loading: Boolean = false, val loading: Boolean = false,
val jobs: List<PrintJobListItem>? = null val jobs: List<PrintJobListItem>? = null,
) : ViewState ) : ViewState
} }
\ No newline at end of file
...@@ -5,20 +5,25 @@ import com.isidroid.c23.domain.use_case.PrintJobsUseCase ...@@ -5,20 +5,25 @@ import com.isidroid.c23.domain.use_case.PrintJobsUseCase
import com.isidroid.c23.ext.isDebug import com.isidroid.c23.ext.isDebug
import com.isidroid.core.FlowResult import com.isidroid.core.FlowResult
import com.isidroid.core.vm.BaseViewModel import com.isidroid.core.vm.BaseViewModel
import com.isidroid.job_sender.SendJobEventCollectorFlow
import com.isidroid.job_sender.domain.dto.JobSenderResult
import com.isidroid.spot.model.Spot import com.isidroid.spot.model.Spot
import com.isidroid.utils.catchTimber import com.isidroid.utils.catchTimber
import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.filterIsInstance
import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import javax.inject.Inject import javax.inject.Inject
@HiltViewModel @HiltViewModel
class PrintJobsViewModel @Inject constructor( class PrintJobsViewModel @Inject constructor(
private val useCase: PrintJobsUseCase private val useCase: PrintJobsUseCase,
private val sendJobEventCollectorFlow: SendJobEventCollectorFlow
) : BaseViewModel<PrintJobsContract.Event, PrintJobsContract.State, PrintJobsContract.Effect>() { ) : BaseViewModel<PrintJobsContract.Event, PrintJobsContract.State, PrintJobsContract.Effect>() {
init { init {
viewModelScope.launch { load() } viewModelScope.launch { load() }
viewModelScope.launch { listenSendJobEvents() }
} }
override val isDebug: Boolean = isDebug() override val isDebug: Boolean = isDebug()
...@@ -58,6 +63,17 @@ class PrintJobsViewModel @Inject constructor( ...@@ -58,6 +63,17 @@ class PrintJobsViewModel @Inject constructor(
} }
} }
private suspend fun listenSendJobEvents() {
sendJobEventCollectorFlow.eventsFlow
.filterIsInstance<JobSenderResult.Statuses>()
.collect { st -> updateStatuses(st) }
}
private fun updateStatuses(statuses: JobSenderResult.Statuses) {
val listItems = useCase.updateStatuses(jobs = viewState.value.jobs, info = statuses) ?: return
setState { copy(jobs = listItems) }
}
// handle callbacks // handle callbacks
private fun openSpotOnMap(spot: Spot?) { private fun openSpotOnMap(spot: Spot?) {
spot ?: return spot ?: return
......
...@@ -40,4 +40,6 @@ ...@@ -40,4 +40,6 @@
<string name="empty" /> <string name="empty" />
<string name="print_job_details">Job details</string> <string name="print_job_details">Job details</string>
<string name="app_navigation_explanation">To navigate to this location, please use an external navigation app. Tap the button below to open your preferred navigation app and plan your route.</string> <string name="app_navigation_explanation">To navigate to this location, please use an external navigation app. Tap the button below to open your preferred navigation app and plan your route.</string>
<string name="print_job_status_rendered">Render complete</string>
<string name="print_job_status_sending">Sending</string>
</resources> </resources>
\ No newline at end of file
...@@ -19,6 +19,9 @@ annotation class JobStatus { ...@@ -19,6 +19,9 @@ annotation class JobStatus {
const val RENDER_UPLOAD = 9 const val RENDER_UPLOAD = 9
const val READY_TO_PRINT = 10 const val READY_TO_PRINT = 10
const val UPLOAD_ERROR = 11 const val UPLOAD_ERROR = 11
const val RENDERED = 12
const val SENDING = 13
const val RENDER_STARTED = 14
} }
} }
...@@ -38,5 +41,8 @@ val Int.jobStatusName ...@@ -38,5 +41,8 @@ val Int.jobStatusName
JobStatus.RENDER_UPLOAD -> "RENDER_UPLOAD" JobStatus.RENDER_UPLOAD -> "RENDER_UPLOAD"
JobStatus.READY_TO_PRINT -> "READY_TO_PRINT" JobStatus.READY_TO_PRINT -> "READY_TO_PRINT"
JobStatus.UPLOAD_ERROR -> "UPLOAD_ERROR" JobStatus.UPLOAD_ERROR -> "UPLOAD_ERROR"
JobStatus.RENDERED -> "RENDERED"
JobStatus.SENDING -> "SENDING"
JobStatus.RENDER_STARTED -> "RENDER_STARTED"
else -> "Unknown" else -> "Unknown"
} }
\ No newline at end of file
package com.isidroid.job_sender package com.isidroid.job_sender
import com.isidroid.job.constant.JobStatus import com.isidroid.job.constant.JobStatus
import com.isidroid.job.model.PrintJob
import com.isidroid.job_sender.domain.dto.JobSenderResult import com.isidroid.job_sender.domain.dto.JobSenderResult
import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.flow.asSharedFlow
...@@ -20,9 +19,6 @@ class SendJobEventCollectorFlow @Inject constructor() { ...@@ -20,9 +19,6 @@ class SendJobEventCollectorFlow @Inject constructor() {
suspend fun renderProgress(index: Int, total: Int) = emit(JobSenderResult.RenderProgress(index, total)) suspend fun renderProgress(index: Int, total: Int) = emit(JobSenderResult.RenderProgress(index, total))
suspend fun updateProgress(index: Int, total: Int) = emit(JobSenderResult.UploadProgress(index, total)) suspend fun updateProgress(index: Int, total: Int) = emit(JobSenderResult.UploadProgress(index, total))
suspend fun jobError(jobId: String, t: Throwable) = emit(JobSenderResult.Error(jobId, t)) suspend fun jobError(jobId: String, t: Throwable) = emit(JobSenderResult.Error(jobId, t))
suspend fun updateStatus(@JobStatus status: Int, vararg job: PrintJob?) { suspend fun updateStatus(@JobStatus status: Int, vararg jobIds: String?) = emit(JobSenderResult.Statuses(status = status, jobIds = jobIds.toSet()))
val jobList = job.toList().filterNotNull()
if (jobList.isNotEmpty())
emit(JobSenderResult.Statuses(status = status, jobs = jobList))
}
} }
\ No newline at end of file
...@@ -7,5 +7,5 @@ sealed interface JobSenderResult { ...@@ -7,5 +7,5 @@ sealed interface JobSenderResult {
data class RenderProgress(val position: Int, val total: Int) : JobSenderResult data class RenderProgress(val position: Int, val total: Int) : JobSenderResult
data class UploadProgress(val position: Int, val total: Int) : JobSenderResult data class UploadProgress(val position: Int, val total: Int) : JobSenderResult
data class Error(val jobId: String, val t: Throwable) : JobSenderResult data class Error(val jobId: String, val t: Throwable) : JobSenderResult
data class Statuses(@JobStatus val status: Int, val jobs: Collection<PrintJob>): JobSenderResult data class Statuses(@JobStatus val status: Int, val jobIds: Collection<String?>): JobSenderResult
} }
\ No newline at end of file
...@@ -2,6 +2,7 @@ package com.isidroid.job_sender.ext ...@@ -2,6 +2,7 @@ package com.isidroid.job_sender.ext
import android.content.Context import android.content.Context
import android.graphics.BitmapFactory import android.graphics.BitmapFactory
import com.isidroid.job.constant.JobStatus
import com.isidroid.job.constant.SendJobStatus import com.isidroid.job.constant.SendJobStatus
import com.isidroid.job.model.PrintJob import com.isidroid.job.model.PrintJob
import com.isidroid.job_sender.RenderBitmapProfileException import com.isidroid.job_sender.RenderBitmapProfileException
......
...@@ -3,7 +3,6 @@ package com.isidroid.job_sender.repository ...@@ -3,7 +3,6 @@ package com.isidroid.job_sender.repository
import android.content.Context import android.content.Context
import com.isidroid.job.constant.JobStatus import com.isidroid.job.constant.JobStatus
import com.isidroid.job.constant.SendJobStatus import com.isidroid.job.constant.SendJobStatus
import com.isidroid.job.constant.jobStatusName
import com.isidroid.job.model.PrintJob import com.isidroid.job.model.PrintJob
import com.isidroid.job.repository.JobRepository import com.isidroid.job.repository.JobRepository
import com.isidroid.job_sender.SendJobEventCollectorFlow import com.isidroid.job_sender.SendJobEventCollectorFlow
...@@ -27,7 +26,6 @@ internal class JobSendRepositoryImpl( ...@@ -27,7 +26,6 @@ internal class JobSendRepositoryImpl(
private val eventCollector: SendJobEventCollectorFlow, private val eventCollector: SendJobEventCollectorFlow,
) : JobSendRepository { ) : JobSendRepository {
override suspend fun completeUpload(jobId: String, token: String): PrintJob? { override suspend fun completeUpload(jobId: String, token: String): PrintJob? {
val printJob = printJobRepository.readLocalList(ids = listOf(jobId)).firstOrNull() val printJob = printJobRepository.readLocalList(ids = listOf(jobId)).firstOrNull()
val uploadResult = sendJobNetworkSource.complete(jobId = jobId, token = token) val uploadResult = sendJobNetworkSource.complete(jobId = jobId, token = token)
...@@ -41,6 +39,8 @@ internal class JobSendRepositoryImpl( ...@@ -41,6 +39,8 @@ internal class JobSendRepositoryImpl(
// let's check whether there is not rendered print jobs // let's check whether there is not rendered print jobs
override suspend fun checkNotRenderedPrintJobs(items: Collection<PrintJob>): Collection<PrintJobSender>? { override suspend fun checkNotRenderedPrintJobs(items: Collection<PrintJob>): Collection<PrintJobSender>? {
if (items.isNotEmpty()) { if (items.isNotEmpty()) {
eventCollector.updateStatus(JobStatus.RENDER_STARTED, *items.map { it.id }.toTypedArray())
val renderItems = createRenderItems(context, spotRepository, renderRepository, eventCollector, items) val renderItems = createRenderItems(context, spotRepository, renderRepository, eventCollector, items)
val jobIds = renderItems.map { it.printJobId }.distinct() val jobIds = renderItems.map { it.printJobId }.distinct()
...@@ -61,24 +61,27 @@ internal class JobSendRepositoryImpl( ...@@ -61,24 +61,27 @@ internal class JobSendRepositoryImpl(
for ((index, item) in items.withIndex()) { for ((index, item) in items.withIndex()) {
eventCollector.updateProgress(index, total) eventCollector.updateProgress(index, total)
try { try {
sendJobLocalSource.updateStatus(item, SendJobStatus.SENDING) updateStatus(item, SendJobStatus.SENDING)
val uploadResult = sendJobNetworkSource.uploadPage(
jobId = item.printJobId,
token = item.accessToken,
filePath = item.sourceFile
)
val uploadResult = sendJobNetworkSource.uploadPage(jobId = item.printJobId, token = item.accessToken, filePath = item.sourceFile)
if (!uploadResult) if (!uploadResult)
throw UploadPageException() throw UploadPageException()
sendJobResults.decreaseCounter(item.printJobId) sendJobResults.decreaseCounter(item.printJobId)
sendJobLocalSource.updateStatus(item, SendJobStatus.COMPLETE) updateStatus(item, SendJobStatus.COMPLETE)
} catch (t: Throwable) { } catch (t: Throwable) {
sendJobLocalSource.updateStatus(item, SendJobStatus.RENDERED) updateStatus(item, SendJobStatus.RENDERED)
Timber.e(t) Timber.e(t)
eventCollector.jobError(jobId = item.printJobId, t = t) eventCollector.jobError(jobId = item.printJobId, t = t)
} }
} }
// Timber.i("===> readFilesAndSend sendJobResults=${sendJobResults}")
eventCollector.updateProgress(total, total) eventCollector.updateProgress(total, total)
// update jobs // update jobs
...@@ -93,10 +96,10 @@ internal class JobSendRepositoryImpl( ...@@ -93,10 +96,10 @@ internal class JobSendRepositoryImpl(
// notify event emitters // notify event emitters
if (successJobs.isNotEmpty()) if (successJobs.isNotEmpty())
eventCollector.updateStatus(status = JobStatus.RENDER_UPLOAD, *successJobs.toTypedArray()) eventCollector.updateStatus(status = JobStatus.RENDER_UPLOAD, *successJobs.map { it.id }.toTypedArray())
if (failedJobs.isNotEmpty()) if (failedJobs.isNotEmpty())
eventCollector.updateStatus(status = JobStatus.UPLOAD_ERROR, *failedJobs.toTypedArray()) eventCollector.updateStatus(status = JobStatus.UPLOAD_ERROR, *failedJobs.map { it.id }.toTypedArray())
return successJobs return successJobs
} }
...@@ -107,9 +110,22 @@ internal class JobSendRepositoryImpl( ...@@ -107,9 +110,22 @@ internal class JobSendRepositoryImpl(
val updateJob = completeUpload(jobId = job.id, token = job.accessToken.orEmpty()) val updateJob = completeUpload(jobId = job.id, token = job.accessToken.orEmpty())
// notify event emitters // notify event emitters
eventCollector.updateStatus(status = JobStatus.READY_TO_PRINT, updateJob) eventCollector.updateStatus(status = JobStatus.READY_TO_PRINT, updateJob?.id)
updateJob updateJob
} }
} }
private suspend fun updateStatus(item: PrintJobSender, @SendJobStatus status: Int) {
sendJobLocalSource.updateStatus(item, status)
val jobStatus = when (status) {
SendJobStatus.RENDERED -> JobStatus.READY
SendJobStatus.SENDING -> JobStatus.SENDING
SendJobStatus.COMPLETE -> JobStatus.READY_TO_PRINT
else -> return
}
eventCollector.updateStatus(jobStatus, item.printJobId)
}
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment