Compress video before sending (#442)

This commit is contained in:
Benoit Marty 2021-04-30 14:04:56 +02:00 committed by Benoit Marty
parent a377a595b9
commit 5e1c503d2e
8 changed files with 123 additions and 6 deletions

View file

@ -7,6 +7,7 @@ Features ✨:
Improvements 🙌:
- Add ability to install APK from directly from Element (#2381)
- Delete and react to stickers (#3250)
- Compress video before sending (#442)
Bugfix 🐛:
- Message states cosmetic changes (#3007)

View file

@ -45,9 +45,11 @@ allprojects {
// PFLockScreen-Android
includeGroupByRegex 'com\\.github\\.vector-im'
//Chat effects
// Chat effects
includeGroupByRegex 'com\\.github\\.jetradarmobile'
includeGroupByRegex 'nl\\.dionsegijn'
// LightCompressor
includeGroupByRegex 'com\\.github\\.AbedElazizShe'
}
}
maven {

View file

@ -168,6 +168,9 @@ dependencies {
implementation 'com.jakewharton.timber:timber:4.7.1'
implementation 'com.facebook.stetho:stetho-okhttp3:1.6.0'
// Video compression
implementation 'com.github.AbedElazizShe:LightCompressor:0.9.0'
// Phone number https://github.com/google/libphonenumber
implementation 'com.googlecode.libphonenumber:libphonenumber:8.12.22'

View file

@ -77,6 +77,7 @@ internal class UploadContentWorker(val context: Context, params: WorkerParameter
@Inject lateinit var fileService: DefaultFileService
@Inject lateinit var cancelSendTracker: CancelSendTracker
@Inject lateinit var imageCompressor: ImageCompressor
@Inject lateinit var videoCompressor: VideoCompressor
@Inject lateinit var localEchoRepository: LocalEchoRepository
override fun injectWith(injector: SessionComponent) {
@ -170,6 +171,18 @@ internal class UploadContentWorker(val context: Context, params: WorkerParameter
}
}
.also { filesToDelete.add(it) }
} else if (attachment.type == ContentAttachmentData.Type.VIDEO
// Do not compress gif
&& attachment.mimeType != MimeTypes.Gif
&& params.compressBeforeSending) {
fileToUpload = videoCompressor.compress(workingFile)
.also { compressedFile ->
// Get new Video size
newAttachmentAttributes = NewAttachmentAttributes(
newFileSize = compressedFile.length()
)
}
.also { filesToDelete.add(it) }
} else {
fileToUpload = workingFile
// Fix: OpenableColumns.SIZE may return -1 or 0

View file

@ -0,0 +1,83 @@
/*
* Copyright 2021 The Matrix.org Foundation C.I.C.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.matrix.android.sdk.internal.session.content
import android.content.Context
import com.abedelazizshe.lightcompressorlibrary.CompressionListener
import com.abedelazizshe.lightcompressorlibrary.VideoCompressor
import com.abedelazizshe.lightcompressorlibrary.VideoQuality
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.withContext
import timber.log.Timber
import java.io.File
import java.util.UUID
import javax.inject.Inject
internal class VideoCompressor @Inject constructor(private val context: Context) {
suspend fun compress(videoFile: File,
quality: VideoQuality = VideoQuality.MEDIUM,
isMinBitRateEnabled: Boolean = false,
keepOriginalResolution: Boolean = true): File {
return withContext(Dispatchers.IO) {
val job = Job()
val destinationFile = createDestinationFile()
// Sadly it does not return the Job, the API is not ideal
VideoCompressor.start(
context = null,
srcUri = null,
srcPath = videoFile.path,
destPath = destinationFile.path,
listener = object : CompressionListener {
override fun onProgress(percent: Float) {
Timber.d("Compressing: $percent%")
}
override fun onStart() {
Timber.d("Compressing: start")
}
override fun onSuccess() {
Timber.d("Compressing: success")
job.complete()
}
override fun onFailure(failureMessage: String) {
Timber.d("Compressing: failure: $failureMessage")
job.completeExceptionally(Exception(failureMessage))
}
override fun onCancelled() {
Timber.d("Compressing: cancel")
job.cancel()
}
},
quality = quality,
isMinBitRateEnabled = isMinBitRateEnabled,
keepOriginalResolution = keepOriginalResolution
)
job.join()
destinationFile
}
}
private fun createDestinationFile(): File {
return File.createTempFile(UUID.randomUUID().toString(), null, context.cacheDir)
}
}

View file

@ -21,15 +21,15 @@ import org.matrix.android.sdk.api.util.MimeTypes
private val listOfPreviewableMimeTypes = listOf(
MimeTypes.Jpeg,
MimeTypes.BadJpg,
MimeTypes.Png,
MimeTypes.Gif
)
fun ContentAttachmentData.isPreviewable(): Boolean {
// For now the preview only supports still image
return type == ContentAttachmentData.Type.IMAGE
&& listOfPreviewableMimeTypes.contains(getSafeMimeType() ?: "")
// Preview supports image and video
return (type == ContentAttachmentData.Type.IMAGE
&& listOfPreviewableMimeTypes.contains(getSafeMimeType() ?: ""))
|| type == ContentAttachmentData.Type.VIDEO
}
data class GroupedContentAttachmentData(

View file

@ -139,7 +139,17 @@ class AttachmentsPreviewFragment @Inject constructor(
attachmentBigPreviewController.setData(state)
views.attachmentPreviewerBigList.scrollToPosition(state.currentAttachmentIndex)
views.attachmentPreviewerMiniatureList.scrollToPosition(state.currentAttachmentIndex)
views.attachmentPreviewerSendImageOriginalSize.text = resources.getQuantityString(R.plurals.send_images_with_original_size, state.attachments.size)
views.attachmentPreviewerSendImageOriginalSize.text = getCheckboxText(state)
}
}
private fun getCheckboxText(state: AttachmentsPreviewViewState): CharSequence {
val nbImages = state.attachments.count { it.type == ContentAttachmentData.Type.IMAGE }
val nbVideos = state.attachments.count { it.type == ContentAttachmentData.Type.VIDEO }
return when {
nbVideos == 0 -> resources.getQuantityString(R.plurals.send_images_with_original_size, nbImages)
nbImages == 0 -> resources.getQuantityString(R.plurals.send_videos_with_original_size, nbVideos)
else -> getString(R.string.send_images_and_video_with_original_size)
}
}

View file

@ -2785,6 +2785,11 @@
<item quantity="one">Send image with the original size</item>
<item quantity="other">Send images with the original size</item>
</plurals>
<plurals name="send_videos_with_original_size">
<item quantity="one">Send video with the original size</item>
<item quantity="other">Send videos with the original size</item>
</plurals>
<string name="send_images_and_video_with_original_size">Send media with the original size</string>
<string name="delete_event_dialog_title">Confirm Removal</string>
<string name="delete_event_dialog_content">Are you sure you wish to remove (delete) this event? Note that if you delete a room name or topic change, it could undo the change.</string>