Skip to content

Instantly share code, notes, and snippets.

@stakancheck
Created April 17, 2024 12:35
Show Gist options
  • Save stakancheck/f8dbc666ef97b97bf680e8605e28b587 to your computer and use it in GitHub Desktop.
Save stakancheck/f8dbc666ef97b97bf680e8605e28b587 to your computer and use it in GitHub Desktop.
GeneratePDFWorker example
@HiltWorker
class GeneratePDFWorker @AssistedInject constructor(
@Assisted context: Context,
@Assisted params: WorkerParameters,
private val repository: Repository
) : CoroutineWorker(context, params) {
private val notificationBuilder: NotificationCompat.Builder
private var pdfGenerator: PdfGenerator? = null
init {
Log.d(TAG, "initialize: GeneratePDFWorker")
notificationBuilder = NotificationCompat.Builder(
applicationContext,
applicationContext.getString(R.string.notification_channel_name)
)
Handler(Looper.getMainLooper()).post {
pdfGenerator = PdfGenerator(
applicationContext
)
}
}
override suspend fun doWork(): Result {
Log.d(TAG, "doWork: before generating act")
return generateData()
}
override suspend fun getForegroundInfo(): ForegroundInfo {
createNotificationChannel()
return ForegroundInfo(NOTIFICATION_ID_WORKER, foregroundNotification())
}
private suspend fun generateData(): Result {
Log.d(TAG, "generateData: start generate data function")
// Подготавливаем к работе (чистим пустые директории в internal storage)
repository.checkAndClearDirs()
// Получаем список уже готовых документов
val alreadyGeneratedIds = repository.getLocalPdfsIds()
// Получаем список данных для генерации и убираем уже сгенерированные.
// Делаем это, потому что мы не удаляем информацию, так как при ошибке генерации, данные могут снова понадобится.
val actIds = repository.getLocalActsIds().filter { act -> !alreadyGeneratedIds.contains(act) }
Log.d(TAG, "generateData: ACTS TO GENERATE: $actIds")
if (actIds.isEmpty()) {
if (alreadyGeneratedIds.isNotEmpty()) {
SendDataWorker.startSendDataWorker(applicationContext)
}
return Result.success()
}
// Начинаем работу, отправляем уведомление с прогрессом и кол-вом задач
sendNotification(progressNotification(0, actIds.size))
// Будем записывать успешные операции
val successOperations: MutableList<String> = mutableListOf()
var progress = 0 // Всего проверено данных
var retry = false // Нужно ли перевыполнить worker
for (id: String in actIds) {
Log.d(TAG, "generateData: save act $id")
// Получаем данные из internal storage
val act = repository.getLocalAct(id)
if (act == null) {
Log.e(TAG, "generateData: can't get act -> $id")
retry = true
} else {
try {
// Вызываем генерацию
generateAndSavePdf(
act = act,
id = id
).onSuccess {
Log.d(TAG, "generateData: successfully created $id")
successOperations.add(id)
}.onFailure {
Log.e(TAG, "generateData: save", it)
retry = true
}
} catch (e: Exception) {
Log.e(TAG, "generateData: generate or save error", e)
retry = true
}
}
// Обновляем прогресс
progress += 1
sendNotification(progressNotification(progress, actIds.size))
}
if (successOperations.isNotEmpty()) {
// Вызываем Worker отправки данных
SendDataWorker.startSendDataWorker(applicationContext)
}
// Заканчиваем работу, уведомляем о результате
return if (retry) {
sendNotification(errorNotification("Не удалось сгенерировать акты, произошла ошибка. Задача будет отложена."))
Result.retry() // Worker сам перезапустится
} else {
sendNotification(successNotification("Акты сгенерированы успешно. Кол-во: ${successOperations.size}/$progress"))
Result.success()
}
}
private suspend fun generateAndSavePdf(
act: ActSave,
id: String
): kotlin.Result<Unit> {
pdfGenerator
?: return kotlin.Result.failure(IllegalStateException("PDF generator is not initialized"))
Log.d(TAG, "createPDF: type ${act.type}")
return pdfGenerator!!.generateExpertAct(
act = act,
images = repository.getImagesForAct(id), // Получаем файлы изображений для акта
outFile = repository.preparePdfFile(id) // Создаем файл для сохранения PDF
)
}
private fun sendNotification(notification: Notification) {
val notificationManager =
getSystemService(applicationContext, NotificationManager::class.java)
createNotificationChannel()
notificationManager?.notify(NOTIFICATION_ID_PROGRESS, notification)
}
private fun foregroundNotification(): Notification {
return notificationBuilder
.setContentTitle("Приложение - Генерация отчетов")
.setProgress(0, 0, false)
.setSilent(true)
.setSmallIcon(R.drawable.ic_time_min)
.build()
}
private fun createNotificationChannel() {
val notificationManager =
getSystemService(applicationContext, NotificationManager::class.java)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val channel = NotificationChannel(
applicationContext.getString(R.string.notification_channel_name),
applicationContext.getString(R.string.notification_channel_generate_description),
NotificationManager.IMPORTANCE_DEFAULT
)
notificationManager?.createNotificationChannel(channel)
}
}
private fun successNotification(message: String = "Все отчеты успешно сгенерированы"): Notification {
return notificationBuilder
.setContentTitle("Приложение - Генерация отчетов")
.setContentText(message)
.setProgress(0, 0, false)
.setSilent(false)
.setColor(Color.GREEN)
.setSmallIcon(R.drawable.ic_check)
.build()
}
private fun errorNotification(message: String): Notification {
return notificationBuilder
.setContentTitle("Приложение - Генерация отчетов")
.setContentText(message)
.setColor(Color.RED)
.setProgress(0, 0, false)
.setSilent(true)
.setSmallIcon(R.drawable.ic_cancel)
.build()
}
private fun progressNotification(
progress: Int,
goal: Int,
message: String = "Генерация отчетов"
): Notification {
return notificationBuilder
.setContentTitle("Приложение - Генерация отчетов")
.setContentText(message)
.setSilent(true)
.setProgress(goal, progress, false)
.setSmallIcon(R.drawable.ic_upload)
.build()
}
companion object {
private const val WORK_NAME = "example.background.worker.GeneratePDFWorker"
private const val TAG: String = "GeneratePDFWorker"
private const val NOTIFICATION_ID_WORKER = 13
private const val NOTIFICATION_ID_PROGRESS = 24
fun startGeneratePDFWorker(
context: Context,
policy: ExistingWorkPolicy = ExistingWorkPolicy.REPLACE
) {
Log.d(TAG, "startGeneratePDFWorker: start enqueue work")
val generateDataWorkRequest = OneTimeWorkRequestBuilder<GeneratePDFWorker>()
.build()
val workManager: WorkManager = WorkManager.getInstance(context)
workManager.enqueueUniqueWork(
WORK_NAME,
policy,
generateDataWorkRequest
)
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment