mirror of
https://github.com/vitorpamplona/amethyst.git
synced 2025-07-13 06:08:59 +02:00
Refactor out file operations into seperate helper
Additional tests for MediaCompressor
This commit is contained in:
@ -24,7 +24,6 @@ import android.content.Context
|
|||||||
import android.graphics.Bitmap
|
import android.graphics.Bitmap
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import android.webkit.MimeTypeMap
|
|
||||||
import androidx.core.net.toUri
|
import androidx.core.net.toUri
|
||||||
import com.abedelazizshe.lightcompressorlibrary.CompressionListener
|
import com.abedelazizshe.lightcompressorlibrary.CompressionListener
|
||||||
import com.abedelazizshe.lightcompressorlibrary.VideoCompressor
|
import com.abedelazizshe.lightcompressorlibrary.VideoCompressor
|
||||||
@ -33,13 +32,11 @@ import com.abedelazizshe.lightcompressorlibrary.config.AppSpecificStorageConfigu
|
|||||||
import com.abedelazizshe.lightcompressorlibrary.config.Configuration
|
import com.abedelazizshe.lightcompressorlibrary.config.Configuration
|
||||||
import com.vitorpamplona.amethyst.R
|
import com.vitorpamplona.amethyst.R
|
||||||
import com.vitorpamplona.amethyst.service.checkNotInMainThread
|
import com.vitorpamplona.amethyst.service.checkNotInMainThread
|
||||||
|
import com.vitorpamplona.amethyst.ui.components.util.MediaCompressorFileUtils
|
||||||
import id.zelory.compressor.Compressor
|
import id.zelory.compressor.Compressor
|
||||||
import id.zelory.compressor.constraint.default
|
import id.zelory.compressor.constraint.default
|
||||||
import kotlinx.coroutines.CancellationException
|
import kotlinx.coroutines.CancellationException
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.FileOutputStream
|
|
||||||
import java.io.InputStream
|
|
||||||
import java.io.OutputStream
|
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
|
|
||||||
class MediaCompressor {
|
class MediaCompressor {
|
||||||
@ -171,7 +168,7 @@ class MediaCompressor {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
Log.d("MediaCompressor", "Using image compression $mediaQuality")
|
Log.d("MediaCompressor", "Using image compression $mediaQuality")
|
||||||
val tempFile = from(uri, context)
|
val tempFile = MediaCompressorFileUtils.from(uri, context)
|
||||||
val compressedImageFile =
|
val compressedImageFile =
|
||||||
Compressor.compress(context, tempFile) {
|
Compressor.compress(context, tempFile) {
|
||||||
default(width = 640, format = Bitmap.CompressFormat.JPEG, quality = imageQuality)
|
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<String, String> {
|
|
||||||
val i = fileName.lastIndexOf(".")
|
|
||||||
return if (i != -1) {
|
|
||||||
fileName.substring(0, i) to fileName.substring(i)
|
|
||||||
} else {
|
|
||||||
fileName to ""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun intToCompressorQuality(mediaQualityFloat: Int): CompressorQuality =
|
fun intToCompressorQuality(mediaQualityFloat: Int): CompressorQuality =
|
||||||
when (mediaQualityFloat) {
|
when (mediaQualityFloat) {
|
||||||
0 -> CompressorQuality.LOW
|
0 -> CompressorQuality.LOW
|
||||||
|
@ -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<String, String> {
|
||||||
|
val i = fileName.lastIndexOf(".")
|
||||||
|
return if (i != -1) {
|
||||||
|
fileName.substring(0, i) to fileName.substring(i)
|
||||||
|
} else {
|
||||||
|
fileName to ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -23,19 +23,21 @@ package com.vitorpamplona.amethyst.ui.components
|
|||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.os.Looper
|
import android.os.Looper
|
||||||
import com.abedelazizshe.lightcompressorlibrary.VideoCompressor
|
import com.abedelazizshe.lightcompressorlibrary.VideoCompressor
|
||||||
|
import com.vitorpamplona.amethyst.ui.components.util.MediaCompressorFileUtils
|
||||||
import id.zelory.compressor.Compressor
|
import id.zelory.compressor.Compressor
|
||||||
import io.mockk.MockKAnnotations
|
import io.mockk.MockKAnnotations
|
||||||
import io.mockk.coEvery
|
import io.mockk.coEvery
|
||||||
import io.mockk.coVerify
|
import io.mockk.coVerify
|
||||||
import io.mockk.every
|
import io.mockk.every
|
||||||
import io.mockk.mockk
|
import io.mockk.mockk
|
||||||
|
import io.mockk.mockkClass
|
||||||
|
import io.mockk.mockkObject
|
||||||
import io.mockk.mockkStatic
|
import io.mockk.mockkStatic
|
||||||
import io.mockk.unmockkAll
|
import io.mockk.unmockkAll
|
||||||
import io.mockk.verify
|
import io.mockk.verify
|
||||||
import kotlinx.coroutines.test.runTest
|
import kotlinx.coroutines.test.runTest
|
||||||
import org.junit.After
|
import org.junit.After
|
||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
import org.junit.Ignore
|
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
@ -43,9 +45,8 @@ class MediaCompressorTest {
|
|||||||
@Before
|
@Before
|
||||||
fun setUp() {
|
fun setUp() {
|
||||||
// Mock compressors
|
// Mock compressors
|
||||||
mockkStatic(android.content.Context::class)
|
|
||||||
mockkStatic(VideoCompressor::class)
|
mockkStatic(VideoCompressor::class)
|
||||||
mockkStatic(Compressor::class)
|
mockkObject(Compressor)
|
||||||
|
|
||||||
// mock out main thread check
|
// mock out main thread check
|
||||||
mockkStatic(Looper::class)
|
mockkStatic(Looper::class)
|
||||||
@ -79,6 +80,7 @@ class MediaCompressorTest {
|
|||||||
|
|
||||||
// Verify
|
// Verify
|
||||||
verify(exactly = 0) { VideoCompressor.start(any(), any(), any(), any(), any(), any(), any()) }
|
verify(exactly = 0) { VideoCompressor.start(any(), any(), any(), any(), any(), any(), any()) }
|
||||||
|
coVerify(exactly = 0) { Compressor.compress(any(), any(), any(), any()) }
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -105,8 +107,8 @@ class MediaCompressorTest {
|
|||||||
verify(exactly = 1) { VideoCompressor.start(any(), any(), any(), any(), any(), any(), any()) }
|
verify(exactly = 1) { VideoCompressor.start(any(), any(), any(), any(), any(), any(), any()) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @Ignore("Bug in mockk https://github.com/mockk/mockk/issues/944")
|
||||||
@Test
|
@Test
|
||||||
@Ignore("Bug in mockk https://github.com/mockk/mockk/issues/944")
|
|
||||||
fun `Image media should invoke image compressor`() =
|
fun `Image media should invoke image compressor`() =
|
||||||
runTest {
|
runTest {
|
||||||
// setup
|
// setup
|
||||||
@ -114,19 +116,21 @@ class MediaCompressorTest {
|
|||||||
val uri = mockk<Uri>()
|
val uri = mockk<Uri>()
|
||||||
val contentType = "image"
|
val contentType = "image"
|
||||||
|
|
||||||
|
mockkObject(MediaCompressorFileUtils)
|
||||||
|
every { MediaCompressorFileUtils.from(any(), any()) } returns File("test")
|
||||||
coEvery { Compressor.compress(any(), any(), any(), any()) } returns File("test")
|
coEvery { Compressor.compress(any(), any(), any(), any()) } returns File("test")
|
||||||
|
|
||||||
// Execution
|
// Execution
|
||||||
MediaCompressor().compress(
|
MediaCompressor().compress(
|
||||||
uri,
|
uri,
|
||||||
contentType,
|
contentType,
|
||||||
applicationContext = mockk(),
|
applicationContext = mockkClass(android.content.Context::class, relaxed = true),
|
||||||
onReady = { _, _, _ -> },
|
onReady = { _, _, _ -> },
|
||||||
onError = { },
|
onError = { },
|
||||||
mediaQuality = mediaQuality,
|
mediaQuality = mediaQuality,
|
||||||
)
|
)
|
||||||
|
|
||||||
// Verify
|
// Verify
|
||||||
coVerify(exactly = 0) { Compressor.compress(any(), any(), any(), any()) }
|
coVerify(exactly = 1) { Compressor.compress(any(), any(), any(), any()) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user