From 8a5612be3dca636ea9c7cb99e041aa3394a9d5d0 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 8 Jul 2019 14:31:19 +0200 Subject: [PATCH] Send file: improve UI feedback --- .../content/ContentUploadStateTracker.kt | 7 ++- .../DefaultContentUploadStateTracker.kt | 19 +++++- .../session/content/UploadContentWorker.kt | 22 +++++-- .../helper/ContentUploadStateTrackerBinder.kt | 58 ++++++++++++++----- vector/src/main/res/values/strings_riotX.xml | 6 ++ 5 files changed, 90 insertions(+), 22 deletions(-) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/content/ContentUploadStateTracker.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/content/ContentUploadStateTracker.kt index 9211371dd5..83074525d8 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/content/ContentUploadStateTracker.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/content/ContentUploadStateTracker.kt @@ -28,10 +28,11 @@ interface ContentUploadStateTracker { sealed class State { object Idle : State() + object EncryptingThumbnail : State() + data class ProgressThumbnailData(val current: Long, val total: Long) : State() + object Encrypting : State() data class ProgressData(val current: Long, val total: Long) : State() object Success : State() - object Failure : State() + data class Failure(val throwable: Throwable) : State() } - - } \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/content/DefaultContentUploadStateTracker.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/content/DefaultContentUploadStateTracker.kt index fbd7983d96..391a90f17d 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/content/DefaultContentUploadStateTracker.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/content/DefaultContentUploadStateTracker.kt @@ -43,8 +43,8 @@ internal class DefaultContentUploadStateTracker @Inject constructor() : ContentU } } - internal fun setFailure(key: String) { - val failure = ContentUploadStateTracker.State.Failure + internal fun setFailure(key: String, throwable: Throwable) { + val failure = ContentUploadStateTracker.State.Failure(throwable) updateState(key, failure) } @@ -53,6 +53,21 @@ internal class DefaultContentUploadStateTracker @Inject constructor() : ContentU updateState(key, success) } + internal fun setEncryptingThumbnail(key: String) { + val progressData = ContentUploadStateTracker.State.EncryptingThumbnail + updateState(key, progressData) + } + + internal fun setProgressThumbnail(key: String, current: Long, total: Long) { + val progressData = ContentUploadStateTracker.State.ProgressThumbnailData(current, total) + updateState(key, progressData) + } + + internal fun setEncrypting(key: String) { + val progressData = ContentUploadStateTracker.State.Encrypting + updateState(key, progressData) + } + internal fun setProgress(key: String, current: Long, total: Long) { val progressData = ContentUploadStateTracker.State.ProgressData(current, total) updateState(key, progressData) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/content/UploadContentWorker.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/content/UploadContentWorker.kt index 37f57e4bfc..0a2aca99a0 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/content/UploadContentWorker.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/content/UploadContentWorker.kt @@ -73,7 +73,7 @@ internal class UploadContentWorker(context: Context, params: WorkerParameters) : File(attachment.path) } catch (e: Exception) { Timber.e(e) - contentUploadStateTracker.setFailure(params.event.eventId) + contentUploadStateTracker.setFailure(params.event.eventId, e) return Result.success( WorkerParamsFactory.toData(params.copy( lastFailureMessage = e.localizedMessage @@ -85,18 +85,31 @@ internal class UploadContentWorker(context: Context, params: WorkerParameters) : var uploadedThumbnailEncryptedFileInfo: EncryptedFileInfo? = null ThumbnailExtractor.extractThumbnail(params.attachment)?.let { thumbnailData -> + val thumbnailProgressListener = object : ProgressRequestBody.Listener { + override fun onProgress(current: Long, total: Long) { + contentUploadStateTracker.setProgressThumbnail(eventId, current, total) + } + } + val contentUploadResponse = if (params.isRoomEncrypted) { Timber.v("Encrypt thumbnail") + contentUploadStateTracker.setEncryptingThumbnail(eventId) MXEncryptedAttachments.encryptAttachment(ByteArrayInputStream(thumbnailData.bytes), thumbnailData.mimeType) .flatMap { encryptionResult -> uploadedThumbnailEncryptedFileInfo = encryptionResult.encryptedFileInfo fileUploader - .uploadByteArray(encryptionResult.encryptedByteArray, "thumb_${attachment.name}", "application/octet-stream") + .uploadByteArray(encryptionResult.encryptedByteArray, + "thumb_${attachment.name}", + "application/octet-stream", + thumbnailProgressListener) } } else { fileUploader - .uploadByteArray(thumbnailData.bytes, "thumb_${attachment.name}", thumbnailData.mimeType) + .uploadByteArray(thumbnailData.bytes, + "thumb_${attachment.name}", + thumbnailData.mimeType, + thumbnailProgressListener) } contentUploadResponse @@ -116,6 +129,7 @@ internal class UploadContentWorker(context: Context, params: WorkerParameters) : val contentUploadResponse = if (params.isRoomEncrypted) { Timber.v("Encrypt file") + contentUploadStateTracker.setEncrypting(eventId) MXEncryptedAttachments.encryptAttachment(FileInputStream(attachmentFile), attachment.mimeType) .flatMap { encryptionResult -> @@ -137,7 +151,7 @@ internal class UploadContentWorker(context: Context, params: WorkerParameters) : } private fun handleFailure(params: Params, failure: Throwable): Result { - contentUploadStateTracker.setFailure(params.event.eventId!!) + contentUploadStateTracker.setFailure(params.event.eventId!!, failure) return Result.success( WorkerParamsFactory.toData( params.copy( diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/helper/ContentUploadStateTrackerBinder.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/helper/ContentUploadStateTrackerBinder.kt index fa4f05f1ec..a431d409c3 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/helper/ContentUploadStateTrackerBinder.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/helper/ContentUploadStateTrackerBinder.kt @@ -16,12 +16,12 @@ package im.vector.riotx.features.home.room.detail.timeline.helper -import android.content.Context import android.text.format.Formatter import android.view.View import android.view.ViewGroup import android.widget.ProgressBar import android.widget.TextView +import androidx.core.view.isVisible import im.vector.matrix.android.api.session.content.ContentUploadStateTracker import im.vector.riotx.R import im.vector.riotx.core.di.ActiveSessionHolder @@ -61,10 +61,13 @@ private class ContentMediaProgressUpdater(private val progressLayout: ViewGroup, override fun onUpdate(state: ContentUploadStateTracker.State) { when (state) { - is ContentUploadStateTracker.State.Idle -> handleIdle(state) - is ContentUploadStateTracker.State.Failure -> handleFailure(state) - is ContentUploadStateTracker.State.Success -> handleSuccess(state) - is ContentUploadStateTracker.State.ProgressData -> handleProgress(state) + is ContentUploadStateTracker.State.Idle -> handleIdle(state) + is ContentUploadStateTracker.State.EncryptingThumbnail -> handleEncryptingThumbnail(state) + is ContentUploadStateTracker.State.ProgressThumbnailData -> handleProgressThumbnail(state) + is ContentUploadStateTracker.State.Encrypting -> handleEncrypting(state) + is ContentUploadStateTracker.State.ProgressData -> handleProgress(state) + is ContentUploadStateTracker.State.Failure -> handleFailure(state) + is ContentUploadStateTracker.State.Success -> handleSuccess(state) } } @@ -74,32 +77,61 @@ private class ContentMediaProgressUpdater(private val progressLayout: ViewGroup, progressLayout.visibility = View.VISIBLE 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 = formatStats(progressLayout.context, 0L, file.length()) + progressTextView?.text = progressLayout.context.getString(R.string.send_file_step_idle) } else { progressLayout.visibility = View.GONE } } - private fun handleFailure(state: ContentUploadStateTracker.State.Failure) { - + private fun handleEncryptingThumbnail(state: ContentUploadStateTracker.State.EncryptingThumbnail) { + _handleEncrypting(R.string.send_file_step_encrypting_thumbnail) } - private fun handleSuccess(state: ContentUploadStateTracker.State.Success) { + private fun handleProgressThumbnail(state: ContentUploadStateTracker.State.ProgressThumbnailData) { + _handleProgress(R.string.send_file_step_sending_thumbnail, state.current, state.total) + } + private fun handleEncrypting(state: ContentUploadStateTracker.State.Encrypting) { + _handleEncrypting(R.string.send_file_step_encrypting_file) } private fun handleProgress(state: ContentUploadStateTracker.State.ProgressData) { + _handleProgress(R.string.send_file_step_sending_file, state.current, state.total) + } + + private fun _handleEncrypting(resId: Int) { progressLayout.visibility = View.VISIBLE - val percent = 100L * (state.current.toFloat() / state.total.toFloat()) val progressBar = progressLayout.findViewById(R.id.mediaProgressBar) val progressTextView = progressLayout.findViewById(R.id.mediaProgressTextView) + progressBar?.isIndeterminate = true + progressTextView?.text = progressLayout.context.getString(resId) + } + + private fun _handleProgress(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() - progressTextView?.text = formatStats(progressLayout.context, state.current, state.total) + progressTextView?.text = progressLayout.context.getString(resId, + Formatter.formatShortFileSize(progressLayout.context, current), + Formatter.formatShortFileSize(progressLayout.context, total)) } - private fun formatStats(context: Context, current: Long, total: Long): String { - return "${Formatter.formatShortFileSize(context, current)} / ${Formatter.formatShortFileSize(context, total)}" + 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 + progressTextView?.text = state.throwable.localizedMessage } + private fun handleSuccess(state: ContentUploadStateTracker.State.Success) { + // Nothing to do + } } diff --git a/vector/src/main/res/values/strings_riotX.xml b/vector/src/main/res/values/strings_riotX.xml index 1539d84dbe..50b1cd8308 100644 --- a/vector/src/main/res/values/strings_riotX.xml +++ b/vector/src/main/res/values/strings_riotX.xml @@ -5,5 +5,11 @@ Direct Messages + Waiting… + Encrypting thumbnail… + Sending thumbnail (%1$s / %2$s) + Encrypting file… + Sending file (%1$s / %2$s) + \ No newline at end of file