From e510de1cccf8fda19cabdb8e535731b433a1194d Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Sat, 1 May 2021 13:34:46 +0200 Subject: [PATCH] Display video/image compression progress --- .../content/ContentUploadStateTracker.kt | 2 + .../DefaultContentUploadStateTracker.kt | 10 +++ .../session/content/UploadContentWorker.kt | 9 ++- .../session/content/VideoCompressor.kt | 5 ++ .../helper/ContentUploadStateTrackerBinder.kt | 65 ++++++++++++------- vector/src/main/res/values/strings.xml | 2 + 6 files changed, 68 insertions(+), 25 deletions(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/content/ContentUploadStateTracker.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/content/ContentUploadStateTracker.kt index 924da6c19b..ec63eb0be2 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/content/ContentUploadStateTracker.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/content/ContentUploadStateTracker.kt @@ -31,6 +31,8 @@ interface ContentUploadStateTracker { sealed class State { object Idle : State() object EncryptingThumbnail : State() + object CompressingImage : State() + data class CompressingVideo(val percent: Float) : State() data class UploadingThumbnail(val current: Long, val total: Long) : State() data class Encrypting(val current: Long, val total: Long) : State() data class Uploading(val current: Long, val total: Long) : State() diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/content/DefaultContentUploadStateTracker.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/content/DefaultContentUploadStateTracker.kt index 754f12bd68..17e0a930c1 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/content/DefaultContentUploadStateTracker.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/content/DefaultContentUploadStateTracker.kt @@ -78,6 +78,16 @@ internal class DefaultContentUploadStateTracker @Inject constructor() : ContentU updateState(key, progressData) } + internal fun setCompressingImage(key: String) { + val progressData = ContentUploadStateTracker.State.CompressingImage + updateState(key, progressData) + } + + internal fun setCompressingVideo(key: String, percent: Float) { + val progressData = ContentUploadStateTracker.State.CompressingVideo(percent) + updateState(key, progressData) + } + internal fun setProgress(key: String, current: Long, total: Long) { val progressData = ContentUploadStateTracker.State.Uploading(current, total) updateState(key, progressData) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/content/UploadContentWorker.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/content/UploadContentWorker.kt index 4dec8a9c78..380f8ee9d1 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/content/UploadContentWorker.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/content/UploadContentWorker.kt @@ -21,6 +21,7 @@ import android.graphics.BitmapFactory import androidx.work.WorkerParameters import com.squareup.moshi.JsonClass import org.matrix.android.sdk.api.extensions.tryOrNull +import org.matrix.android.sdk.api.listeners.ProgressListener import org.matrix.android.sdk.api.session.content.ContentAttachmentData import org.matrix.android.sdk.api.session.events.model.toContent import org.matrix.android.sdk.api.session.events.model.toModel @@ -156,6 +157,8 @@ internal class UploadContentWorker(val context: Context, params: WorkerParameter // Do not compress gif && attachment.mimeType != MimeTypes.Gif && params.compressBeforeSending) { + notifyTracker(params) { contentUploadStateTracker.setCompressingImage(it) } + fileToUpload = imageCompressor.compress(workingFile, MAX_IMAGE_SIZE, MAX_IMAGE_SIZE) .also { compressedFile -> // Get new Bitmap size @@ -174,7 +177,11 @@ internal class UploadContentWorker(val context: Context, params: WorkerParameter // Do not compress gif && attachment.mimeType != MimeTypes.Gif && params.compressBeforeSending) { - fileToUpload = videoCompressor.compress(workingFile) + fileToUpload = videoCompressor.compress(workingFile, object: ProgressListener { + override fun onProgress(progress: Int, total: Int) { + notifyTracker(params) { contentUploadStateTracker.setCompressingVideo(it, progress.toFloat()) } + } + }) .also { compressedFile -> // Get new Video file size. For now video dimensions are not updated newAttachmentAttributes = newAttachmentAttributes.copy(newFileSize = compressedFile.length()) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/content/VideoCompressor.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/content/VideoCompressor.kt index 78624bbbf9..8d3425d0cf 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/content/VideoCompressor.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/content/VideoCompressor.kt @@ -23,6 +23,7 @@ import com.abedelazizshe.lightcompressorlibrary.VideoQuality import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job import kotlinx.coroutines.withContext +import org.matrix.android.sdk.api.listeners.ProgressListener import timber.log.Timber import java.io.File import java.util.UUID @@ -30,6 +31,7 @@ import javax.inject.Inject internal class VideoCompressor @Inject constructor(private val context: Context) { suspend fun compress(videoFile: File, + progressListener: ProgressListener?, quality: VideoQuality = VideoQuality.MEDIUM, isMinBitRateEnabled: Boolean = false, keepOriginalResolution: Boolean = true): File { @@ -46,14 +48,17 @@ internal class VideoCompressor @Inject constructor(private val context: Context) listener = object : CompressionListener { override fun onProgress(percent: Float) { Timber.d("Compressing: $percent%") + progressListener?.onProgress(percent.toInt(), 100) } override fun onStart() { Timber.d("Compressing: start") + progressListener?.onProgress(0, 100) } override fun onSuccess() { Timber.d("Compressing: success") + progressListener?.onProgress(100, 100) job.complete() } diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/ContentUploadStateTrackerBinder.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/ContentUploadStateTrackerBinder.kt index 8216d36ac9..2dd94ff244 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/ContentUploadStateTrackerBinder.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/ContentUploadStateTrackerBinder.kt @@ -25,6 +25,7 @@ import im.vector.app.R import im.vector.app.core.di.ActiveSessionHolder import im.vector.app.core.di.ScreenScope import im.vector.app.core.error.ErrorFormatter +import im.vector.app.core.extensions.exhaustive import im.vector.app.core.utils.TextUtils import im.vector.app.features.home.room.detail.timeline.MessageColorProvider import org.matrix.android.sdk.api.session.content.ContentUploadStateTracker @@ -70,6 +71,9 @@ private class ContentMediaProgressUpdater(private val progressLayout: ViewGroup, private val messageColorProvider: MessageColorProvider, private val errorFormatter: ErrorFormatter) : ContentUploadStateTracker.UpdateListener { + private val progressBar: ProgressBar = progressLayout.findViewById(R.id.mediaProgressBar) + private val progressTextView: TextView = progressLayout.findViewById(R.id.mediaProgressTextView) + override fun onUpdate(state: ContentUploadStateTracker.State) { when (state) { is ContentUploadStateTracker.State.Idle -> handleIdle() @@ -79,19 +83,19 @@ private class ContentMediaProgressUpdater(private val progressLayout: ViewGroup, is ContentUploadStateTracker.State.Uploading -> handleProgress(state) is ContentUploadStateTracker.State.Failure -> handleFailure(/*state*/) is ContentUploadStateTracker.State.Success -> handleSuccess() - } + is ContentUploadStateTracker.State.CompressingImage -> handleCompressingImage() + is ContentUploadStateTracker.State.CompressingVideo -> handleCompressingVideo(state) + }.exhaustive } private fun handleIdle() { if (isLocalFile) { progressLayout.isVisible = true - val progressBar = progressLayout.findViewById(R.id.mediaProgressBar) - val progressTextView = progressLayout.findViewById(R.id.mediaProgressTextView) - progressBar?.isVisible = true - progressBar?.isIndeterminate = true - progressBar?.progress = 0 - progressTextView?.text = progressLayout.context.getString(R.string.send_file_step_idle) - progressTextView?.setTextColor(messageColorProvider.getMessageTextColor(SendState.UNSENT)) + progressBar.isVisible = true + progressBar.isIndeterminate = true + progressBar.progress = 0 + progressTextView.text = progressLayout.context.getString(R.string.send_file_step_idle) + progressTextView.setTextColor(messageColorProvider.getMessageTextColor(SendState.UNSENT)) } else { progressLayout.isVisible = false } @@ -113,38 +117,51 @@ private class ContentMediaProgressUpdater(private val progressLayout: ViewGroup, doHandleProgress(R.string.send_file_step_sending_file, state.current, state.total) } + private fun handleCompressingImage() { + progressLayout.visibility = View.VISIBLE + progressBar.isVisible = true + progressBar.isIndeterminate = true + progressTextView.isVisible = true + progressTextView.text = progressLayout.context.getString(R.string.send_file_step_compressing_image) + progressTextView.setTextColor(messageColorProvider.getMessageTextColor(SendState.SENDING)) + } + + private fun handleCompressingVideo(state: ContentUploadStateTracker.State.CompressingVideo) { + progressLayout.visibility = View.VISIBLE + progressBar.isVisible = true + progressBar.isIndeterminate = false + progressBar.progress = state.percent.toInt() + progressTextView.isVisible = true + progressTextView.text = progressLayout.context.getString(R.string.send_file_step_compressing_video, state.percent.toInt()) + progressTextView.setTextColor(messageColorProvider.getMessageTextColor(SendState.SENDING)) + } + private fun doHandleEncrypting(resId: Int, current: Long, total: Long) { progressLayout.visibility = View.VISIBLE val percent = if (total > 0) (100L * (current.toFloat() / total.toFloat())) else 0f - val progressBar = progressLayout.findViewById(R.id.mediaProgressBar) - val progressTextView = progressLayout.findViewById(R.id.mediaProgressTextView) - progressBar?.isIndeterminate = false - progressBar?.progress = percent.toInt() + progressBar.isIndeterminate = false + progressBar.progress = percent.toInt() progressTextView.isVisible = true - progressTextView?.text = progressLayout.context.getString(resId) - progressTextView?.setTextColor(messageColorProvider.getMessageTextColor(SendState.ENCRYPTING)) + progressTextView.text = progressLayout.context.getString(resId) + progressTextView.setTextColor(messageColorProvider.getMessageTextColor(SendState.ENCRYPTING)) } private fun doHandleProgress(resId: Int, current: Long, total: Long) { progressLayout.visibility = View.VISIBLE val percent = 100L * (current.toFloat() / total.toFloat()) - val progressBar = progressLayout.findViewById(R.id.mediaProgressBar) - val progressTextView = progressLayout.findViewById(R.id.mediaProgressTextView) - progressBar?.isVisible = true - progressBar?.isIndeterminate = false - progressBar?.progress = percent.toInt() + progressBar.isVisible = true + progressBar.isIndeterminate = false + progressBar.progress = percent.toInt() progressTextView.isVisible = true - progressTextView?.text = progressLayout.context.getString(resId, + progressTextView.text = progressLayout.context.getString(resId, TextUtils.formatFileSize(progressLayout.context, current, true), TextUtils.formatFileSize(progressLayout.context, total, true)) - progressTextView?.setTextColor(messageColorProvider.getMessageTextColor(SendState.SENDING)) + progressTextView.setTextColor(messageColorProvider.getMessageTextColor(SendState.SENDING)) } private fun handleFailure(/*state: ContentUploadStateTracker.State.Failure*/) { progressLayout.visibility = View.VISIBLE - val progressBar = progressLayout.findViewById(R.id.mediaProgressBar) - val progressTextView = progressLayout.findViewById(R.id.mediaProgressTextView) - progressBar?.isVisible = false + progressBar.isVisible = false // Do not show the message it's too technical for users, and unfortunate when upload is cancelled // in the middle by turning airplane mode for example progressTextView.isVisible = false diff --git a/vector/src/main/res/values/strings.xml b/vector/src/main/res/values/strings.xml index eadc0db1f4..55a46a2d35 100644 --- a/vector/src/main/res/values/strings.xml +++ b/vector/src/main/res/values/strings.xml @@ -2174,6 +2174,8 @@ Sending thumbnail (%1$s / %2$s) Encrypting file… Sending file (%1$s / %2$s) + Compressing image… + Compressing video %d%% Downloading file %1$s… File %1$s has been downloaded!