Skip to content

Commit

Permalink
rotating and flipping image added
Browse files Browse the repository at this point in the history
  • Loading branch information
T8RIN committed Apr 7, 2022
1 parent 63c0d10 commit 4d09423
Show file tree
Hide file tree
Showing 6 changed files with 149 additions and 48 deletions.
7 changes: 5 additions & 2 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ android {
applicationId "ru.tech.imageresizershrinker"
minSdk 21
targetSdk 32
versionCode 2
versionName "1.0.1"
versionCode 3
versionName "1.0.2"
}

buildTypes {
Expand Down Expand Up @@ -47,5 +47,8 @@ dependencies {
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.4.1'
implementation 'androidx.activity:activity-compose:1.4.0'
implementation "androidx.compose.ui:ui:1.2.0-alpha07"
implementation "androidx.compose.foundation:foundation:1.2.0-alpha07"
implementation 'androidx.compose.material3:material3:1.0.0-alpha09'
implementation "androidx.compose.material:material-icons-extended:1.2.0-alpha07"
implementation "com.google.accompanist:accompanist-systemuicontroller:0.24.5-alpha"
}
Binary file modified app/release/app-release.apk
Binary file not shown.
4 changes: 2 additions & 2 deletions app/release/output-metadata.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@
"type": "SINGLE",
"filters": [],
"attributes": [],
"versionCode": 2,
"versionName": "1.0.1",
"versionCode": 3,
"versionName": "1.0.2",
"outputFile": "app-release.apk"
}
],
Expand Down
156 changes: 131 additions & 25 deletions app/src/main/java/ru/tech/imageresizershrinker/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.graphics.ImageDecoder.createSource
import android.graphics.ImageDecoder.decodeBitmap
import android.graphics.Matrix
import android.os.Build
import android.os.Bundle
import android.os.Environment
Expand All @@ -24,25 +25,28 @@ import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Flip
import androidx.compose.material.icons.filled.RotateLeft
import androidx.compose.material.icons.filled.RotateRight
import androidx.compose.material.icons.twotone.Image
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.asImageBitmap
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.vectorResource
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.unit.dp
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat.checkSelfPermission
import androidx.core.text.isDigitsOnly
import androidx.core.view.WindowCompat
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import ru.tech.imageresizershrinker.ui.theme.ImageResizerShrinkerTheme
import java.io.*
import java.text.SimpleDateFormat
import java.util.*
Expand All @@ -55,6 +59,7 @@ class MainActivity : ComponentActivity() {
private val viewModel by viewModels<MainViewModel>()

override fun onCreate(savedInstanceState: Bundle?) {
WindowCompat.setDecorFitsSystemWindows(window, false)
super.onCreate(savedInstanceState)
setContent {

Expand All @@ -63,6 +68,8 @@ class MainActivity : ComponentActivity() {
var quality by rememberSaveable { mutableStateOf(0f) }
var mime by rememberSaveable { mutableStateOf(0) }
var resize by rememberSaveable { mutableStateOf(0) }
var rotation by rememberSaveable { mutableStateOf(0f) }
var isFlipped by rememberSaveable { mutableStateOf(false) }


ImageResizerShrinkerTheme {
Expand Down Expand Up @@ -102,7 +109,13 @@ class MainActivity : ComponentActivity() {
) {
LazyColumn(
reverseLayout = true,
contentPadding = PaddingValues(horizontal = 40.dp)
contentPadding = PaddingValues(
top = WindowInsets.statusBars.asPaddingValues()
.calculateBottomPadding(),
bottom = WindowInsets.navigationBars.asPaddingValues()
.calculateBottomPadding(),
start = 40.dp, end = 40.dp
)
) {
item {
Column(
Expand All @@ -116,7 +129,7 @@ class MainActivity : ComponentActivity() {
bitmap.value?.asImageBitmap()?.let { Image(it, null) }
} else {
Icon(
ImageVector.vectorResource(id = R.drawable.ic_twotone_image_24),
Icons.TwoTone.Image,
null,
modifier = Modifier.size(100.dp)
)
Expand All @@ -125,6 +138,61 @@ class MainActivity : ComponentActivity() {
Spacer(Modifier.size(20.dp))
}
Spacer(Modifier.size(40.dp))
if (viewModel.globalBitmap.value != null) {
Row {
SmallFloatingActionButton(onClick = {
rotation -= 90f
checkBitmapAndUpdate(
scope,
bitmap,
quality,
width,
height,
mime,
resize,
rotation,
isFlipped
)
}) {
Icon(Icons.Default.RotateLeft, null)
}

SmallFloatingActionButton(onClick = {
isFlipped = !isFlipped
checkBitmapAndUpdate(
scope,
bitmap,
quality,
width,
height,
mime,
resize,
rotation,
isFlipped
)
}) {
Icon(Icons.Default.Flip, null)
}

SmallFloatingActionButton(onClick = {
rotation += 90f
checkBitmapAndUpdate(
scope,
bitmap,
quality,
width,
height,
mime,
resize,
rotation,
isFlipped
)
}) {
Icon(Icons.Default.RotateRight, null)
}
}
}
Spacer(Modifier.size(10.dp))
Row {
TextField(
value = width,
Expand All @@ -137,7 +205,9 @@ class MainActivity : ComponentActivity() {
width,
height,
mime,
resize
resize,
rotation,
isFlipped
)
},
keyboardOptions = KeyboardOptions(
Expand All @@ -158,7 +228,9 @@ class MainActivity : ComponentActivity() {
width,
height,
mime,
resize
resize,
rotation,
isFlipped
)
},
keyboardOptions = KeyboardOptions(
Expand All @@ -182,7 +254,9 @@ class MainActivity : ComponentActivity() {
width,
height,
mime,
resize
resize,
rotation,
isFlipped
)
},
valueRange = 0f..100f,
Expand All @@ -204,7 +278,9 @@ class MainActivity : ComponentActivity() {
width,
height,
mime,
resize
resize,
rotation,
isFlipped
)
}
)
Expand All @@ -224,7 +300,9 @@ class MainActivity : ComponentActivity() {
width,
height,
mime,
resize
resize,
rotation,
isFlipped
)
}
)
Expand All @@ -240,7 +318,9 @@ class MainActivity : ComponentActivity() {
width.toIntOrNull(),
height.toIntOrNull(),
mime,
resize
resize,
rotation,
isFlipped
)?.let { bmp ->
viewModel.globalBitmap.value = bmp
Toast.makeText(
Expand Down Expand Up @@ -275,7 +355,9 @@ class MainActivity : ComponentActivity() {
widthValue: Int?,
heightValue: Int?,
mime: Int,
resize: Int
resize: Int,
rotation: Float,
isFlipped: Boolean
): Bitmap? {
return if (isExternalStorageWritable()) {
val ext = if (mime == 1) "jpg" else "png"
Expand All @@ -296,7 +378,8 @@ class MainActivity : ComponentActivity() {
)
} else {
bitmap.resizeBitmap(max(tWidth, tHeight))
}
}.rotate(rotation).flip(isFlipped)

val fos: OutputStream? = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
val resolver: ContentResolver = contentResolver
val contentValues = ContentValues().apply {
Expand Down Expand Up @@ -359,11 +442,14 @@ class MainActivity : ComponentActivity() {
width: String,
height: String,
mime: Int,
resize: Int
resize: Int,
rotation: Float,
isFlipped: Boolean
) {
scope.launch {
viewModel.globalBitmap.value?.let { bmp ->
bitmap.value = updatePreview(bmp, quality, width, height, mime, resize)
bitmap.value =
updatePreview(bmp, quality, width, height, mime, resize, rotation, isFlipped)
}
}
}
Expand All @@ -374,15 +460,19 @@ class MainActivity : ComponentActivity() {
width: String,
height: String,
mime: Int,
resize: Int
resize: Int,
rotation: Float,
isFlipped: Boolean
): Bitmap = withContext(Dispatchers.IO) {
globalBitmap.let {
it.previewBitmap(
quality,
width.toIntOrNull(),
height.toIntOrNull(),
mime,
resize
resize,
rotation,
isFlipped
).let { bmp ->
return@withContext bmp
}
Expand All @@ -394,7 +484,9 @@ class MainActivity : ComponentActivity() {
widthValue: Int?,
heightValue: Int?,
mime: Int,
resize: Int
resize: Int,
rotation: Float,
isFlipped: Boolean
): Bitmap {
val out = ByteArrayOutputStream()
val explicit = resize == 0
Expand All @@ -410,23 +502,37 @@ class MainActivity : ComponentActivity() {
)
} else {
this.resizeBitmap(max(tWidth, tHeight))
}.compress(
if (mime == 1) Bitmap.CompressFormat.JPEG else Bitmap.CompressFormat.PNG,
quality.toInt(), out
)
}.rotate(rotation)
.flip(isFlipped)
.compress(
if (mime == 1) Bitmap.CompressFormat.JPEG else Bitmap.CompressFormat.PNG,
quality.toInt(), out
)
return BitmapFactory.decodeStream(ByteArrayInputStream(out.toByteArray()))
}

private fun String.restrict(): String {
if (isEmpty()) return this

return if ((this.toIntOrNull() ?: 0) > 4200) "4200"
else if (this.isDigitsOnly() && (this.toIntOrNull() ?: 0) == 0) ""
else filter {
return if ((this.trim().toIntOrNull() ?: 0) > 4200) "4200"
else if (this.trim().isDigitsOnly() && (this.toIntOrNull() ?: 0) == 0) ""
else this.trim().filter {
!listOf('-', '.', ',', ' ').contains(it)
}
}

private fun Bitmap.rotate(degrees: Float): Bitmap {
val matrix = Matrix().apply { postRotate(degrees) }
return Bitmap.createBitmap(this, 0, 0, width, height, matrix, true)
}

private fun Bitmap.flip(value: Boolean): Bitmap {
return if (value) {
val matrix = Matrix().apply { postScale(-1f, 1f, width / 2f, width / 2f) }
Bitmap.createBitmap(this, 0, 0, width, height, matrix, true)
} else this
}

private fun Bitmap.resizeBitmap(maxLength: Int): Bitmap {
return try {
if (height >= width) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
package ru.tech.imageresizershrinker.ui.theme
package ru.tech.imageresizershrinker

import android.app.Activity
import android.os.Build
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.material3.*
import androidx.compose.runtime.Composable
import androidx.compose.runtime.SideEffect
import androidx.compose.ui.graphics.toArgb
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalView
import androidx.core.view.ViewCompat
import com.google.accompanist.systemuicontroller.rememberSystemUiController

private val DarkColorScheme = darkColorScheme()
private val LightColorScheme = lightColorScheme()
Expand All @@ -29,12 +27,14 @@ fun ImageResizerShrinkerTheme(
else -> LightColorScheme
}

val view = LocalView.current
if (!view.isInEditMode) {
SideEffect {
(view.context as Activity).window.statusBarColor = colorScheme.primary.toArgb()
ViewCompat.getWindowInsetsController(view)?.isAppearanceLightStatusBars = darkTheme
}
val systemUiController = rememberSystemUiController()
val useDarkIcons = !isSystemInDarkTheme()

SideEffect {
systemUiController.setSystemBarsColor(
color = Color.Transparent,
darkIcons = useDarkIcons
)
}

MaterialTheme(
Expand Down
Loading

0 comments on commit 4d09423

Please sign in to comment.