Display video/image compression progress

This commit is contained in:
Benoit Marty 2021-05-01 13:34:46 +02:00 committed by Benoit Marty
parent 765380ab95
commit e510de1ccc
6 changed files with 68 additions and 25 deletions

View File

@ -31,6 +31,8 @@ interface ContentUploadStateTracker {
sealed class State { sealed class State {
object Idle : State() object Idle : State()
object EncryptingThumbnail : 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 UploadingThumbnail(val current: Long, val total: Long) : State()
data class Encrypting(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() data class Uploading(val current: Long, val total: Long) : State()

View File

@ -78,6 +78,16 @@ internal class DefaultContentUploadStateTracker @Inject constructor() : ContentU
updateState(key, progressData) 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) { internal fun setProgress(key: String, current: Long, total: Long) {
val progressData = ContentUploadStateTracker.State.Uploading(current, total) val progressData = ContentUploadStateTracker.State.Uploading(current, total)
updateState(key, progressData) updateState(key, progressData)

View File

@ -21,6 +21,7 @@ import android.graphics.BitmapFactory
import androidx.work.WorkerParameters import androidx.work.WorkerParameters
import com.squareup.moshi.JsonClass import com.squareup.moshi.JsonClass
import org.matrix.android.sdk.api.extensions.tryOrNull 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.content.ContentAttachmentData
import org.matrix.android.sdk.api.session.events.model.toContent import org.matrix.android.sdk.api.session.events.model.toContent
import org.matrix.android.sdk.api.session.events.model.toModel 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 // Do not compress gif
&& attachment.mimeType != MimeTypes.Gif && attachment.mimeType != MimeTypes.Gif
&& params.compressBeforeSending) { && params.compressBeforeSending) {
notifyTracker(params) { contentUploadStateTracker.setCompressingImage(it) }
fileToUpload = imageCompressor.compress(workingFile, MAX_IMAGE_SIZE, MAX_IMAGE_SIZE) fileToUpload = imageCompressor.compress(workingFile, MAX_IMAGE_SIZE, MAX_IMAGE_SIZE)
.also { compressedFile -> .also { compressedFile ->
// Get new Bitmap size // Get new Bitmap size
@ -174,7 +177,11 @@ internal class UploadContentWorker(val context: Context, params: WorkerParameter
// Do not compress gif // Do not compress gif
&& attachment.mimeType != MimeTypes.Gif && attachment.mimeType != MimeTypes.Gif
&& params.compressBeforeSending) { && 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 -> .also { compressedFile ->
// Get new Video file size. For now video dimensions are not updated // Get new Video file size. For now video dimensions are not updated
newAttachmentAttributes = newAttachmentAttributes.copy(newFileSize = compressedFile.length()) newAttachmentAttributes = newAttachmentAttributes.copy(newFileSize = compressedFile.length())

View File

@ -23,6 +23,7 @@ import com.abedelazizshe.lightcompressorlibrary.VideoQuality
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import org.matrix.android.sdk.api.listeners.ProgressListener
import timber.log.Timber import timber.log.Timber
import java.io.File import java.io.File
import java.util.UUID import java.util.UUID
@ -30,6 +31,7 @@ import javax.inject.Inject
internal class VideoCompressor @Inject constructor(private val context: Context) { internal class VideoCompressor @Inject constructor(private val context: Context) {
suspend fun compress(videoFile: File, suspend fun compress(videoFile: File,
progressListener: ProgressListener?,
quality: VideoQuality = VideoQuality.MEDIUM, quality: VideoQuality = VideoQuality.MEDIUM,
isMinBitRateEnabled: Boolean = false, isMinBitRateEnabled: Boolean = false,
keepOriginalResolution: Boolean = true): File { keepOriginalResolution: Boolean = true): File {
@ -46,14 +48,17 @@ internal class VideoCompressor @Inject constructor(private val context: Context)
listener = object : CompressionListener { listener = object : CompressionListener {
override fun onProgress(percent: Float) { override fun onProgress(percent: Float) {
Timber.d("Compressing: $percent%") Timber.d("Compressing: $percent%")
progressListener?.onProgress(percent.toInt(), 100)
} }
override fun onStart() { override fun onStart() {
Timber.d("Compressing: start") Timber.d("Compressing: start")
progressListener?.onProgress(0, 100)
} }
override fun onSuccess() { override fun onSuccess() {
Timber.d("Compressing: success") Timber.d("Compressing: success")
progressListener?.onProgress(100, 100)
job.complete() job.complete()
} }

View File

@ -25,6 +25,7 @@ import im.vector.app.R
import im.vector.app.core.di.ActiveSessionHolder import im.vector.app.core.di.ActiveSessionHolder
import im.vector.app.core.di.ScreenScope import im.vector.app.core.di.ScreenScope
import im.vector.app.core.error.ErrorFormatter 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.core.utils.TextUtils
import im.vector.app.features.home.room.detail.timeline.MessageColorProvider import im.vector.app.features.home.room.detail.timeline.MessageColorProvider
import org.matrix.android.sdk.api.session.content.ContentUploadStateTracker 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 messageColorProvider: MessageColorProvider,
private val errorFormatter: ErrorFormatter) : ContentUploadStateTracker.UpdateListener { 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) { override fun onUpdate(state: ContentUploadStateTracker.State) {
when (state) { when (state) {
is ContentUploadStateTracker.State.Idle -> handleIdle() 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.Uploading -> handleProgress(state)
is ContentUploadStateTracker.State.Failure -> handleFailure(/*state*/) is ContentUploadStateTracker.State.Failure -> handleFailure(/*state*/)
is ContentUploadStateTracker.State.Success -> handleSuccess() is ContentUploadStateTracker.State.Success -> handleSuccess()
} is ContentUploadStateTracker.State.CompressingImage -> handleCompressingImage()
is ContentUploadStateTracker.State.CompressingVideo -> handleCompressingVideo(state)
}.exhaustive
} }
private fun handleIdle() { private fun handleIdle() {
if (isLocalFile) { if (isLocalFile) {
progressLayout.isVisible = true progressLayout.isVisible = true
val progressBar = progressLayout.findViewById<ProgressBar>(R.id.mediaProgressBar) progressBar.isVisible = true
val progressTextView = progressLayout.findViewById<TextView>(R.id.mediaProgressTextView) progressBar.isIndeterminate = true
progressBar?.isVisible = true progressBar.progress = 0
progressBar?.isIndeterminate = true progressTextView.text = progressLayout.context.getString(R.string.send_file_step_idle)
progressBar?.progress = 0 progressTextView.setTextColor(messageColorProvider.getMessageTextColor(SendState.UNSENT))
progressTextView?.text = progressLayout.context.getString(R.string.send_file_step_idle)
progressTextView?.setTextColor(messageColorProvider.getMessageTextColor(SendState.UNSENT))
} else { } else {
progressLayout.isVisible = false 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) 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) { private fun doHandleEncrypting(resId: Int, current: Long, total: Long) {
progressLayout.visibility = View.VISIBLE progressLayout.visibility = View.VISIBLE
val percent = if (total > 0) (100L * (current.toFloat() / total.toFloat())) else 0f val percent = if (total > 0) (100L * (current.toFloat() / total.toFloat())) else 0f
val progressBar = progressLayout.findViewById<ProgressBar>(R.id.mediaProgressBar) progressBar.isIndeterminate = false
val progressTextView = progressLayout.findViewById<TextView>(R.id.mediaProgressTextView) progressBar.progress = percent.toInt()
progressBar?.isIndeterminate = false
progressBar?.progress = percent.toInt()
progressTextView.isVisible = true progressTextView.isVisible = true
progressTextView?.text = progressLayout.context.getString(resId) progressTextView.text = progressLayout.context.getString(resId)
progressTextView?.setTextColor(messageColorProvider.getMessageTextColor(SendState.ENCRYPTING)) progressTextView.setTextColor(messageColorProvider.getMessageTextColor(SendState.ENCRYPTING))
} }
private fun doHandleProgress(resId: Int, current: Long, total: Long) { private fun doHandleProgress(resId: Int, current: Long, total: Long) {
progressLayout.visibility = View.VISIBLE progressLayout.visibility = View.VISIBLE
val percent = 100L * (current.toFloat() / total.toFloat()) val percent = 100L * (current.toFloat() / total.toFloat())
val progressBar = progressLayout.findViewById<ProgressBar>(R.id.mediaProgressBar) progressBar.isVisible = true
val progressTextView = progressLayout.findViewById<TextView>(R.id.mediaProgressTextView) progressBar.isIndeterminate = false
progressBar?.isVisible = true progressBar.progress = percent.toInt()
progressBar?.isIndeterminate = false
progressBar?.progress = percent.toInt()
progressTextView.isVisible = true 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, current, true),
TextUtils.formatFileSize(progressLayout.context, total, 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*/) { private fun handleFailure(/*state: ContentUploadStateTracker.State.Failure*/) {
progressLayout.visibility = View.VISIBLE progressLayout.visibility = View.VISIBLE
val progressBar = progressLayout.findViewById<ProgressBar>(R.id.mediaProgressBar) progressBar.isVisible = false
val progressTextView = progressLayout.findViewById<TextView>(R.id.mediaProgressTextView)
progressBar?.isVisible = false
// Do not show the message it's too technical for users, and unfortunate when upload is cancelled // 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 // in the middle by turning airplane mode for example
progressTextView.isVisible = false progressTextView.isVisible = false

View File

@ -2174,6 +2174,8 @@
<string name="send_file_step_sending_thumbnail">Sending thumbnail (%1$s / %2$s)</string> <string name="send_file_step_sending_thumbnail">Sending thumbnail (%1$s / %2$s)</string>
<string name="send_file_step_encrypting_file">Encrypting file…</string> <string name="send_file_step_encrypting_file">Encrypting file…</string>
<string name="send_file_step_sending_file">Sending file (%1$s / %2$s)</string> <string name="send_file_step_sending_file">Sending file (%1$s / %2$s)</string>
<string name="send_file_step_compressing_image">Compressing image…</string>
<string name="send_file_step_compressing_video">Compressing video %d%%</string>
<string name="downloading_file">Downloading file %1$s…</string> <string name="downloading_file">Downloading file %1$s…</string>
<string name="downloaded_file">File %1$s has been downloaded!</string> <string name="downloaded_file">File %1$s has been downloaded!</string>