Add crop and rotate editing before sending images (#6363)
* feat(messages): add crop and rotate before image upload * Update screenshots * chore: trigger CI after screenshot update * fix: resolve detekt violations in image editor and media viewer modules * fix: require explicit edits param, use plurals for rotation a11y, remove redundant @Inject * fix: require explicit edits param, use plurals for rotation a11y, remove redundant @Inject * fix: use semantically correct RotateRight icon for image rotation action * Update screenshots * chore: trigger CI after screenshot update --------- Co-authored-by: ElementBot <android@element.io> Co-authored-by: Benoit Marty <benoitm@element.io>
This commit is contained in:
parent
00efd9a01c
commit
bcad1f9dce
17 changed files with 1517 additions and 49 deletions
|
|
@ -9,7 +9,9 @@
|
|||
package io.element.android.libraries.mediaviewer.impl.local
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.BitmapFactory
|
||||
import android.net.Uri
|
||||
import android.webkit.MimeTypeMap
|
||||
import androidx.core.net.toUri
|
||||
import dev.zacsweers.metro.AppScope
|
||||
import dev.zacsweers.metro.ContributesBinding
|
||||
|
|
@ -17,6 +19,7 @@ import io.element.android.libraries.androidutils.file.getFileName
|
|||
import io.element.android.libraries.androidutils.file.getFileSize
|
||||
import io.element.android.libraries.androidutils.file.getMimeType
|
||||
import io.element.android.libraries.androidutils.filesize.FileSizeFormatter
|
||||
import io.element.android.libraries.core.data.tryOrNull
|
||||
import io.element.android.libraries.core.mimetype.MimeTypes
|
||||
import io.element.android.libraries.di.annotations.ApplicationContext
|
||||
import io.element.android.libraries.matrix.api.core.UserId
|
||||
|
|
@ -85,8 +88,12 @@ class AndroidLocalMediaFactory(
|
|||
waveform: List<Float>?,
|
||||
duration: String?,
|
||||
): LocalMedia {
|
||||
val resolvedMimeType = mimeType ?: context.getMimeType(uri) ?: MimeTypes.OctetStream
|
||||
val fileName = name ?: context.getFileName(uri) ?: ""
|
||||
val resolvedMimeType = resolveMimeType(
|
||||
uri = uri,
|
||||
mimeType = mimeType,
|
||||
fileName = fileName,
|
||||
)
|
||||
val fileSize = context.getFileSize(uri)
|
||||
val calculatedFormattedFileSize = formattedFileSize ?: fileSizeFormatter.format(fileSize)
|
||||
val fileExtension = fileExtensionExtractor.extractFromName(fileName)
|
||||
|
|
@ -110,4 +117,36 @@ class AndroidLocalMediaFactory(
|
|||
)
|
||||
)
|
||||
}
|
||||
|
||||
private fun resolveMimeType(
|
||||
uri: Uri,
|
||||
mimeType: String?,
|
||||
fileName: String,
|
||||
): String {
|
||||
val explicitMimeType = mimeType.takeUnless { it.isNullOrBlank() || it == MimeTypes.OctetStream }
|
||||
if (explicitMimeType != null) return explicitMimeType
|
||||
|
||||
val resolverMimeType = context.getMimeType(uri).takeUnless { it.isNullOrBlank() || it == MimeTypes.OctetStream }
|
||||
if (resolverMimeType != null) return resolverMimeType
|
||||
|
||||
val decodedImageMimeType = decodeImageMimeType(uri)
|
||||
if (decodedImageMimeType != null) return decodedImageMimeType
|
||||
|
||||
val extensionMimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(
|
||||
fileExtensionExtractor.extractFromName(fileName)
|
||||
)
|
||||
if (!extensionMimeType.isNullOrBlank()) return extensionMimeType
|
||||
|
||||
return MimeTypes.OctetStream
|
||||
}
|
||||
|
||||
private fun decodeImageMimeType(uri: Uri): String? {
|
||||
return tryOrNull {
|
||||
context.contentResolver.openInputStream(uri)?.use { inputStream ->
|
||||
val options = BitmapFactory.Options().apply { inJustDecodeBounds = true }
|
||||
BitmapFactory.decodeStream(inputStream, null, options)
|
||||
options.outMimeType
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,7 +8,9 @@
|
|||
|
||||
package io.element.android.libraries.mediaviewer.impl.local
|
||||
|
||||
import android.graphics.Bitmap
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import io.element.android.libraries.androidutils.file.getMimeType
|
||||
import io.element.android.libraries.androidutils.filesize.FakeFileSizeFormatter
|
||||
import io.element.android.libraries.core.mimetype.MimeTypes
|
||||
import io.element.android.libraries.matrix.api.media.MediaFile
|
||||
|
|
@ -22,9 +24,13 @@ import org.junit.Test
|
|||
import org.junit.runner.RunWith
|
||||
import org.robolectric.RobolectricTestRunner
|
||||
import org.robolectric.RuntimeEnvironment
|
||||
import java.io.File
|
||||
import java.io.FileOutputStream
|
||||
|
||||
@RunWith(RobolectricTestRunner::class)
|
||||
class AndroidLocalMediaFactoryTest {
|
||||
private val context = RuntimeEnvironment.getApplication()
|
||||
|
||||
@Test
|
||||
fun `test AndroidLocalMediaFactory`() {
|
||||
val sut = createAndroidLocalMediaFactory()
|
||||
|
|
@ -58,13 +64,34 @@ class AndroidLocalMediaFactoryTest {
|
|||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `createFromUri detects image mime type from content when picker mime type is generic`() {
|
||||
val imageFile = File(context.cacheDir, "picked-media").apply {
|
||||
FileOutputStream(this).use { outputStream ->
|
||||
Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888)
|
||||
.compress(Bitmap.CompressFormat.PNG, 100, outputStream)
|
||||
}
|
||||
}
|
||||
|
||||
val result = createAndroidLocalMediaFactory().createFromUri(
|
||||
uri = imageFile.toURI().toString().let(android.net.Uri::parse),
|
||||
mimeType = MimeTypes.OctetStream,
|
||||
name = imageFile.name,
|
||||
formattedFileSize = null,
|
||||
)
|
||||
|
||||
assertThat(context.getMimeType(result.uri)).isNull()
|
||||
assertThat(result.info.mimeType).isEqualTo(MimeTypes.Png)
|
||||
assertThat(result.info.fileExtension).isEmpty()
|
||||
}
|
||||
|
||||
private fun aMediaFile(): MediaFile {
|
||||
return FakeMediaFile("aPath")
|
||||
}
|
||||
|
||||
private fun createAndroidLocalMediaFactory(): AndroidLocalMediaFactory {
|
||||
return AndroidLocalMediaFactory(
|
||||
RuntimeEnvironment.getApplication(),
|
||||
context,
|
||||
FakeFileSizeFormatter(),
|
||||
FileExtensionExtractorWithoutValidation()
|
||||
)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue