diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/MediaCompressor.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/MediaCompressor.kt index 405e0d32b..96b6c8ecc 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/MediaCompressor.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/MediaCompressor.kt @@ -24,7 +24,6 @@ import android.content.Context import android.graphics.Bitmap import android.net.Uri import android.util.Log -import android.webkit.MimeTypeMap import androidx.core.net.toUri import com.abedelazizshe.lightcompressorlibrary.CompressionListener import com.abedelazizshe.lightcompressorlibrary.VideoCompressor @@ -33,13 +32,11 @@ import com.abedelazizshe.lightcompressorlibrary.config.AppSpecificStorageConfigu import com.abedelazizshe.lightcompressorlibrary.config.Configuration import com.vitorpamplona.amethyst.R import com.vitorpamplona.amethyst.service.checkNotInMainThread +import com.vitorpamplona.amethyst.ui.components.util.MediaCompressorFileUtils import id.zelory.compressor.Compressor import id.zelory.compressor.constraint.default import kotlinx.coroutines.CancellationException import java.io.File -import java.io.FileOutputStream -import java.io.InputStream -import java.io.OutputStream import java.util.UUID class MediaCompressor { @@ -171,7 +168,7 @@ class MediaCompressor { try { Log.d("MediaCompressor", "Using image compression $mediaQuality") - val tempFile = from(uri, context) + val tempFile = MediaCompressorFileUtils.from(uri, context) val compressedImageFile = Compressor.compress(context, tempFile) { default(width = 640, format = Bitmap.CompressFormat.JPEG, quality = imageQuality) @@ -186,42 +183,6 @@ class MediaCompressor { } } - private fun from( - uri: Uri, - context: Context, - ): File { - val extension = MimeTypeMap.getSingleton().getExtensionFromMimeType(context.contentResolver.getType(uri)) ?: "" - val inputStream = context.contentResolver.openInputStream(uri)!! - val fileName = UUID.randomUUID().toString() + ".$extension" - val (name, ext) = splitFileName(fileName) - val tempFile = File.createTempFile(name, ext) - - copyStream(inputStream, FileOutputStream(tempFile)) - - return tempFile - } - - private fun copyStream( - input: InputStream, - output: OutputStream, - ) { - val buffer = ByteArray(1024 * 50) - var read = input.read(buffer) - while (read != -1) { - output.write(buffer, 0, read) - read = input.read(buffer) - } - } - - private fun splitFileName(fileName: String): Pair { - val i = fileName.lastIndexOf(".") - return if (i != -1) { - fileName.substring(0, i) to fileName.substring(i) - } else { - fileName to "" - } - } - fun intToCompressorQuality(mediaQualityFloat: Int): CompressorQuality = when (mediaQualityFloat) { 0 -> CompressorQuality.LOW diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/util/MediaCompressorFileUtils.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/util/MediaCompressorFileUtils.kt new file mode 100644 index 000000000..504fc2b8a --- /dev/null +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/util/MediaCompressorFileUtils.kt @@ -0,0 +1,68 @@ +/** + * Copyright (c) 2024 Vitor Pamplona + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the + * Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package com.vitorpamplona.amethyst.ui.components.util + +import android.content.Context +import android.net.Uri +import android.webkit.MimeTypeMap +import java.io.File +import java.io.FileOutputStream +import java.io.InputStream +import java.io.OutputStream +import java.util.UUID + +object MediaCompressorFileUtils { + fun from( + uri: Uri, + context: Context, + ): File { + val extension = MimeTypeMap.getSingleton().getExtensionFromMimeType(context.contentResolver.getType(uri)) ?: "" + val inputStream = context.contentResolver.openInputStream(uri)!! + val fileName = UUID.randomUUID().toString() + ".$extension" + val (name, ext) = splitFileName(fileName) + val tempFile = File.createTempFile(name, ext) + + copyStream(inputStream, FileOutputStream(tempFile)) + + return tempFile + } + + private fun copyStream( + input: InputStream, + output: OutputStream, + ) { + val buffer = ByteArray(1024 * 50) + var read = input.read(buffer) + while (read != -1) { + output.write(buffer, 0, read) + read = input.read(buffer) + } + } + + private fun splitFileName(fileName: String): Pair { + val i = fileName.lastIndexOf(".") + return if (i != -1) { + fileName.substring(0, i) to fileName.substring(i) + } else { + fileName to "" + } + } +} diff --git a/amethyst/src/test/java/com/vitorpamplona/amethyst/ui/components/MediaCompressorTest.kt b/amethyst/src/test/java/com/vitorpamplona/amethyst/ui/components/MediaCompressorTest.kt index d0ab42afd..9b0cc1c28 100644 --- a/amethyst/src/test/java/com/vitorpamplona/amethyst/ui/components/MediaCompressorTest.kt +++ b/amethyst/src/test/java/com/vitorpamplona/amethyst/ui/components/MediaCompressorTest.kt @@ -23,19 +23,21 @@ package com.vitorpamplona.amethyst.ui.components import android.net.Uri import android.os.Looper import com.abedelazizshe.lightcompressorlibrary.VideoCompressor +import com.vitorpamplona.amethyst.ui.components.util.MediaCompressorFileUtils import id.zelory.compressor.Compressor import io.mockk.MockKAnnotations import io.mockk.coEvery import io.mockk.coVerify import io.mockk.every import io.mockk.mockk +import io.mockk.mockkClass +import io.mockk.mockkObject import io.mockk.mockkStatic import io.mockk.unmockkAll import io.mockk.verify import kotlinx.coroutines.test.runTest import org.junit.After import org.junit.Before -import org.junit.Ignore import org.junit.Test import java.io.File @@ -43,9 +45,8 @@ class MediaCompressorTest { @Before fun setUp() { // Mock compressors - mockkStatic(android.content.Context::class) mockkStatic(VideoCompressor::class) - mockkStatic(Compressor::class) + mockkObject(Compressor) // mock out main thread check mockkStatic(Looper::class) @@ -79,6 +80,7 @@ class MediaCompressorTest { // Verify verify(exactly = 0) { VideoCompressor.start(any(), any(), any(), any(), any(), any(), any()) } + coVerify(exactly = 0) { Compressor.compress(any(), any(), any(), any()) } } @Test @@ -105,8 +107,8 @@ class MediaCompressorTest { verify(exactly = 1) { VideoCompressor.start(any(), any(), any(), any(), any(), any(), any()) } } + // @Ignore("Bug in mockk https://github.com/mockk/mockk/issues/944") @Test - @Ignore("Bug in mockk https://github.com/mockk/mockk/issues/944") fun `Image media should invoke image compressor`() = runTest { // setup @@ -114,19 +116,21 @@ class MediaCompressorTest { val uri = mockk() val contentType = "image" + mockkObject(MediaCompressorFileUtils) + every { MediaCompressorFileUtils.from(any(), any()) } returns File("test") coEvery { Compressor.compress(any(), any(), any(), any()) } returns File("test") // Execution MediaCompressor().compress( uri, contentType, - applicationContext = mockk(), + applicationContext = mockkClass(android.content.Context::class, relaxed = true), onReady = { _, _, _ -> }, onError = { }, mediaQuality = mediaQuality, ) // Verify - coVerify(exactly = 0) { Compressor.compress(any(), any(), any(), any()) } + coVerify(exactly = 1) { Compressor.compress(any(), any(), any(), any()) } } }