Skip to content

Commit

Permalink
refactor: cải thiện mã nguồn
Browse files Browse the repository at this point in the history
  • Loading branch information
nqmgaming committed Dec 19, 2024
1 parent dcfe0ab commit e7ebc63
Show file tree
Hide file tree
Showing 13 changed files with 96 additions and 63 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -128,9 +128,13 @@ fun CreateFlashCardScreen(
scope.launch {
val compressedImageBytes = imageCompressor.compressImage(uri, 200 * 1024L) // 200KB
val compressedImageUri = compressedImageBytes?.let {
Uri.fromFile(File(context.cacheDir, "compressed_image.jpg").apply {
writeBytes(it)
})
Uri.fromFile(
File(
context.cacheDir,
"compressed_image_${System.currentTimeMillis()}.jpg"
).apply {
writeBytes(it)
})
}
viewModel.onEvent(
CreateFlashCardUiAction.FlashCardDefinitionImageChanged(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -130,9 +130,13 @@ fun EditFlashCardScreen(
scope.launch {
val compressedImageBytes = imageCompressor.compressImage(uri, 200 * 1024L) // 200KB
val compressedImageUri = compressedImageBytes?.let {
Uri.fromFile(File(context.cacheDir, "compressed_image.jpg").apply {
writeBytes(it)
})
Uri.fromFile(
File(
context.cacheDir,
"compressed_image_${System.currentTimeMillis()}.jpg"
).apply {
writeBytes(it)
})
}
viewModel.onEvent(
EditFlashCardUiAction.FlashCardDefinitionImageChanged(
Expand Down
1 change: 0 additions & 1 deletion easycrop/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ plugins {
android {
namespace = "com.mr0xf00.easycrop"
compileSdk = libs.versions.compileSdk.get().toInt()
compileSdk = libs.versions.compileSdk.get().toInt()

defaultConfig {
minSdk = libs.versions.minSdk.get().toInt()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,10 @@ class ResultTest {
}

private fun imageStream(name: String): ImageStream {
return ImageStream { javaClass.classLoader!!.getResourceAsStream(name) }
return ImageStream {
javaClass.classLoader?.getResourceAsStream(name)
?: throw IllegalStateException("ClassLoader not found")
}
}
}

Expand Down
4 changes: 4 additions & 0 deletions easycrop/src/main/java/com/mr0xf00/easycrop/CropShapes.kt
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,10 @@ val StarCropShape = CropShape { rect ->
}

data class RoundRectCropShape(private val cornersPercent: Int) : CropShape {
init {
require(cornersPercent in 0..100) { "Corners percent must be in [0, 100]" }
}

override fun asPath(rect: Rect): Path {
val radius = CornerRadius(rect.minDimension * cornersPercent / 100f)
return Path().apply { addRoundRect(RoundRect(rect = rect, radius)) }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ import android.content.Context
import android.net.Uri
import androidx.compose.ui.unit.IntSize
import com.mr0xf00.easycrop.images.toImageSrc
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.io.File
Expand Down Expand Up @@ -59,7 +59,7 @@ private suspend fun <R> cacheUri(

@OptIn(DelicateCoroutinesApi::class)
private fun File.deleteInBackground() {
GlobalScope.launch(Dispatchers.IO) { runCatching { delete() } }
CoroutineScope(Dispatchers.IO).launch { runCatching { delete() } }
}

private suspend fun copy(src: Uri, dst: File, context: Context) = withContext(Dispatchers.IO) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,7 @@ internal fun animateImgTransform(target: ImgTransform): State<ImgTransform> {
current.value = (a.lerp(target, p))
}
} finally {
current.value = (target)
prev = target
current.value = target.also { prev = it }
}
}
return current
Expand Down
8 changes: 4 additions & 4 deletions easycrop/src/main/java/com/mr0xf00/easycrop/images/Decode.kt
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,16 @@ data class DecodeParams(val sampleSize: Int, val subset: IntRect)
data class DecodeResult(val params: DecodeParams, val bmp: ImageBitmap)

internal fun calculateSampleSize(imgRegion: IntSize, view: IntSize): Int {
val imgArea = imgRegion.let { it.width.toDouble() * it.height }
val viewArea = view.let { it.width.toDouble() * it.height }
return (imgArea / viewArea).toFloat().align(2).coerceIn(1f, 32f).toInt()
val ratio =
(imgRegion.width.toDouble() / view.width) * (imgRegion.height.toDouble() / view.height)
return ratio.toFloat().align(2).coerceIn(1f, 32f).toInt()
}

private fun getImageSubset(
view: IntSize, viewToImg: Matrix, imgRect: IntRect, align: Boolean
): IntRect {
return viewToImg
.map(view.toSize().toRect()).let{ if(align) it.align(128) else it }
.map(view.toSize().toRect()).let { if (align) it.align(128) else it }
.roundOut().intersect(imgRect)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,17 @@ internal fun interface ImageStream {
fun openStream(): InputStream?
}

internal suspend fun Uri.toImageSrc(context: Context) = ImageStreamSrc(UriImageStream(this, context))
internal suspend fun Uri.toImageSrc(context: Context) =
ImageStreamSrc(UriImageStream(this, context))

internal suspend fun File.toImageSrc() = ImageStreamSrc(FileImageStream(this))

internal data class FileImageStream(val file: File) : ImageStream {
override fun openStream(): InputStream = file.inputStream()
override fun openStream(): InputStream = try {
file.inputStream()
} catch (e: Exception) {
throw IllegalArgumentException("Failed to open file stream", e)
}
}

internal data class UriImageStream(val uri: Uri, val context: Context) : ImageStream {
Expand Down
93 changes: 52 additions & 41 deletions easycrop/src/main/java/com/mr0xf00/easycrop/ui/Controls.kt
Original file line number Diff line number Diff line change
Expand Up @@ -45,21 +45,27 @@ internal fun CropperControls(
CompositionLocalProvider(LocalVerticalControls provides isVertical) {
ButtonsBar(modifier = modifier) {
IconButton(onClick = { state.rotLeft() }) {
Icon(painterResource(id = R.drawable.rot_left), null)
Icon(painter = painterResource(id = R.drawable.rot_left), contentDescription = null)
}
IconButton(onClick = { state.rotRight() }) {
Icon(painterResource(id = R.drawable.rot_right), null)
Icon(
painter = painterResource(id = R.drawable.rot_right),
contentDescription = null
)
}
IconButton(onClick = { state.flipHorizontal() }) {
Icon(painterResource(id = R.drawable.flip_hor), null)
Icon(painter = painterResource(id = R.drawable.flip_hor), contentDescription = null)
}
IconButton(onClick = { state.flipVertical() }) {
Icon(painterResource(id = R.drawable.flip_ver), null)
Icon(painter = painterResource(id = R.drawable.flip_ver), contentDescription = null)
}
Box {
var menu by remember { mutableStateOf(false) }
IconButton(onClick = { menu = !menu }) {
Icon(painterResource(id = R.drawable.resize), null)
Icon(
painter = painterResource(id = R.drawable.resize),
contentDescription = null
)
}
if (menu) AspectSelectionMenu(
onDismiss = { menu = false },
Expand All @@ -70,17 +76,19 @@ internal fun CropperControls(
)
}
LocalCropperStyle.current.shapes?.let { shapes ->
Box {
var menu by remember { mutableStateOf(false) }
IconButton(onClick = { menu = !menu }) {
Icon(Icons.Default.Star, null)
if (shapes.isNotEmpty()) {
Box {
var menu by remember { mutableStateOf(false) }
IconButton(onClick = { menu = !menu }) {
Icon(imageVector = Icons.Default.Star, contentDescription = null)
}
if (menu) ShapeSelectionMenu(
onDismiss = { menu = false },
selected = state.shape,
onSelect = { state.shape = it },
shapes = shapes
)
}
if (menu) ShapeSelectionMenu(
onDismiss = { menu = false },
selected = state.shape,
onSelect = { state.shape = it },
shapes = shapes
)
}
}
}
Expand Down Expand Up @@ -114,7 +122,6 @@ private fun ButtonsBar(
}



@Composable
private fun ShapeSelectionMenu(
onDismiss: () -> Unit,
Expand All @@ -124,7 +131,8 @@ private fun ShapeSelectionMenu(
) {
OptionsPopup(onDismiss = onDismiss, optionCount = shapes.size) { i ->
val shape = shapes[i]
ShapeItem(shape = shape, selected = selected == shape,
ShapeItem(
shape = shape, selected = selected == shape,
onSelect = { onSelect(shape) })
}
}
Expand All @@ -144,15 +152,16 @@ private fun ShapeItem(
onClick = onSelect
) {
val shapeState by rememberUpdatedState(newValue = shape)
Box(modifier = Modifier
.size(20.dp)
.drawWithCache {
val path = shapeState.asPath(size.toRect())
val strokeWidth = 2.dp.toPx()
onDrawWithContent {
drawPath(path = path, color = color, style = Stroke(strokeWidth))
}
})
Box(
modifier = Modifier
.size(20.dp)
.drawWithCache {
val path = shapeState.asPath(size.toRect())
val strokeWidth = 2.dp.toPx()
onDrawWithContent {
drawPath(path = path, color = color, style = Stroke(strokeWidth))
}
})
}
}

Expand All @@ -166,22 +175,24 @@ private fun AspectSelectionMenu(
onLock: (Boolean) -> Unit
) {
val aspects = LocalCropperStyle.current.aspects
OptionsPopup(onDismiss = onDismiss, optionCount = 1 + aspects.size) { i ->
val unselectedTint = LocalContentColor.current
val selectedTint = MaterialTheme.colorScheme.secondary
if (i == 0) IconButton(onClick = { onLock(!lock) }) {
Icon(
Icons.Default.Lock, null,
tint = if (lock) selectedTint else unselectedTint
)
} else {
val aspect = aspects[i - 1]
val isSelected = region.size.isAspect(aspect)
IconButton(onClick = { onRegion(region.setAspect(aspect)) }) {
Text(
"${aspect.x}:${aspect.y}",
color = if (isSelected) selectedTint else unselectedTint
if (aspects.isNotEmpty()) {
OptionsPopup(onDismiss = onDismiss, optionCount = 1 + aspects.size) { i ->
val unselectedTint = LocalContentColor.current
val selectedTint = MaterialTheme.colorScheme.secondary
if (i == 0) IconButton(onClick = { onLock(!lock) }) {
Icon(
imageVector = Icons.Default.Lock, contentDescription = null,
tint = if (lock) selectedTint else unselectedTint
)
} else {
val aspect = aspects[i - 1]
val isSelected = region.size.isAspect(aspect)
IconButton(onClick = { onRegion(region.setAspect(aspect)) }) {
Text(
"${aspect.x}:${aspect.y}",
color = if (isSelected) selectedTint else unselectedTint
)
}
}
}
}
Expand Down
4 changes: 2 additions & 2 deletions easycrop/src/main/java/com/mr0xf00/easycrop/utils/Math.kt
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ internal fun lerp(a: Float, b: Float, p: Float): Float = a + p * (b - a)
internal fun lerp(a: Int, b: Int, p: Float) = lerp(a.toFloat(), b.toFloat(), p).roundToInt()

internal fun lerpAngle(a: Int, b: Int, p: Float): Int {
val angleDist = (2 * ((b - a) % 360) % 360 - (b - a) % 360)
return (a + angleDist * p).roundToInt()
val diff = (((b - a + 180) % 360) - 180)
return (a + diff * p).roundToInt()
}

internal fun Int.next90() = (this + 90).angleRange()
Expand Down
4 changes: 4 additions & 0 deletions easycrop/src/main/java/com/mr0xf00/easycrop/utils/Matrix.kt
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ internal fun Matrix.setScaleTranslate(sx: Float, sy: Float, tx: Float, ty: Float
}

internal fun Matrix.setRectToRect(src: Rect, dst: Rect) {
if (src.isEmpty || dst.isEmpty) {
setScaleTranslate(1f, 1f, 0f, 0f)
return
}
val sx: Float = dst.width / src.width
val tx = dst.left - src.left * sx
val sy: Float = dst.height / src.height
Expand Down
3 changes: 1 addition & 2 deletions settings.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -33,5 +33,4 @@ check(JavaVersion.current().isCompatibleWith(JavaVersion.VERSION_17)) {
Java Home: [${System.getProperty("java.home")}]
https://developer.android.com/build/jdks#jdk-config-in-studio
""".trimIndent()
}
include(":easycrop")
}

0 comments on commit e7ebc63

Please sign in to comment.