Compress image before sending
This commit is contained in:
parent
385fa317c0
commit
e6bd09859f
@ -119,6 +119,7 @@ dependencies {
|
|||||||
|
|
||||||
// Image
|
// Image
|
||||||
implementation 'androidx.exifinterface:exifinterface:1.1.0'
|
implementation 'androidx.exifinterface:exifinterface:1.1.0'
|
||||||
|
implementation 'id.zelory:compressor:3.0.0'
|
||||||
|
|
||||||
// Database
|
// Database
|
||||||
implementation 'com.github.Zhuinden:realm-monarchy:0.5.1'
|
implementation 'com.github.Zhuinden:realm-monarchy:0.5.1'
|
||||||
|
@ -51,26 +51,24 @@ interface SendService {
|
|||||||
/**
|
/**
|
||||||
* Method to send a media asynchronously.
|
* Method to send a media asynchronously.
|
||||||
* @param attachment the media to send
|
* @param attachment the media to send
|
||||||
* @param compressBeforeSending set to true to compress media before sending them
|
* @param compressBeforeSending set to true to compress images before sending them
|
||||||
* @param roomIds set of roomIds to where the media will be sent. The current roomId will be add to this set if not present.
|
* @param roomIds set of roomIds to where the media will be sent. The current roomId will be add to this set if not present.
|
||||||
* It can be useful to send media to multiple room. It's safe to include the current roomId in this set
|
* It can be useful to send media to multiple room. It's safe to include the current roomId in this set
|
||||||
* @return a [Cancelable]
|
* @return a [Cancelable]
|
||||||
*/
|
*/
|
||||||
fun sendMedia(attachment: ContentAttachmentData,
|
fun sendMedia(attachment: ContentAttachmentData,
|
||||||
// TODO Change to a Compression Level Enum
|
|
||||||
compressBeforeSending: Boolean,
|
compressBeforeSending: Boolean,
|
||||||
roomIds: Set<String>): Cancelable
|
roomIds: Set<String>): Cancelable
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Method to send a list of media asynchronously.
|
* Method to send a list of media asynchronously.
|
||||||
* @param attachments the list of media to send
|
* @param attachments the list of media to send
|
||||||
* @param compressBeforeSending set to true to compress media before sending them
|
* @param compressBeforeSending set to true to compress images before sending them
|
||||||
* @param roomIds set of roomIds to where the media will be sent. The current roomId will be add to this set if not present.
|
* @param roomIds set of roomIds to where the media will be sent. The current roomId will be add to this set if not present.
|
||||||
* It can be useful to send media to multiple room. It's safe to include the current roomId in this set
|
* It can be useful to send media to multiple room. It's safe to include the current roomId in this set
|
||||||
* @return a [Cancelable]
|
* @return a [Cancelable]
|
||||||
*/
|
*/
|
||||||
fun sendMedias(attachments: List<ContentAttachmentData>,
|
fun sendMedias(attachments: List<ContentAttachmentData>,
|
||||||
// TODO Change to a Compression Level Enum
|
|
||||||
compressBeforeSending: Boolean,
|
compressBeforeSending: Boolean,
|
||||||
roomIds: Set<String>): Cancelable
|
roomIds: Set<String>): Cancelable
|
||||||
|
|
||||||
|
@ -17,9 +17,12 @@
|
|||||||
package im.vector.matrix.android.internal.session.content
|
package im.vector.matrix.android.internal.session.content
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import android.graphics.BitmapFactory
|
||||||
import androidx.work.CoroutineWorker
|
import androidx.work.CoroutineWorker
|
||||||
import androidx.work.WorkerParameters
|
import androidx.work.WorkerParameters
|
||||||
import com.squareup.moshi.JsonClass
|
import com.squareup.moshi.JsonClass
|
||||||
|
import id.zelory.compressor.Compressor
|
||||||
|
import id.zelory.compressor.constraint.default
|
||||||
import im.vector.matrix.android.api.session.content.ContentAttachmentData
|
import im.vector.matrix.android.api.session.content.ContentAttachmentData
|
||||||
import im.vector.matrix.android.api.session.events.model.Event
|
import im.vector.matrix.android.api.session.events.model.Event
|
||||||
import im.vector.matrix.android.api.session.events.model.toContent
|
import im.vector.matrix.android.api.session.events.model.toContent
|
||||||
@ -42,7 +45,13 @@ import java.io.File
|
|||||||
import java.io.FileInputStream
|
import java.io.FileInputStream
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
internal class UploadContentWorker(context: Context, params: WorkerParameters) : CoroutineWorker(context, params) {
|
private data class NewImageAttributes(
|
||||||
|
val newWidth: Int?,
|
||||||
|
val newHeight: Int?,
|
||||||
|
val newFileSize: Int
|
||||||
|
)
|
||||||
|
|
||||||
|
internal class UploadContentWorker(val context: Context, params: WorkerParameters) : CoroutineWorker(context, params) {
|
||||||
|
|
||||||
@JsonClass(generateAdapter = true)
|
@JsonClass(generateAdapter = true)
|
||||||
internal data class Params(
|
internal data class Params(
|
||||||
@ -73,6 +82,8 @@ internal class UploadContentWorker(context: Context, params: WorkerParameters) :
|
|||||||
|
|
||||||
val attachment = params.attachment
|
val attachment = params.attachment
|
||||||
|
|
||||||
|
var newImageAttributes: NewImageAttributes? = null
|
||||||
|
|
||||||
val attachmentFile = try {
|
val attachmentFile = try {
|
||||||
File(attachment.path)
|
File(attachment.path)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
@ -88,8 +99,35 @@ internal class UploadContentWorker(context: Context, params: WorkerParameters) :
|
|||||||
))
|
))
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
.let {originalFile ->
|
||||||
|
if (attachment.type == ContentAttachmentData.Type.IMAGE) {
|
||||||
|
if (params.compressBeforeSending) {
|
||||||
|
Compressor.compress(context, originalFile) {
|
||||||
|
default(
|
||||||
|
width = MAX_IMAGE_SIZE,
|
||||||
|
height = MAX_IMAGE_SIZE
|
||||||
|
)
|
||||||
|
}.also { compressedFile ->
|
||||||
|
// Update the params
|
||||||
|
val options = BitmapFactory.Options().apply { inJustDecodeBounds = true }
|
||||||
|
BitmapFactory.decodeFile(compressedFile.absolutePath, options)
|
||||||
|
val fileSize = compressedFile.length().toInt()
|
||||||
|
|
||||||
// TODO Use compressBeforeSending
|
newImageAttributes = NewImageAttributes(
|
||||||
|
options.outWidth,
|
||||||
|
options.outHeight,
|
||||||
|
fileSize
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// TODO Fix here the image rotation issue
|
||||||
|
originalFile
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Other type
|
||||||
|
originalFile
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var uploadedThumbnailUrl: String? = null
|
var uploadedThumbnailUrl: String? = null
|
||||||
var uploadedThumbnailEncryptedFileInfo: EncryptedFileInfo? = null
|
var uploadedThumbnailEncryptedFileInfo: EncryptedFileInfo? = null
|
||||||
@ -168,7 +206,12 @@ internal class UploadContentWorker(context: Context, params: WorkerParameters) :
|
|||||||
.uploadFile(attachmentFile, attachment.name, attachment.mimeType, progressListener)
|
.uploadFile(attachmentFile, attachment.name, attachment.mimeType, progressListener)
|
||||||
}
|
}
|
||||||
|
|
||||||
handleSuccess(params, contentUploadResponse.contentUri, uploadedFileEncryptedFileInfo, uploadedThumbnailUrl, uploadedThumbnailEncryptedFileInfo)
|
handleSuccess(params,
|
||||||
|
contentUploadResponse.contentUri,
|
||||||
|
uploadedFileEncryptedFileInfo,
|
||||||
|
uploadedThumbnailUrl,
|
||||||
|
uploadedThumbnailEncryptedFileInfo,
|
||||||
|
newImageAttributes)
|
||||||
} catch (t: Throwable) {
|
} catch (t: Throwable) {
|
||||||
Timber.e(t)
|
Timber.e(t)
|
||||||
handleFailure(params, t)
|
handleFailure(params, t)
|
||||||
@ -195,7 +238,8 @@ internal class UploadContentWorker(context: Context, params: WorkerParameters) :
|
|||||||
attachmentUrl: String,
|
attachmentUrl: String,
|
||||||
encryptedFileInfo: EncryptedFileInfo?,
|
encryptedFileInfo: EncryptedFileInfo?,
|
||||||
thumbnailUrl: String?,
|
thumbnailUrl: String?,
|
||||||
thumbnailEncryptedFileInfo: EncryptedFileInfo?): Result {
|
thumbnailEncryptedFileInfo: EncryptedFileInfo?,
|
||||||
|
newImageAttributes: NewImageAttributes?): Result {
|
||||||
Timber.v("handleSuccess $attachmentUrl, work is stopped $isStopped")
|
Timber.v("handleSuccess $attachmentUrl, work is stopped $isStopped")
|
||||||
params.events
|
params.events
|
||||||
.mapNotNull { it.eventId }
|
.mapNotNull { it.eventId }
|
||||||
@ -205,7 +249,7 @@ internal class UploadContentWorker(context: Context, params: WorkerParameters) :
|
|||||||
|
|
||||||
val updatedEvents = params.events
|
val updatedEvents = params.events
|
||||||
.map {
|
.map {
|
||||||
updateEvent(it, attachmentUrl, encryptedFileInfo, thumbnailUrl, thumbnailEncryptedFileInfo)
|
updateEvent(it, attachmentUrl, encryptedFileInfo, thumbnailUrl, thumbnailEncryptedFileInfo, newImageAttributes)
|
||||||
}
|
}
|
||||||
|
|
||||||
val sendParams = MultipleEventSendingDispatcherWorker.Params(params.sessionId, updatedEvents, params.isRoomEncrypted)
|
val sendParams = MultipleEventSendingDispatcherWorker.Params(params.sessionId, updatedEvents, params.isRoomEncrypted)
|
||||||
@ -216,10 +260,11 @@ internal class UploadContentWorker(context: Context, params: WorkerParameters) :
|
|||||||
url: String,
|
url: String,
|
||||||
encryptedFileInfo: EncryptedFileInfo?,
|
encryptedFileInfo: EncryptedFileInfo?,
|
||||||
thumbnailUrl: String? = null,
|
thumbnailUrl: String? = null,
|
||||||
thumbnailEncryptedFileInfo: EncryptedFileInfo?): Event {
|
thumbnailEncryptedFileInfo: EncryptedFileInfo?,
|
||||||
|
newImageAttributes: NewImageAttributes?): Event {
|
||||||
val messageContent: MessageContent = event.content.toModel() ?: return event
|
val messageContent: MessageContent = event.content.toModel() ?: return event
|
||||||
val updatedContent = when (messageContent) {
|
val updatedContent = when (messageContent) {
|
||||||
is MessageImageContent -> messageContent.update(url, encryptedFileInfo)
|
is MessageImageContent -> messageContent.update(url, encryptedFileInfo, newImageAttributes)
|
||||||
is MessageVideoContent -> messageContent.update(url, encryptedFileInfo, thumbnailUrl, thumbnailEncryptedFileInfo)
|
is MessageVideoContent -> messageContent.update(url, encryptedFileInfo, thumbnailUrl, thumbnailEncryptedFileInfo)
|
||||||
is MessageFileContent -> messageContent.update(url, encryptedFileInfo)
|
is MessageFileContent -> messageContent.update(url, encryptedFileInfo)
|
||||||
is MessageAudioContent -> messageContent.update(url, encryptedFileInfo)
|
is MessageAudioContent -> messageContent.update(url, encryptedFileInfo)
|
||||||
@ -229,10 +274,16 @@ internal class UploadContentWorker(context: Context, params: WorkerParameters) :
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun MessageImageContent.update(url: String,
|
private fun MessageImageContent.update(url: String,
|
||||||
encryptedFileInfo: EncryptedFileInfo?): MessageImageContent {
|
encryptedFileInfo: EncryptedFileInfo?,
|
||||||
|
newImageAttributes: NewImageAttributes?): MessageImageContent {
|
||||||
return copy(
|
return copy(
|
||||||
url = if (encryptedFileInfo == null) url else null,
|
url = if (encryptedFileInfo == null) url else null,
|
||||||
encryptedFileInfo = encryptedFileInfo?.copy(url = url)
|
encryptedFileInfo = encryptedFileInfo?.copy(url = url),
|
||||||
|
info = info?.copy(
|
||||||
|
width = newImageAttributes?.newWidth ?: info.width,
|
||||||
|
height = newImageAttributes?.newHeight ?: info.height,
|
||||||
|
size = newImageAttributes?.newFileSize ?: info.size
|
||||||
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -265,4 +316,8 @@ internal class UploadContentWorker(context: Context, params: WorkerParameters) :
|
|||||||
encryptedFileInfo = encryptedFileInfo?.copy(url = url)
|
encryptedFileInfo = encryptedFileInfo?.copy(url = url)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val MAX_IMAGE_SIZE = 640
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -314,6 +314,11 @@ SOFTWARE.
|
|||||||
<br/>
|
<br/>
|
||||||
Copyright (c) 2012-2016 Dan Wheeler and Dropbox, Inc.
|
Copyright (c) 2012-2016 Dan Wheeler and Dropbox, Inc.
|
||||||
</li>
|
</li>
|
||||||
|
<li>
|
||||||
|
<b>Compressor</b>
|
||||||
|
<br/>
|
||||||
|
Copyright (c) 2016 Zetra.
|
||||||
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<b>com.otaliastudios:autocomplete</b>
|
<b>com.otaliastudios:autocomplete</b>
|
||||||
<br/>
|
<br/>
|
||||||
|
@ -124,6 +124,7 @@ class AttachmentsPreviewFragment @Inject constructor(
|
|||||||
attachmentBigPreviewController.setData(state)
|
attachmentBigPreviewController.setData(state)
|
||||||
attachmentPreviewerBigList.scrollToPosition(state.currentAttachmentIndex)
|
attachmentPreviewerBigList.scrollToPosition(state.currentAttachmentIndex)
|
||||||
attachmentPreviewerMiniatureList.scrollToPosition(state.currentAttachmentIndex)
|
attachmentPreviewerMiniatureList.scrollToPosition(state.currentAttachmentIndex)
|
||||||
|
attachmentPreviewerSendImageOriginalSize.text = resources.getQuantityString(R.plurals.send_images_with_original_size, state.attachments.size)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -508,7 +508,7 @@ class RoomDetailFragment @Inject constructor(
|
|||||||
AttachmentsPreviewActivity.REQUEST_CODE -> {
|
AttachmentsPreviewActivity.REQUEST_CODE -> {
|
||||||
val sendData = AttachmentsPreviewActivity.getOutput(data)
|
val sendData = AttachmentsPreviewActivity.getOutput(data)
|
||||||
val keepOriginalSize = AttachmentsPreviewActivity.getKeepOriginalSize(data)
|
val keepOriginalSize = AttachmentsPreviewActivity.getKeepOriginalSize(data)
|
||||||
roomDetailViewModel.handle(RoomDetailAction.SendMedia(sendData, keepOriginalSize))
|
roomDetailViewModel.handle(RoomDetailAction.SendMedia(sendData, !keepOriginalSize))
|
||||||
}
|
}
|
||||||
REACTION_SELECT_REQUEST_CODE -> {
|
REACTION_SELECT_REQUEST_CODE -> {
|
||||||
val (eventId, reaction) = EmojiReactionPickerActivity.getOutput(data) ?: return
|
val (eventId, reaction) = EmojiReactionPickerActivity.getOutput(data) ?: return
|
||||||
@ -1352,7 +1352,7 @@ class RoomDetailFragment @Inject constructor(
|
|||||||
override fun onContentAttachmentsReady(attachments: List<ContentAttachmentData>) {
|
override fun onContentAttachmentsReady(attachments: List<ContentAttachmentData>) {
|
||||||
val grouped = attachments.toGroupedContentAttachmentData()
|
val grouped = attachments.toGroupedContentAttachmentData()
|
||||||
if (grouped.notPreviewables.isNotEmpty()) {
|
if (grouped.notPreviewables.isNotEmpty()) {
|
||||||
// Send the not previewable attachment right now (?)
|
// Send the not previewable attachments right now (?)
|
||||||
roomDetailViewModel.handle(RoomDetailAction.SendMedia(grouped.notPreviewables, false))
|
roomDetailViewModel.handle(RoomDetailAction.SendMedia(grouped.notPreviewables, false))
|
||||||
}
|
}
|
||||||
if (grouped.previewables.isNotEmpty()) {
|
if (grouped.previewables.isNotEmpty()) {
|
||||||
|
@ -142,7 +142,7 @@ class IncomingShareViewModel @AssistedInject constructor(@Assisted initialState:
|
|||||||
if (proposeMediaEdition) {
|
if (proposeMediaEdition) {
|
||||||
val grouped = attachmentData.toGroupedContentAttachmentData()
|
val grouped = attachmentData.toGroupedContentAttachmentData()
|
||||||
if (grouped.notPreviewables.isNotEmpty()) {
|
if (grouped.notPreviewables.isNotEmpty()) {
|
||||||
// Send the not previewable attachment right now (?)
|
// Send the not previewable attachments right now (?)
|
||||||
// Pick the first room to send the media
|
// Pick the first room to send the media
|
||||||
selectedRoomIds.firstOrNull()
|
selectedRoomIds.firstOrNull()
|
||||||
?.let { roomId -> session.getRoom(roomId) }
|
?.let { roomId -> session.getRoom(roomId) }
|
||||||
|
Loading…
x
Reference in New Issue
Block a user