Search code examples
androidandroid-jetpackandroid-workmanager

Unable to catch CancellationException in CoroutineWorker (WorkManager)


I am using a CoroutineWorker of WorkManager to download a long video. The download progress is shown in the notification along with an action to cancel the ongoing download. When the cancel action is done, the worker gets canceled successfully which can be seen in the log (The ongoing notification also gets removed). I am using FFmpeg for downloading the video which doesn't automatically cancel the ongoing download when the cancel button is pressed and hence I want to get the callback when the cancel action is done. By looking at the documentation and various samples, the best way to get the callback is by wrapping the code in the try-catch block. The below code shows my implementation.

override suspend fun doWork(): Result = coroutineScope {
        val videoTitle = removeSpecialCharacters(inputData.getString(KEY_VIDEO_TITLE)!!)
        val videoUrl = inputData.getString(KEY_VIDEO_URL)!!
        val videoDurationSeconds = inputData.getInt(KEY_VIDEO_DURATION_SECONDS, 0)
        val downloadNotificationId = (0..1000).random()

        setForeground(createForegroundInfo(0, videoTitle, downloadNotificationId))
        
        withContext(Dispatchers.Default) {
            try {
                downloadVideo(
                    videoTitle,
                    videoUrl,
                    videoDurationSeconds,
                    this@coroutineScope,
                    downloadNotificationId
                )
            } catch (e: Exception) {
                Timber.e("Exception: $ffmpegSessionId")
                Result.failure()
            } finally {
                // Cancel the ongoing download manually
                Timber.e("Exception: $ffmpegSessionId")
            }
        }
    }

The code for creating the foreground notification is shown below.

 private fun createForegroundInfo(
        downloadProgress: Int,
        videoTitle: String,
        downloadNotificationId: Int
    ): ForegroundInfo {
        NotificationUtils.createChannel(
            notificationManager,
            applicationContext,
            applicationContext.getString(R.string.app_download_channel_id),
            applicationContext.getString(R.string.app_download_channel_name),
            applicationContext.getString(R.string.app_download_channel_desc),
            NotificationManager.IMPORTANCE_LOW,
            null,
            false
        )

        val cancelText = applicationContext.getString(R.string.all_text_cancel)
        val cancelPendingIntent =
            WorkManager.getInstance(applicationContext).createCancelPendingIntent(id)

        val notification = NotificationCompat.Builder(
            applicationContext,
            applicationContext.getString(R.string.app_download_channel_id)
        )
            .setContentTitle(videoTitle)
            .setContentText(
                applicationContext.getString(
                    R.string.twitch_text_video_downloading,
                    downloadProgress
                )
            )
            .setTicker(applicationContext.getString(R.string.twitch_text_video_downloading))
            .setProgress(100, downloadProgress, false)
            .setSmallIcon(R.drawable.ic_stat_notification)
            .setOngoing(true)
            .addAction(R.drawable.ic_close, cancelText, cancelPendingIntent)
            .build()

        return ForegroundInfo(downloadNotificationId, notification)
    }

I would like to know if there is any recommended approach for getting the cancellation callback.


Solution

  • Once the CoroutineWorker is cancelled via the PendingIntent, you can use the isStopped property while the download is going on and cancel the ongoing work if needed.

    if (isStopped) {
    // Cancel ongoing work
    }