diff --git a/app/.gitignore b/app/.gitignore deleted file mode 100644 index 42afabf..0000000 --- a/app/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/build \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle deleted file mode 100644 index f9aca38..0000000 --- a/app/build.gradle +++ /dev/null @@ -1,104 +0,0 @@ -plugins { - id 'com.android.application' - id 'org.jetbrains.kotlin.android' -} - -android { - namespace 'com.smarttoolfactory.composecropper' - compileSdk 33 - - defaultConfig { - applicationId "com.smarttoolfactory.composecropper" - minSdk 21 - targetSdk 33 - versionCode 1 - versionName "1.0" - - testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" - vectorDrawables { - useSupportLibrary true - } - } - - buildTypes { - release { - minifyEnabled false - proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' - } - } - compileOptions { - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 - } - kotlinOptions { - jvmTarget = '1.8' - } - buildFeatures { - compose true - } - composeOptions { - kotlinCompilerExtensionVersion = "1.2.0-rc02" - } - packagingOptions { - resources { - excludes += '/META-INF/{AL2.0,LGPL2.1}' - } - } -} - -dependencies { - - implementation project(':cropper') - - implementation 'androidx.core:core-ktx:1.9.0' - implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.5.1' - - // Colorful Customizable Sliders - implementation 'com.github.SmartToolFactory:Compose-Colorful-Sliders:1.1.0' - // Color picker - implementation 'com.github.SmartToolFactory:Compose-Color-Picker-Bundle:1.0.1' - // Gestures - implementation 'com.github.SmartToolFactory:Compose-Extended-Gestures:2.1.0' - // Animated List - implementation 'com.github.SmartToolFactory:Compose-AnimatedList:0.5.1' - - implementation "androidx.compose.ui:ui:$compose_version" - // Tooling support (Previews, etc.) - implementation "androidx.compose.ui:ui-tooling:$compose_version" - // Foundation (Border, Background, Box, Image, Scroll, shapes, animations, etc.) - implementation "androidx.compose.foundation:foundation:$compose_version" - // Material Design - implementation "androidx.compose.material:material:$compose_version" - // Material design icons - implementation "androidx.compose.material:material-icons-core:$compose_version" - implementation "androidx.compose.material:material-icons-extended:$compose_version" - // Integration with activities - implementation 'androidx.activity:activity-compose:1.6.1' - // Integration with ViewModels - implementation 'androidx.lifecycle:lifecycle-viewmodel-compose:2.5.1' - - // Material Design 3 for Compose - implementation "androidx.compose.material3:material3:1.0.0" - - def nav_compose_version = "2.5.3" - implementation "androidx.navigation:navigation-compose:$nav_compose_version" - - def accompanist_version = "0.25.0" - // Accompanist - implementation "com.google.accompanist:accompanist-systemuicontroller:$accompanist_version" - implementation "com.google.accompanist:accompanist-pager:$accompanist_version" - implementation "com.google.accompanist:accompanist-drawablepainter:$accompanist_version" - - // Coil - implementation("io.coil-kt:coil-compose:2.1.0") - - // Photo Picker - implementation("com.google.modernstorage:modernstorage-photopicker:1.0.0-alpha06") - - testImplementation 'junit:junit:4.13.2' - androidTestImplementation 'androidx.test.ext:junit:1.1.3' - androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' - androidTestImplementation "androidx.compose.ui:ui-test-junit4:$compose_version" - debugImplementation "androidx.compose.ui:ui-tooling:$compose_version" - debugImplementation "androidx.compose.ui:ui-test-manifest:$compose_version" -} \ No newline at end of file diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro deleted file mode 100644 index 481bb43..0000000 --- a/app/proguard-rules.pro +++ /dev/null @@ -1,21 +0,0 @@ -# Add project specific ProGuard rules here. -# You can control the set of applied configuration files using the -# proguardFiles setting in build.gradle. -# -# For more details, see -# http://developer.android.com/guide/developing/tools/proguard.html - -# If your project uses WebView with JS, uncomment the following -# and specify the fully qualified class name to the JavaScript interface -# class: -#-keepclassmembers class fqcn.of.javascript.interface.for.webview { -# public *; -#} - -# Uncomment this to preserve the line number information for -# debugging stack traces. -#-keepattributes SourceFile,LineNumberTable - -# If you keep the line number information, uncomment this to -# hide the original source file name. -#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/app/src/androidTest/java/com/smarttoolfactory/composecropper/ExampleInstrumentedTest.kt b/app/src/androidTest/java/com/smarttoolfactory/composecropper/ExampleInstrumentedTest.kt deleted file mode 100644 index 6a9f2db..0000000 --- a/app/src/androidTest/java/com/smarttoolfactory/composecropper/ExampleInstrumentedTest.kt +++ /dev/null @@ -1,24 +0,0 @@ -package com.smarttoolfactory.composecropper - -import androidx.test.platform.app.InstrumentationRegistry -import androidx.test.ext.junit.runners.AndroidJUnit4 - -import org.junit.Test -import org.junit.runner.RunWith - -import org.junit.Assert.* - -/** - * Instrumented test, which will execute on an Android device. - * - * See [testing documentation](http://d.android.com/tools/testing). - */ -@RunWith(AndroidJUnit4::class) -class ExampleInstrumentedTest { - @Test - fun useAppContext() { - // Context of the app under test. - val appContext = InstrumentationRegistry.getInstrumentation().targetContext - assertEquals("com.smarttoolfactory.composecropper", appContext.packageName) - } -} \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml deleted file mode 100644 index b842507..0000000 --- a/app/src/main/AndroidManifest.xml +++ /dev/null @@ -1,32 +0,0 @@ - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/java/com/smarttoolfactory/composecropper/ImageSelectionButton.kt b/app/src/main/java/com/smarttoolfactory/composecropper/ImageSelectionButton.kt deleted file mode 100644 index fc9e8c7..0000000 --- a/app/src/main/java/com/smarttoolfactory/composecropper/ImageSelectionButton.kt +++ /dev/null @@ -1,59 +0,0 @@ -package com.smarttoolfactory.composecropper - -import android.annotation.SuppressLint -import android.graphics.Bitmap -import android.graphics.ImageDecoder -import android.os.Build -import android.provider.MediaStore -import androidx.activity.compose.rememberLauncherForActivityResult -import androidx.compose.material.Icon -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.Add -import androidx.compose.material3.FloatingActionButton -import androidx.compose.material3.FloatingActionButtonDefaults -import androidx.compose.material3.FloatingActionButtonElevation -import androidx.compose.runtime.Composable -import androidx.compose.ui.graphics.ImageBitmap -import androidx.compose.ui.graphics.asImageBitmap -import androidx.compose.ui.platform.LocalContext -import com.google.modernstorage.photopicker.PhotoPicker - -@SuppressLint("UnsafeOptInUsageError") -@Composable -fun ImageSelectionButton( - elevation: FloatingActionButtonElevation = FloatingActionButtonDefaults.elevation(), - onImageSelected: (ImageBitmap) -> Unit -) { - val context = LocalContext.current - - val photoPicker = - rememberLauncherForActivityResult(PhotoPicker()) { uris -> - val uri = uris.firstOrNull() ?: return@rememberLauncherForActivityResult - - val bitmap: Bitmap = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { - ImageDecoder.decodeBitmap( - ImageDecoder.createSource(context.contentResolver, uri) - ) { decoder, info, source -> - decoder.allocator = ImageDecoder.ALLOCATOR_SOFTWARE - decoder.isMutableRequired = true - } - } else { - MediaStore.Images.Media.getBitmap(context.contentResolver, uri) - } - - onImageSelected(bitmap.asImageBitmap()) - - } - - FloatingActionButton( - elevation = elevation, - onClick = { - photoPicker.launch(PhotoPicker.Args(PhotoPicker.Type.IMAGES_ONLY, 1)) - }, - ) { - Icon( - imageVector = Icons.Default.Add, - contentDescription = null - ) - } -} \ No newline at end of file diff --git a/app/src/main/java/com/smarttoolfactory/composecropper/MainActivity.kt b/app/src/main/java/com/smarttoolfactory/composecropper/MainActivity.kt deleted file mode 100644 index a6cb6ce..0000000 --- a/app/src/main/java/com/smarttoolfactory/composecropper/MainActivity.kt +++ /dev/null @@ -1,31 +0,0 @@ -package com.smarttoolfactory.composecropper - -import android.os.Bundle -import androidx.activity.ComponentActivity -import androidx.activity.compose.setContent -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.Surface -import androidx.compose.ui.Modifier -import com.smarttoolfactory.composecropper.demo.ImageCropDemo -import com.smarttoolfactory.composecropper.ui.theme.ComposeCropperTheme - -class MainActivity : ComponentActivity() { - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - setContent { - ComposeCropperTheme { - // A surface container using the 'background' color from the theme - Surface( - modifier = Modifier.fillMaxSize(), - color = MaterialTheme.colorScheme.background - ) { - Column { - ImageCropDemo() - } - } - } - } - } -} diff --git a/app/src/main/java/com/smarttoolfactory/composecropper/demo/CanvasDemo.kt b/app/src/main/java/com/smarttoolfactory/composecropper/demo/CanvasDemo.kt deleted file mode 100644 index 772ad0e..0000000 --- a/app/src/main/java/com/smarttoolfactory/composecropper/demo/CanvasDemo.kt +++ /dev/null @@ -1,223 +0,0 @@ -package com.smarttoolfactory.composecropper.demo - -import android.graphics.Bitmap -import androidx.compose.foundation.Image -import androidx.compose.foundation.background -import androidx.compose.foundation.border -import androidx.compose.foundation.layout.* -import androidx.compose.runtime.Composable -import androidx.compose.runtime.remember -import androidx.compose.ui.Modifier -import androidx.compose.ui.geometry.Offset -import androidx.compose.ui.graphics.* -import androidx.compose.ui.graphics.drawscope.Stroke -import androidx.compose.ui.graphics.drawscope.translate -import androidx.compose.ui.layout.ContentScale -import androidx.compose.ui.res.imageResource -import androidx.compose.ui.unit.dp -import com.smarttoolfactory.composecropper.R - -@Composable -fun CanvasDemo() { - - Column(modifier = Modifier.fillMaxSize()) { - - NativeCanvasSample1( - modifier = Modifier - .fillMaxWidth() - .aspectRatio(4 / 3f) - ) - - NativeCanvasMaskSample() - - VectorSample() - } - -} - - -@Composable -fun NativeCanvasMaskSample() { - - - val cropMaskBitmap = ImageBitmap.imageResource(id = R.drawable.squircle) - - val imagePaint = remember { - Paint().apply { - blendMode = BlendMode.SrcIn - } - } - - val paint = remember { - Paint() - } - - val imageBitmap = ImageBitmap - .imageResource(id = R.drawable.cinnamon) - .asAndroidBitmap() - .copy(Bitmap.Config.ARGB_8888, true)!! - .asImageBitmap() - - - Canvas(image = imageBitmap).apply { - - saveLayer(nativeCanvas.clipBounds.toComposeRect(), imagePaint) - // Destination -// drawCircle(center = Offset(400f, 400f), radius = 300f, paint) -// drawImage(cropMaskBitmap, topLeftOffset = Offset.Zero, paint) - - val matrix = android.graphics.Matrix() - matrix.postScale(30f, 30f) - favoritePath.asAndroidPath().transform(matrix) - - val left = favoritePath.getBounds().left - val top = favoritePath.getBounds().top - - - drawPath(favoritePath, paint) - - // Source - drawImage(imageBitmap, topLeftOffset = Offset.Zero, imagePaint) - - restore() - } - - Image( - modifier = Modifier - .fillMaxWidth() - .aspectRatio(4 / 3f) - .border(1.dp, Color.Green), - bitmap = imageBitmap, - contentDescription = null - ) -} - -@Composable -fun NativeCanvasSample1(modifier: Modifier) { - - val imageBitmap = ImageBitmap - .imageResource(id = R.drawable.cinnamon) - .asAndroidBitmap() - .copy(Bitmap.Config.ARGB_8888, true)!! - .asImageBitmap() - - BoxWithConstraints(modifier) { - - - val imageWidth = constraints.maxWidth - val imageHeight = constraints.maxHeight - - val bitmapWidth = imageBitmap.width - val bitmapHeight = imageBitmap.height - - - val canvas = Canvas(imageBitmap) - - val imagePaint = remember { - Paint().apply { - blendMode = BlendMode.SrcIn - } - } - - val paint = remember { - Paint().apply { - color = Color(0xff29B6F6) - } - } - - canvas.apply { - val nativeCanvas = this.nativeCanvas - val canvasWidth = nativeCanvas.width.toFloat() - val canvasHeight = nativeCanvas.height.toFloat() - - println( - "🔥 Canvas Width: $canvasWidth, canvasHeight: $canvasHeight, " + - "imageWidth: $imageWidth, imageHeight: $imageHeight\n" + - "bitmapWidth: $bitmapWidth, bitmapHeight: $bitmapHeight\n" + - "rect: ${nativeCanvas.clipBounds.toComposeRect()}" - ) - saveLayer(nativeCanvas.clipBounds.toComposeRect(), imagePaint) - - drawCircle( - center = Offset(canvasWidth / 2, canvasHeight / 2), - radius = canvasHeight / 2, - paint = paint - ) - drawImage(image = imageBitmap, topLeftOffset = Offset.Zero, imagePaint) - restore() - - - } - - Image( - modifier = Modifier - .background(Color.LightGray) - .border(2.dp, Color.Red), - bitmap = imageBitmap, - contentDescription = null, - contentScale = ContentScale.FillBounds - ) - - } -} - -@Composable -private fun VectorSample() { - - androidx.compose.foundation.Canvas( - modifier = Modifier - .fillMaxWidth() - .aspectRatio(4 / 3f) - .border(3.dp,Color.Cyan) - ) { - - val matrix = android.graphics.Matrix() - matrix.postScale(30f, 30f) - starPath.asAndroidPath().transform(matrix) - - val left = starPath.getBounds().left - val top = starPath.getBounds().top - - translate(left = -left, top = -top) { - drawPath(starPath, Color.Red) - } - - drawRect( - color = Color.Green, - topLeft = starPath.getBounds().topLeft, - size = starPath.getBounds().size, - style = Stroke(2.dp.toPx()) - ) - - } -} - - - - -val favoritePath = Path().apply { - moveTo(12.0f, 21.35f) - relativeLineTo(-1.45f, -1.32f) - cubicTo(5.4f, 15.36f, 2.0f, 12.28f, 2.0f, 8.5f) - cubicTo(2.0f, 5.42f, 4.42f, 3.0f, 7.5f, 3.0f) - relativeCubicTo(1.74f, 0.0f, 3.41f, 0.81f, 4.5f, 2.09f) - cubicTo(13.09f, 3.81f, 14.76f, 3.0f, 16.5f, 3.0f) - cubicTo(19.58f, 3.0f, 22.0f, 5.42f, 22.0f, 8.5f) - relativeCubicTo(0.0f, 3.78f, -3.4f, 6.86f, -8.55f, 11.54f) - lineTo(12.0f, 21.35f) - close() -} - -val starPath = Path().apply { - moveTo(12.0f, 17.27f) - lineTo(18.18f, 21.0f) - relativeLineTo(-1.64f, -7.03f) - lineTo(22.0f, 9.24f) - relativeLineTo(-7.19f, -0.61f) - lineTo(12.0f, 2.0f) - lineTo(9.19f, 8.63f) - lineTo(2.0f, 9.24f) - relativeLineTo(5.46f, 4.73f) - lineTo(5.82f, 21.0f) - close() -} \ No newline at end of file diff --git a/app/src/main/java/com/smarttoolfactory/composecropper/demo/ImageCropDemo.kt b/app/src/main/java/com/smarttoolfactory/composecropper/demo/ImageCropDemo.kt deleted file mode 100644 index f6a83ef..0000000 --- a/app/src/main/java/com/smarttoolfactory/composecropper/demo/ImageCropDemo.kt +++ /dev/null @@ -1,293 +0,0 @@ -@file:OptIn(ExperimentalMaterialApi::class, ExperimentalMaterialApi::class) - -package com.smarttoolfactory.composecropper.demo - -import androidx.compose.foundation.Image -import androidx.compose.foundation.background -import androidx.compose.foundation.isSystemInDarkTheme -import androidx.compose.foundation.layout.* -import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material.* -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.Brush -import androidx.compose.material.icons.filled.Crop -import androidx.compose.material.icons.filled.Settings -import androidx.compose.material3.BottomAppBar -import androidx.compose.material3.FloatingActionButtonDefaults -import androidx.compose.material3.Icon -import androidx.compose.material3.IconButton -import androidx.compose.material3.Text -import androidx.compose.runtime.* -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.graphics.ImageBitmap -import androidx.compose.ui.layout.ContentScale -import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.platform.LocalDensity -import androidx.compose.ui.res.imageResource -import androidx.compose.ui.unit.dp -import com.smarttoolfactory.colorpicker.widget.drawChecker -import com.smarttoolfactory.composecropper.ImageSelectionButton -import com.smarttoolfactory.composecropper.R -import com.smarttoolfactory.composecropper.preferences.CropStyleSelectionMenu -import com.smarttoolfactory.composecropper.preferences.PropertySelectionSheet -import com.smarttoolfactory.composecropper.ui.theme.ComposeCropperTheme -import com.smarttoolfactory.cropper.ImageCropper -import com.smarttoolfactory.cropper.model.CornerRadiusProperties -import com.smarttoolfactory.cropper.model.OutlineType -import com.smarttoolfactory.cropper.model.RectCropShape -import com.smarttoolfactory.cropper.model.RoundedCornerCropShape -import com.smarttoolfactory.cropper.settings.* -import kotlinx.coroutines.launch - -internal enum class SelectionPage { - Properties, Style -} - -@OptIn(ExperimentalMaterialApi::class) -@Composable -fun ImageCropDemo() { - - val bottomSheetScaffoldState: BottomSheetScaffoldState = rememberBottomSheetScaffoldState( - bottomSheetState = BottomSheetState(BottomSheetValue.Collapsed) - ) - - val defaultImage1 = ImageBitmap.imageResource(id = R.drawable.squircle) - val defaultImage2 = ImageBitmap.imageResource(id = R.drawable.cloud) - val defaultImage3 = ImageBitmap.imageResource(id = R.drawable.sun) - - val cropFrameFactory = remember { - CropFrameFactory( - listOf( - defaultImage1, - defaultImage2, - defaultImage3 - ) - ) - } - - val handleSize = LocalDensity.current.run { 14.dp.toPx() } - val cornerSize = LocalDensity.current.run { 8.dp.toPx() } - val corner = CornerRadiusProperties(radius = cornerSize) - - var cropProperties by remember { - mutableStateOf( - CropDefaults.properties( - cropOutlineProperty = CropOutlineProperty( - outlineType = OutlineType.RoundedRect, - cropOutline = RoundedCornerCropShape(0, "RoundRect", cornerRadius = corner) - ), - handleSize = handleSize - ) - ) - } - var cropStyle by remember { mutableStateOf(CropDefaults.style().copy(handleStrokeWidth = 4.dp)) } - val coroutineScope = rememberCoroutineScope() - - var selectionPage by remember { mutableStateOf(SelectionPage.Properties) } - - - val theme by remember { - derivedStateOf { - cropStyle.cropTheme - } - } - - ComposeCropperTheme( - darkTheme = when(theme){ - CropTheme.Dark ->true - CropTheme.Light->false - else -> isSystemInDarkTheme() - } - ) { - BottomSheetScaffold( - scaffoldState = bottomSheetScaffoldState, - sheetElevation = 16.dp, - sheetShape = RoundedCornerShape( - bottomStart = 0.dp, - bottomEnd = 0.dp, - topStart = 28.dp, - topEnd = 28.dp - ), - - sheetGesturesEnabled = true, - sheetContent = { - - if (selectionPage == SelectionPage.Properties) { - PropertySelectionSheet( - cropFrameFactory = cropFrameFactory, - cropProperties = cropProperties, - onCropPropertiesChange = { - cropProperties = it - } - ) - } else { - CropStyleSelectionMenu( - cropType = cropProperties.cropType, - cropStyle = cropStyle, - onCropStyleChange = { - cropStyle = it - } - ) - } - }, - - // This is the height in collapsed state - sheetPeekHeight = 0.dp - ) { - MainContent( - cropProperties, - cropStyle, - ) { - selectionPage = it - - coroutineScope.launch { - if (bottomSheetScaffoldState.bottomSheetState.isExpanded) { - bottomSheetScaffoldState.bottomSheetState.collapse() - } else { - bottomSheetScaffoldState.bottomSheetState.expand() - } - } - } - } - } -} - -@Composable -private fun MainContent( - cropProperties: CropProperties, - cropStyle: CropStyle, - onSelectionPageMenuClicked: (SelectionPage) -> Unit -) { - - val imageBitmapLarge = ImageBitmap.imageResource( - LocalContext.current.resources, - R.drawable.landscape5 - ) - - var imageBitmap by remember { mutableStateOf(imageBitmapLarge) } - var croppedImage by remember { mutableStateOf(null) } - - - var crop by remember { mutableStateOf(false) } - var showDialog by remember { mutableStateOf(false) } - var isCropping by remember { mutableStateOf(false) } - - Box( - modifier = Modifier - .fillMaxSize() - .background(Color.DarkGray), - contentAlignment = Alignment.Center - ) { - Column(modifier = Modifier.fillMaxSize()) { - - ImageCropper( - modifier = Modifier - .fillMaxWidth() - .weight(1f), - imageBitmap = imageBitmap, - contentDescription = "Image Cropper", - cropStyle = cropStyle, - cropProperties = cropProperties, - crop = crop, - onCropStart = { - isCropping = true - } - ) { - croppedImage = it - isCropping = false - crop = false - showDialog = true - } - } - - BottomAppBar( - modifier = Modifier.align(Alignment.BottomStart), - actions = { - - - IconButton( - onClick = { - onSelectionPageMenuClicked(SelectionPage.Properties) - } - ) { - Icon( - Icons.Filled.Settings, - contentDescription = "Settings", - ) - - } - IconButton( - onClick = { - onSelectionPageMenuClicked(SelectionPage.Style) - } - ) { - Icon(Icons.Filled.Brush, contentDescription = "Style") - } - - IconButton( - onClick = { crop = true }) { - Icon(Icons.Filled.Crop, contentDescription = "Crop Image") - } - }, - floatingActionButton = { - ImageSelectionButton( - elevation = FloatingActionButtonDefaults.elevation(defaultElevation = 0.dp), - onImageSelected = { bitmap: ImageBitmap -> - imageBitmap = bitmap - } - ) - } - ) - - if (isCropping) { - CircularProgressIndicator() - } - } - - if (showDialog) { - croppedImage?.let { - ShowCroppedImageDialog(imageBitmap = it) { - showDialog = !showDialog - croppedImage = null - } - } - } -} - -@Composable -private fun ShowCroppedImageDialog(imageBitmap: ImageBitmap, onDismissRequest: () -> Unit) { - androidx.compose.material3.AlertDialog( - onDismissRequest = onDismissRequest, - text = { - Image( - modifier = Modifier - .drawChecker(RoundedCornerShape(8.dp)) - .fillMaxWidth() - .aspectRatio(1f), - contentScale = ContentScale.Fit, - bitmap = imageBitmap, - contentDescription = "result" - ) - }, - confirmButton = { - TextButton( - onClick = { - onDismissRequest() - } - ) { - Text("Confirm") - } - }, - dismissButton = { - TextButton( - onClick = { - onDismissRequest() - } - ) { - Text("Dismiss") - } - } - ) -} \ No newline at end of file diff --git a/app/src/main/java/com/smarttoolfactory/composecropper/demo/ImageCropDemoSimple.kt b/app/src/main/java/com/smarttoolfactory/composecropper/demo/ImageCropDemoSimple.kt deleted file mode 100644 index 5d3ced3..0000000 --- a/app/src/main/java/com/smarttoolfactory/composecropper/demo/ImageCropDemoSimple.kt +++ /dev/null @@ -1,76 +0,0 @@ -package com.smarttoolfactory.composecropper.demo - -import androidx.compose.foundation.background -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.runtime.Composable -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.graphics.ImageBitmap -import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.platform.LocalDensity -import androidx.compose.ui.res.imageResource -import androidx.compose.ui.unit.dp -import com.smarttoolfactory.composecropper.R -import com.smarttoolfactory.cropper.ImageCropper -import com.smarttoolfactory.cropper.model.OutlineType -import com.smarttoolfactory.cropper.model.RectCropShape -import com.smarttoolfactory.cropper.settings.CropDefaults -import com.smarttoolfactory.cropper.settings.CropOutlineProperty - -@Composable -fun ImageCropDemoSimple() { - - val handleSize: Float = LocalDensity.current.run { 20.dp.toPx() } - - val cropProperties by remember { - mutableStateOf( - CropDefaults.properties( - cropOutlineProperty = CropOutlineProperty( - OutlineType.Rect, - RectCropShape(0, "Rect") - ), - handleSize = handleSize - ) - ) - } - val cropStyle by remember { mutableStateOf(CropDefaults.style()) } - - val imageBitmapLarge = ImageBitmap.imageResource( - LocalContext.current.resources, - R.drawable.cinnamon - ) - - val imageBitmap by remember { mutableStateOf(imageBitmapLarge) } - val crop by remember { mutableStateOf(false) } - - - Box( - modifier = Modifier - .fillMaxSize() - .background(Color.DarkGray), - contentAlignment = Alignment.Center - ) { - Column(modifier = Modifier.fillMaxSize()) { - - ImageCropper( - modifier = Modifier - .fillMaxWidth() - .weight(1f), - imageBitmap = imageBitmap, - contentDescription = "Image Cropper", - cropStyle = cropStyle, - cropProperties = cropProperties, - crop = crop, - onCropStart = {} - ) { - } - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/smarttoolfactory/composecropper/preferences/AspectRatioSelection.kt b/app/src/main/java/com/smarttoolfactory/composecropper/preferences/AspectRatioSelection.kt deleted file mode 100644 index 26d42fa..0000000 --- a/app/src/main/java/com/smarttoolfactory/composecropper/preferences/AspectRatioSelection.kt +++ /dev/null @@ -1,53 +0,0 @@ -package com.smarttoolfactory.composecropper.preferences - -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.width -import androidx.compose.material3.MaterialTheme -import androidx.compose.runtime.* -import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.graphicsLayer -import androidx.compose.ui.unit.Dp -import androidx.compose.ui.unit.dp -import com.smarttoolfactory.animatedlist.AnimatedInfiniteLazyRow -import com.smarttoolfactory.animatedlist.model.AnimationProgress -import com.smarttoolfactory.cropper.model.CropAspectRatio -import com.smarttoolfactory.cropper.model.aspectRatios -import com.smarttoolfactory.cropper.widget.AspectRatioSelectionCard - -@Composable -internal fun AnimatedAspectRatioSelection( - modifier: Modifier = Modifier, - initialSelectedIndex: Int = 2, - onAspectRatioChange: (CropAspectRatio) -> Unit -) { - - var currentIndex by remember { mutableStateOf(initialSelectedIndex) } - - AnimatedInfiniteLazyRow( - modifier = modifier.padding(horizontal = 10.dp), - items = aspectRatios, - inactiveItemPercent = 80, - initialFirstVisibleIndex = initialSelectedIndex - 2 - ) { animationProgress: AnimationProgress, index: Int, item: CropAspectRatio, width: Dp -> - - val scale = animationProgress.scale - val color = animationProgress.color - val selectedLocalIndex = animationProgress.itemIndex - - AspectRatioSelectionCard(modifier = Modifier - .graphicsLayer { - scaleX = scale - scaleY = scale - } - .width(width), - contentColor = MaterialTheme.colorScheme.surface, - color = color, - cropAspectRatio = item - ) - - if (currentIndex != selectedLocalIndex) { - currentIndex = selectedLocalIndex - onAspectRatioChange(aspectRatios[selectedLocalIndex]) - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/smarttoolfactory/composecropper/preferences/BaseSheet.kt b/app/src/main/java/com/smarttoolfactory/composecropper/preferences/BaseSheet.kt deleted file mode 100644 index f51d905..0000000 --- a/app/src/main/java/com/smarttoolfactory/composecropper/preferences/BaseSheet.kt +++ /dev/null @@ -1,47 +0,0 @@ -package com.smarttoolfactory.composecropper.preferences - -import androidx.compose.foundation.background -import androidx.compose.foundation.layout.* -import androidx.compose.foundation.rememberScrollState -import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.foundation.verticalScroll -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.Surface -import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.unit.dp - -@Composable -internal fun BaseSheet(content: @Composable () -> Unit) { - Surface { - Column { - Box( - modifier = Modifier - .padding(vertical = 16.dp) - .fillMaxWidth(), - contentAlignment = Alignment.Center - ) { - Box( - modifier = Modifier - .width(32.dp) - .height(4.dp) - .background( - MaterialTheme.colorScheme.onSurfaceVariant.copy(alpha = .4f), - RoundedCornerShape(50) - ) - ) - } - - Column( - modifier = Modifier - .fillMaxWidth() - .height(400.dp) - .padding(horizontal = 16.dp, vertical = 8.dp) - .verticalScroll(rememberScrollState()) - ) { - content() - } - } - } -} diff --git a/app/src/main/java/com/smarttoolfactory/composecropper/preferences/ContentScaleSelection.kt b/app/src/main/java/com/smarttoolfactory/composecropper/preferences/ContentScaleSelection.kt deleted file mode 100644 index 9af5bf3..0000000 --- a/app/src/main/java/com/smarttoolfactory/composecropper/preferences/ContentScaleSelection.kt +++ /dev/null @@ -1,113 +0,0 @@ -package com.smarttoolfactory.composecropper.preferences - -import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.padding -import androidx.compose.material3.Text -import androidx.compose.runtime.* -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.layout.ContentScale -import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp - -val contentScaleOptions = - listOf("None", "Fit", "Crop", "FillBounds", "FillWidth", "FillHeight", "Inside") - -@Composable -internal fun ContentScaleDialogSelection( - contentScale: ContentScale, - onContentScaleChanged: (ContentScale) -> Unit -){ - - var showDialog by remember { mutableStateOf(false) } - - val index = when (contentScale) { - ContentScale.None -> 0 - ContentScale.Fit -> 1 - ContentScale.Crop -> 2 - ContentScale.FillBounds -> 3 - ContentScale.FillWidth -> 4 - ContentScale.FillHeight -> 5 - else -> 6 - } - - Text( - text = contentScaleOptions[index], - fontSize = 18.sp, - modifier = Modifier - .fillMaxWidth() - .clickable { - showDialog = true - } - .padding(8.dp) - ) - - if (showDialog) { - DialogWithMultipleSelection( - title = "Content Scale", - options = contentScaleOptions, - value = index, - onDismiss = { showDialog = false }, - onConfirm = { - - val scale = when (it) { - 0 -> ContentScale.None - 1 -> ContentScale.Fit - 2 -> ContentScale.Crop - 3 -> ContentScale.FillBounds - 4 -> ContentScale.FillWidth - 5 -> ContentScale.FillHeight - else -> ContentScale.Inside - } - onContentScaleChanged(scale) - showDialog = false - } - ) - } -} - -@Composable -internal fun ContentScaleExposedSelection( - contentScale: ContentScale, - onContentScaleChanged: (ContentScale) -> Unit -) { - var index = when (contentScale) { - ContentScale.None -> 0 - ContentScale.Fit -> 1 - ContentScale.Crop -> 2 - ContentScale.FillBounds -> 3 - ContentScale.FillWidth -> 4 - ContentScale.FillHeight -> 5 - else -> 6 - } - - Row( - verticalAlignment = Alignment.CenterVertically, - modifier = Modifier - .fillMaxWidth() - .padding(2.dp) - ) { - ExposedSelectionMenu( - modifier = Modifier.fillMaxWidth(), - index = index, - title = "ContentScale", - options = contentScaleOptions, - onSelected = { - index = it - val scale = when (index) { - 0 -> ContentScale.None - 1 -> ContentScale.Fit - 2 -> ContentScale.Crop - 3 -> ContentScale.FillBounds - 4 -> ContentScale.FillWidth - 5 -> ContentScale.FillHeight - else -> ContentScale.Inside - } - - onContentScaleChanged(scale) - } - ) - } -} diff --git a/app/src/main/java/com/smarttoolfactory/composecropper/preferences/CropFrameSelection.kt b/app/src/main/java/com/smarttoolfactory/composecropper/preferences/CropFrameSelection.kt deleted file mode 100644 index d6488a4..0000000 --- a/app/src/main/java/com/smarttoolfactory/composecropper/preferences/CropFrameSelection.kt +++ /dev/null @@ -1,131 +0,0 @@ -package com.smarttoolfactory.composecropper.preferences - -import CropFrameListDialog -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.width -import androidx.compose.runtime.* -import androidx.compose.ui.Modifier -import androidx.compose.ui.unit.Dp -import androidx.compose.ui.unit.dp -import com.smarttoolfactory.animatedlist.AnimatedInfiniteLazyRow -import com.smarttoolfactory.animatedlist.model.AnimationProgress -import com.smarttoolfactory.cropper.model.AspectRatio -import com.smarttoolfactory.cropper.model.CropFrame -import com.smarttoolfactory.cropper.model.OutlineType -import com.smarttoolfactory.cropper.settings.CropFrameFactory -import com.smarttoolfactory.cropper.settings.CropOutlineProperty -import com.smarttoolfactory.cropper.widget.CropFrameDisplayCard - -/** - * Crop frame selection - */ -@Composable -fun CropFrameSelection( - aspectRatio: AspectRatio, - cropFrameFactory: CropFrameFactory, - cropOutlineProperty: CropOutlineProperty, - conCropOutlinePropertyChange: (CropOutlineProperty) -> Unit -) { - - var showEditDialog by remember { mutableStateOf(false) } - - var cropFrame by remember { - mutableStateOf( - cropFrameFactory.getCropFrame(cropOutlineProperty.outlineType) - ) - } - - if (showEditDialog) { - CropFrameListDialog( - aspectRatio = aspectRatio, - cropFrame = cropFrame, - onConfirm = { - cropFrame = it - cropFrameFactory.editCropFrame(cropFrame) - - conCropOutlinePropertyChange( - CropOutlineProperty( - it.outlineType, - it.cropOutlineContainer.selectedItem - ) - ) - showEditDialog = false - }, - onDismiss = { - showEditDialog = false - } - ) - } - - val initialIndex = remember { - OutlineType.values().indexOfFirst { - it == cropOutlineProperty.outlineType - } - } - - CropFrameSelectionList( - modifier = Modifier.fillMaxWidth(), - cropFrames = cropFrameFactory.getCropFrames(), - initialSelectedIndex = initialIndex, - onClick = { - cropFrame = it - showEditDialog = true - }, - onCropFrameChange = { - conCropOutlinePropertyChange( - CropOutlineProperty( - it.outlineType, - it.cropOutlineContainer.selectedItem - ) - ) - } - ) -} - -/** - * Animated list for selecting [CropFrame] - */ -@Composable -private fun CropFrameSelectionList( - modifier: Modifier = Modifier, - initialSelectedIndex: Int = 0, - cropFrames: List, - onClick: (CropFrame) -> Unit, - onCropFrameChange: (CropFrame) -> Unit -) { - - var currentIndex by remember { mutableStateOf(initialSelectedIndex) } - - AnimatedInfiniteLazyRow( - modifier = modifier.padding(horizontal = 10.dp), - items = cropFrames, - inactiveItemPercent = 80, - initialFirstVisibleIndex = initialSelectedIndex - 2, - ) { animationProgress: AnimationProgress, index: Int, item: CropFrame, width: Dp -> - - val scale = animationProgress.scale - val color = animationProgress.color - - val selectedLocalIndex = animationProgress.itemIndex - val cropOutline = item.cropOutlineContainer.selectedItem - - val editable = item.editable - - CropFrameDisplayCard( - modifier = Modifier.width(width), - editable = editable, - scale = scale, - outlineColor = color, - title = cropOutline.title, - cropOutline = cropOutline - ) { - onClick(item) - } - - if (currentIndex != selectedLocalIndex) { - currentIndex = selectedLocalIndex - onCropFrameChange(cropFrames[selectedLocalIndex]) - } - } -} diff --git a/app/src/main/java/com/smarttoolfactory/composecropper/preferences/CropPropertySelection.kt b/app/src/main/java/com/smarttoolfactory/composecropper/preferences/CropPropertySelection.kt deleted file mode 100644 index cc21fc4..0000000 --- a/app/src/main/java/com/smarttoolfactory/composecropper/preferences/CropPropertySelection.kt +++ /dev/null @@ -1,231 +0,0 @@ -package com.smarttoolfactory.composecropper.preferences - -import androidx.compose.animation.AnimatedVisibility -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.runtime.Composable -import androidx.compose.runtime.remember -import androidx.compose.ui.Modifier -import androidx.compose.ui.platform.LocalDensity -import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp -import com.smarttoolfactory.cropper.model.AspectRatio -import com.smarttoolfactory.cropper.model.CropAspectRatio -import com.smarttoolfactory.cropper.model.aspectRatios -import com.smarttoolfactory.cropper.settings.CropFrameFactory -import com.smarttoolfactory.cropper.settings.CropProperties -import com.smarttoolfactory.cropper.settings.CropType -import kotlin.math.roundToInt - - -@Composable -internal fun CropPropertySelectionMenu( - cropFrameFactory: CropFrameFactory, - cropProperties: CropProperties, - onCropPropertiesChange: (CropProperties) -> Unit -) { - - val density = LocalDensity.current - - // Crop properties - val cropType = cropProperties.cropType - val aspectRatio = cropProperties.aspectRatio - - val handleSize = density.run { cropProperties.handleSize.toDp() } - val contentScale = cropProperties.contentScale - val cropOutlineProperty = cropProperties.cropOutlineProperty - val overlayRatio = (cropProperties.overlayRatio * 100) - - Title("Crop Type") - CropTypeDialogSelection( - cropType = cropType, - onCropTypeChange = { cropTypeChange -> - onCropPropertiesChange( - cropProperties.copy(cropType = cropTypeChange) - ) - } - ) - - Title("Content Scale") - ContentScaleDialogSelection(contentScale) { - onCropPropertiesChange( - cropProperties.copy(contentScale = it) - ) - } - - Title("Aspect Ratio") - AspectRatioSelection( - aspectRatio = aspectRatio, - onAspectRatioChange = { - onCropPropertiesChange( - cropProperties.copy(aspectRatio = it.aspectRatio) - ) - } - ) - - Title("Frame") - CropFrameSelection( - aspectRatio = aspectRatio, - cropFrameFactory = cropFrameFactory, - cropOutlineProperty = cropOutlineProperty, - conCropOutlinePropertyChange = { - onCropPropertiesChange( - cropProperties.copy(cropOutlineProperty = it) - ) - } - ) - - Title("Overlay Ratio ${overlayRatio.toInt()}%") - SliderSelection( - value = overlayRatio, valueRange = 50f..100f - ) { - onCropPropertiesChange( - cropProperties.copy(overlayRatio = (it.toInt() / 100f)) - ) - } - - // Handle size and overlay size applies only to Dynamic crop - if (cropType == CropType.Dynamic) { - Title("Handle Size") - DpSliderSelection( - value = handleSize, - onValueChange = { - onCropPropertiesChange( - cropProperties.copy(handleSize = density.run { it.toPx() }) - ) - }, - lowerBound = 10.dp, - upperBound = 40.dp - ) - } -} - -@Composable -internal fun CropGestureSelectionMenu( - cropProperties: CropProperties, - onCropPropertiesChange: (CropProperties) -> Unit -) { - // Gestures - val flingEnabled = cropProperties.fling - val pannable = cropProperties.pannable - val zoomable = cropProperties.zoomable - val maxZoom = cropProperties.maxZoom - - Title("Pan Enabled") - PanEnableSelection( - panEnabled = pannable, - onPanEnabledChange = { - onCropPropertiesChange( - cropProperties.copy(pannable = it) - ) - } - ) - - Title("Fling") - FlingEnableSelection( - flingEnabled = flingEnabled, - onFlingEnabledChange = { - onCropPropertiesChange( - cropProperties.copy(fling = it) - ) - } - ) - - Title("Zoom Enabled") - ZoomEnableSelection( - zoomEnabled = zoomable, - onZoomEnabledChange = { - onCropPropertiesChange( - cropProperties.copy(zoomable = it) - ) - } - ) - - AnimatedVisibility(visible = zoomable) { - Column { - - Title("Max Zoom ${maxZoom}x", fontSize = 16.sp) - MaxZoomSelection( - maxZoom = maxZoom, - onMaxZoomChange = { - - val max = (it * 100f).roundToInt() / 100f - onCropPropertiesChange( - cropProperties.copy(maxZoom = max) - ) - }, - valueRange = 1f..10f - ) - } - } -} - -@Composable -internal fun AspectRatioSelection( - aspectRatio: AspectRatio, - onAspectRatioChange: (CropAspectRatio) -> Unit -) { - - val initialSelectedIndex = remember { - val aspectRatios = aspectRatios - val aspectRatioModel = aspectRatios.first { it.aspectRatio.value == aspectRatio.value } - aspectRatios.indexOf(aspectRatioModel) - } - - AnimatedAspectRatioSelection( - modifier = Modifier.fillMaxWidth(), - initialSelectedIndex = initialSelectedIndex - ) { - onAspectRatioChange(it) - } -} - -@Composable -internal fun FlingEnableSelection( - flingEnabled: Boolean, - onFlingEnabledChange: (Boolean) -> Unit -) { - FullRowSwitch( - label = "Enable fling gesture", - state = flingEnabled, - onStateChange = onFlingEnabledChange - ) - -} - -@Composable -internal fun PanEnableSelection( - panEnabled: Boolean, - onPanEnabledChange: (Boolean) -> Unit -) { - FullRowSwitch( - label = "Enable pan gesture", - state = panEnabled, - onStateChange = onPanEnabledChange - ) -} - -@Composable -internal fun ZoomEnableSelection( - zoomEnabled: Boolean, - onZoomEnabledChange: (Boolean) -> Unit -) { - FullRowSwitch( - label = "Enable zoom gesture", - state = zoomEnabled, - onStateChange = onZoomEnabledChange - ) -} - -@Composable -internal fun MaxZoomSelection( - maxZoom: Float, - onMaxZoomChange: (Float) -> Unit, - valueRange: ClosedFloatingPointRange -) { - SliderSelection( - value = maxZoom, - onValueChange = onMaxZoomChange, - valueRange = valueRange - ) -} diff --git a/app/src/main/java/com/smarttoolfactory/composecropper/preferences/CropStyleSelection.kt b/app/src/main/java/com/smarttoolfactory/composecropper/preferences/CropStyleSelection.kt deleted file mode 100644 index 2071d7c..0000000 --- a/app/src/main/java/com/smarttoolfactory/composecropper/preferences/CropStyleSelection.kt +++ /dev/null @@ -1,168 +0,0 @@ -package com.smarttoolfactory.composecropper.preferences - -import androidx.compose.animation.AnimatedVisibility -import androidx.compose.foundation.background -import androidx.compose.foundation.border -import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.* -import androidx.compose.foundation.shape.CircleShape -import androidx.compose.material3.MaterialTheme -import androidx.compose.runtime.* -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.clip -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp -import com.smarttoolfactory.colorpicker.dialog.ColorPickerRingDiamondHSLDialog -import com.smarttoolfactory.cropper.settings.CropStyle -import com.smarttoolfactory.cropper.settings.CropType - -/** - * Crop style selection menu - */ -@Composable -internal fun CropStyleSelectionMenu( - cropType: CropType, - cropStyle: CropStyle, - onCropStyleChange: (CropStyle) -> Unit -) { - - BaseSheet { - val drawOverlayEnabled = cropStyle.drawOverlay - val overlayStrokeWidth = cropStyle.strokeWidth - val overlayColor = cropStyle.overlayColor - val handleColor = cropStyle.handleColor - val backgroundColor = cropStyle.backgroundColor - val drawGridEnabled = cropStyle.drawGrid - val theme = cropStyle.cropTheme - - Title("Theme") - CropThemeSelection( - cropTheme = theme, - onThemeChange = { - onCropStyleChange( - cropStyle.copy(cropTheme = it) - ) - } - ) - - - Title("Overlay") - FullRowSwitch( - label = "Draw overlay", - state = drawOverlayEnabled, - onStateChange = { - onCropStyleChange( - cropStyle.copy(drawOverlay = it) - ) - } - ) - - AnimatedVisibility( - visible = drawOverlayEnabled - ) { - - Column { - Title("StrokeWidth", fontSize = 16.sp) - DpSliderSelection( - value = overlayStrokeWidth, - onValueChange = { - onCropStyleChange( - cropStyle.copy(strokeWidth = it) - ) - }, - lowerBound = .5.dp, - upperBound = 3.dp - ) - - - ColorSelection( - title = "Overlay Color", - color = overlayColor, - onColorChange = { color: Color -> - onCropStyleChange( - cropStyle.copy(overlayColor = color) - ) - } - ) - - if(cropType== CropType.Dynamic){ - Spacer(modifier = Modifier.height(20.dp)) - ColorSelection( - title = "Handle Color", - color = handleColor, - onColorChange = { color: Color -> - onCropStyleChange( - cropStyle.copy(handleColor = color) - ) - } - ) - } - - Spacer(modifier = Modifier.height(20.dp)) - ColorSelection( - title = "Background Color", - color = backgroundColor, - onColorChange = { color: Color -> - onCropStyleChange( - cropStyle.copy(backgroundColor = color) - ) - } - ) - - Title("Grid") - FullRowSwitch( - label = "Draw grid", - state = drawGridEnabled, - onStateChange = { - onCropStyleChange( - cropStyle.copy(drawGrid = it) - ) - } - ) - } - } - } -} - -@Composable -internal fun ColorSelection( - title: String, - color: Color, - onColorChange: (Color) -> Unit -) { - - var showColorDialog by remember { mutableStateOf(false) } - Row( - modifier = Modifier.fillMaxWidth(), - verticalAlignment = Alignment.CenterVertically - ) { - Title(title, fontSize = 16.sp) - Spacer(modifier = Modifier.weight(1f)) - Box( - modifier = Modifier - .clip(CircleShape) - .size(40.dp) - .border( - 1.dp, - MaterialTheme.colorScheme.primary, - CircleShape - ) - .background(color = color) - .clickable { - showColorDialog = true - } - ) - } - - if (showColorDialog) { - ColorPickerRingDiamondHSLDialog( - initialColor = color, - onDismiss = { colorChange: Color, _: String -> - showColorDialog = false - onColorChange(colorChange) - } - ) - } -} diff --git a/app/src/main/java/com/smarttoolfactory/composecropper/preferences/CropThemeSelection.kt b/app/src/main/java/com/smarttoolfactory/composecropper/preferences/CropThemeSelection.kt deleted file mode 100644 index 8eb0ca2..0000000 --- a/app/src/main/java/com/smarttoolfactory/composecropper/preferences/CropThemeSelection.kt +++ /dev/null @@ -1,67 +0,0 @@ -package com.smarttoolfactory.composecropper.preferences - -import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.padding -import androidx.compose.material3.Text -import androidx.compose.runtime.* -import androidx.compose.ui.Modifier -import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp -import com.smarttoolfactory.cropper.settings.CropTheme - -@Composable -fun CropThemeSelection( - cropTheme: CropTheme, - onThemeChange: (CropTheme) -> Unit -) { - - val cropThemeOptions = - remember { - listOf( - CropTheme.Light.toString(), - CropTheme.Dark.toString(), - CropTheme.System.toString() - ) - } - - var showDialog by remember { mutableStateOf(false) } - - val index = when (cropTheme) { - CropTheme.Light -> 0 - CropTheme.Dark -> 1 - else -> 2 - } - - Text( - text = cropThemeOptions[index], - fontSize = 18.sp, - modifier = Modifier - .fillMaxWidth() - .clickable { - showDialog = true - } - .padding(8.dp) - - ) - - if (showDialog) { - DialogWithMultipleSelection( - title = "Theme", - options = cropThemeOptions, - value = index, - onDismiss = { showDialog = false }, - onConfirm = { - - val cropThemeChange = when (it) { - 0 -> CropTheme.Light - 1 -> CropTheme.Dark - else -> CropTheme.System - } - onThemeChange(cropThemeChange) - showDialog = false - } - ) - } - -} diff --git a/app/src/main/java/com/smarttoolfactory/composecropper/preferences/CropTypeSelection.kt b/app/src/main/java/com/smarttoolfactory/composecropper/preferences/CropTypeSelection.kt deleted file mode 100644 index 6fa115e..0000000 --- a/app/src/main/java/com/smarttoolfactory/composecropper/preferences/CropTypeSelection.kt +++ /dev/null @@ -1,93 +0,0 @@ -package com.smarttoolfactory.composecropper.preferences - -import androidx.compose.foundation.border -import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.padding -import androidx.compose.material3.Text -import androidx.compose.runtime.* -import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp -import com.smarttoolfactory.cropper.settings.CropType - -@Composable -internal fun CropTypeExposedSelection( - cropType: CropType, - onCropTypeChange: (CropType) -> Unit -) { - val cropTypeOptions = - remember { listOf(CropType.Dynamic.toString(), CropType.Static.toString()) } - - val index = when (cropType) { - CropType.Dynamic -> 0 - else -> 1 - } - - ExposedSelectionMenu( - modifier = Modifier - .fillMaxWidth() - .border(2.dp, Color.Cyan), - index = index, - title = "Crop Type", - options = cropTypeOptions, - onSelected = { - val newCropType = when (it) { - 0 -> CropType.Dynamic - else -> CropType.Static - } - - onCropTypeChange(newCropType) - } - ) -} - -@Composable -internal fun CropTypeDialogSelection( - cropType: CropType, - onCropTypeChange: (CropType) -> Unit -) { - - val cropTypeOptions = - remember { listOf(CropType.Dynamic.toString(), CropType.Static.toString()) } - - var showDialog by remember { mutableStateOf(false) } - - val index = when (cropType) { - CropType.Dynamic -> 0 - else -> 1 - } - - Text( - text = cropTypeOptions[index], - fontSize = 18.sp, - modifier = Modifier - .fillMaxWidth() - .clickable { - showDialog = true - } - .padding(8.dp) - - ) - - if (showDialog) { - DialogWithMultipleSelection( - title = "Crop Type", - options = cropTypeOptions, - value = index, - onDismiss = { showDialog = false }, - onConfirm = { - - val cropTypeChange = when (it) { - 0 -> CropType.Dynamic - else -> CropType.Static - } - onCropTypeChange(cropTypeChange) - showDialog = false - } - ) - } - -} - diff --git a/app/src/main/java/com/smarttoolfactory/composecropper/preferences/PropertySelectionSheet.kt b/app/src/main/java/com/smarttoolfactory/composecropper/preferences/PropertySelectionSheet.kt deleted file mode 100644 index c00a4c7..0000000 --- a/app/src/main/java/com/smarttoolfactory/composecropper/preferences/PropertySelectionSheet.kt +++ /dev/null @@ -1,25 +0,0 @@ -package com.smarttoolfactory.composecropper.preferences - -import androidx.compose.runtime.Composable -import com.smarttoolfactory.cropper.settings.CropFrameFactory -import com.smarttoolfactory.cropper.settings.CropProperties - -@Composable -internal fun PropertySelectionSheet( - cropFrameFactory:CropFrameFactory, - cropProperties: CropProperties, - onCropPropertiesChange: (CropProperties) -> Unit -) { - BaseSheet { - CropPropertySelectionMenu( - cropFrameFactory =cropFrameFactory, - cropProperties = cropProperties, - onCropPropertiesChange = onCropPropertiesChange - ) - - CropGestureSelectionMenu( - cropProperties = cropProperties, - onCropPropertiesChange = onCropPropertiesChange - ) - } -} diff --git a/app/src/main/java/com/smarttoolfactory/composecropper/preferences/SelctionWidgets.kt b/app/src/main/java/com/smarttoolfactory/composecropper/preferences/SelctionWidgets.kt deleted file mode 100644 index f8ecb4f..0000000 --- a/app/src/main/java/com/smarttoolfactory/composecropper/preferences/SelctionWidgets.kt +++ /dev/null @@ -1,321 +0,0 @@ -package com.smarttoolfactory.composecropper.preferences - -import androidx.compose.foundation.BorderStroke -import androidx.compose.foundation.clickable -import androidx.compose.foundation.interaction.MutableInteractionSource -import androidx.compose.foundation.layout.* -import androidx.compose.foundation.selection.selectable -import androidx.compose.foundation.selection.selectableGroup -import androidx.compose.material.RadioButton -import androidx.compose.material3.* -import androidx.compose.runtime.* -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.platform.LocalDensity -import androidx.compose.ui.semantics.Role -import androidx.compose.ui.text.TextStyle -import androidx.compose.ui.text.font.FontWeight -import androidx.compose.ui.unit.Dp -import androidx.compose.ui.unit.TextUnit -import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp -import com.smarttoolfactory.slider.ColorfulSlider -import com.smarttoolfactory.slider.MaterialSliderColors -import com.smarttoolfactory.slider.MaterialSliderDefaults -import com.smarttoolfactory.slider.SliderBrushColor - -@Composable -internal fun DpSliderSelection( - value: Dp, - onValueChange: (Dp) -> Unit, - lowerBound: Dp, - upperBound: Dp -) { - - val density = LocalDensity.current - val strokeWidthPx = density.run { value.toPx() } - val lowerBoundPx = density.run { lowerBound.toPx() } - val upperBoundPx = density.run { upperBound.toPx() } - - SliderSelection( - value = strokeWidthPx, - onValueChange = { - onValueChange( - density.run { it.toDp() } - ) - }, - valueRange = lowerBoundPx..upperBoundPx - ) -} - -@Composable -internal fun SliderWithValueSelection( - modifier: Modifier = Modifier, - value: Float, - title: String = "", - text: String, - onValueChange: (Float) -> Unit, - valueRange: ClosedFloatingPointRange, - colors: MaterialSliderColors = MaterialSliderDefaults.materialColors( - activeTrackColor = SliderBrushColor(MaterialTheme.colorScheme.primary), - inactiveTrackColor = SliderBrushColor(Color.Transparent), - thumbColor = SliderBrushColor(MaterialTheme.colorScheme.inversePrimary) - ) -) { - Column { - - Text( - text = if (title.isNotEmpty()) "$title $text" else text, - fontSize = 12.sp, - color = MaterialTheme.colorScheme.tertiary, - fontWeight = FontWeight.Bold - ) - - Row( - modifier = modifier, - verticalAlignment = Alignment.CenterVertically - ) { - ColorfulSlider( - modifier = Modifier.weight(1f), - value = value, - onValueChange = onValueChange, - valueRange = valueRange, - colors = colors, - trackHeight = 10.dp, - thumbRadius = 12.dp - ) - } - } -} - -@Composable -internal fun SliderSelection( - modifier: Modifier = Modifier, - value: Float, - valueRange: ClosedFloatingPointRange, - colors: MaterialSliderColors = MaterialSliderDefaults.materialColors( - activeTrackColor = SliderBrushColor(MaterialTheme.colorScheme.primary), - inactiveTrackColor = SliderBrushColor(Color.Transparent) - ), - onValueChangeFinished: (() -> Unit)? = null, - onValueChange: (Float) -> Unit, -) { - ColorfulSlider( - modifier = modifier, - value = value, - onValueChange = onValueChange, - valueRange = valueRange, - colors = colors, - borderStroke = BorderStroke(2.dp, MaterialTheme.colorScheme.primary), - trackHeight = 10.dp, - thumbRadius = 12.dp, - onValueChangeFinished = onValueChangeFinished - ) -} - -@Composable -internal fun Title( - text: String, - fontSize: TextUnit = 20.sp -) { - Text( - modifier = Modifier.padding(vertical = 1.dp), - text = text, - color = MaterialTheme.colorScheme.primary, - fontSize = fontSize, - fontWeight = FontWeight.Bold - ) -} - - -@Composable -internal fun FullRowSwitch( - label: String, - state: Boolean, - onStateChange: (Boolean) -> Unit -) { - - // Switch with text on right side - Row(modifier = Modifier - .fillMaxWidth() - .clickable( - interactionSource = remember { MutableInteractionSource() }, - indication = null, - role = Role.Switch, - onClick = { - onStateChange(!state) - } - ) - .padding(8.dp), - verticalAlignment = Alignment.CenterVertically - ) { - - Text(text = label, modifier = Modifier.weight(1f)) - - Switch( - checked = state, - onCheckedChange = null - ) - } -} - -@OptIn(ExperimentalMaterial3Api::class) -@Composable -internal fun CropTextField(value: String, onValueChange: (String) -> Unit) { - TextField( - value = value, - onValueChange = onValueChange, - colors = TextFieldDefaults.textFieldColors( - containerColor = Color.Transparent, - focusedIndicatorColor = Color.Transparent, - unfocusedIndicatorColor = Color.Transparent, - disabledIndicatorColor = Color.Transparent - ) - ) -} - -@Composable -internal fun DialogWithMultipleSelection( - title: String = "", - options: List, - value: Int, - onDismiss: () -> Unit, - onConfirm: (Int) -> Unit -) { - - val (selectedOption: Int, onOptionSelected: (Int) -> Unit) = remember { - mutableStateOf(value) - } - - AlertDialog( - onDismissRequest = { onDismiss() }, - title = { - Text( - title, - fontSize = 18.sp, - fontWeight = FontWeight.Bold, - color = MaterialTheme.colorScheme.primary - ) - }, - text = { - - // Note that Modifier.selectableGroup() - // is essential to ensure correct accessibility behavior - Column(Modifier.selectableGroup()) { - options.forEachIndexed { index, text -> - Row( - Modifier - .fillMaxWidth() - .selectable( - selected = (index == selectedOption), - onClick = { onOptionSelected(index) }, - role = Role.RadioButton - ) - .padding(horizontal = 4.dp, vertical = 8.dp), - verticalAlignment = Alignment.CenterVertically - ) { - RadioButton( - selected = (index == selectedOption), - onClick = null - ) - Spacer(modifier = Modifier.width(8.dp)) - Text( - text = text, - fontSize = 18.sp, - ) - } - } - } - }, - confirmButton = { - TextButton( - onClick = { - onConfirm(selectedOption) - } - ) { - Text(text = "Accept") - } - }, - dismissButton = { - TextButton( - onClick = { - onDismiss() - } - ) { - Text(text = "Cancel") - } - } - ) -} - -@OptIn(ExperimentalMaterial3Api::class) -@Composable -internal fun ExposedSelectionMenu( - modifier: Modifier = Modifier, - index: Int, - title: String? = null, - textStyle: TextStyle = TextStyle( - fontWeight = FontWeight.W600, - fontSize = 14.sp - ), - colors: TextFieldColors = ExposedDropdownMenuDefaults.textFieldColors( - containerColor = Color.Transparent, - focusedIndicatorColor = Color.Transparent, - unfocusedIndicatorColor = Color.Transparent, - disabledIndicatorColor = Color.Transparent - ), - options: List, - onSelected: (Int) -> Unit -) { - - var expanded by remember { mutableStateOf(false) } - var selectedOptionText by remember { mutableStateOf(options[index]) } - - ExposedDropdownMenuBox( - modifier = modifier, - expanded = expanded, - onExpandedChange = { - expanded = !expanded - } - ) { - TextField( - modifier = modifier, - readOnly = true, - value = selectedOptionText, - onValueChange = { }, - label = { - title?.let { - Text(it) - } - }, - trailingIcon = { - ExposedDropdownMenuDefaults.TrailingIcon( - expanded = expanded - ) - }, - colors = colors, - textStyle = textStyle - ) - ExposedDropdownMenu( - expanded = expanded, - onDismissRequest = { - expanded = false - - } - ) { - options.forEachIndexed { index: Int, selectionOption: String -> - DropdownMenuItem( - onClick = { - selectedOptionText = selectionOption - expanded = false - onSelected(index) - }, - text = { - Text(text = selectionOption) - } - ) - } - } - } -} diff --git a/app/src/main/java/com/smarttoolfactory/composecropper/preferences/frames/edit/CropFrameEditDialog.kt b/app/src/main/java/com/smarttoolfactory/composecropper/preferences/frames/edit/CropFrameEditDialog.kt deleted file mode 100644 index ee90b00..0000000 --- a/app/src/main/java/com/smarttoolfactory/composecropper/preferences/frames/edit/CropFrameEditDialog.kt +++ /dev/null @@ -1,130 +0,0 @@ -package com.smarttoolfactory.composecropper.preferences.frames.edit - -import androidx.compose.material3.AlertDialog -import androidx.compose.material3.Text -import androidx.compose.material3.TextButton -import androidx.compose.runtime.* -import androidx.compose.ui.graphics.ImageBitmap -import androidx.compose.ui.res.imageResource -import com.smarttoolfactory.cropper.R -import com.smarttoolfactory.cropper.model.* - -@Composable -fun CropFrameEditDialog( - aspectRatio: AspectRatio, - index: Int, - cropFrame: CropFrame, - onConfirm: (CropFrame) -> Unit, - onDismiss: () -> Unit -) { - - val dstBitmap = ImageBitmap.imageResource(id = R.drawable.landscape2) - - val outlineType = cropFrame.outlineType - - var outline: CropOutline by remember { - mutableStateOf(cropFrame.outlines[index]) - } - - AlertDialog( - onDismissRequest = onDismiss, - text = { - when (outlineType) { - OutlineType.RoundedRect -> { - - val shape = outline as RoundedCornerCropShape - - RoundedCornerCropShapeEdit( - aspectRatio = aspectRatio, - dstBitmap = dstBitmap, - title = outline.title, - roundedCornerCropShape = shape - ) { - outline = it - } - } - OutlineType.CutCorner -> { - val shape = outline as CutCornerCropShape - - CutCornerCropShapeEdit( - aspectRatio = aspectRatio, - dstBitmap = dstBitmap, - title = outline.title, - cutCornerCropShape = shape - ) { - outline = it - } - } - OutlineType.Oval -> { - - val shape = outline as OvalCropShape - - OvalCropShapeEdit( - aspectRatio = aspectRatio, - dstBitmap = dstBitmap, - title = outline.title, - ovalCropShape = shape - ) { - outline = it - } - } - OutlineType.Polygon -> { - - val shape = outline as PolygonCropShape - - PolygonCropShapeEdit( - aspectRatio = aspectRatio, - dstBitmap = dstBitmap, - title = outline.title, - polygonCropShape = shape - ) { - outline = it - } - } - OutlineType.ImageMask -> { - val imageMaskOutline = outline as ImageMaskOutline - ImageMaskEdit(imageMaskOutline) { - outline = it - } - } - OutlineType.Custom -> { - val customPathOutline = outline as CustomPathOutline - CustomPathEdit( - aspectRatio = aspectRatio, - dstBitmap = dstBitmap, - customPathOutline = customPathOutline, - ) { - outline = it - } - } - else -> Unit - } - }, - confirmButton = { - TextButton( - onClick = { - val newOutlines: List = cropFrame.outlines - .toMutableList() - .apply { - set(index, outline) - } - .toList() - - val newCropFrame = cropFrame.copy( - cropOutlineContainer = getOutlineContainer(outlineType, index, newOutlines) - ) - - onConfirm(newCropFrame) - }) { - Text("Accept") - } - }, - dismissButton = { - TextButton( - onClick = { onDismiss() } - ) { - Text(text = "Cancel") - } - } - ) -} diff --git a/app/src/main/java/com/smarttoolfactory/composecropper/preferences/frames/edit/CropShapeAddDialog.kt b/app/src/main/java/com/smarttoolfactory/composecropper/preferences/frames/edit/CropShapeAddDialog.kt deleted file mode 100644 index 633bd47..0000000 --- a/app/src/main/java/com/smarttoolfactory/composecropper/preferences/frames/edit/CropShapeAddDialog.kt +++ /dev/null @@ -1,115 +0,0 @@ -package com.smarttoolfactory.composecropper.preferences.frames.edit - -import androidx.compose.material3.AlertDialog -import androidx.compose.material3.Text -import androidx.compose.material3.TextButton -import androidx.compose.runtime.* -import androidx.compose.ui.graphics.ImageBitmap -import androidx.compose.ui.res.imageResource -import com.smarttoolfactory.cropper.R -import com.smarttoolfactory.cropper.model.* - -@Composable -fun CropShapeAddDialog( - aspectRatio: AspectRatio, - cropFrame: CropFrame, - onConfirm: (CropFrame) -> Unit, - onDismiss: () -> Unit -) { - - val dstBitmap = ImageBitmap.imageResource(id = R.drawable.landscape2) - - val outlineType = cropFrame.outlineType - - var outline: CropOutline by remember { - mutableStateOf(cropFrame.copy().outlines[0]) - } - - AlertDialog( - onDismissRequest = onDismiss, - text = { - when (outlineType) { - OutlineType.RoundedRect -> { - - val shape = outline as RoundedCornerCropShape - - RoundedCornerCropShapeEdit( - aspectRatio = aspectRatio, - dstBitmap = dstBitmap, - title = outline.title, - roundedCornerCropShape = shape - ) { - outline = it - } - } - OutlineType.CutCorner -> { - val shape = outline as CutCornerCropShape - - CutCornerCropShapeEdit( - aspectRatio = aspectRatio, - dstBitmap = dstBitmap, - title = outline.title, - cutCornerCropShape = shape - ) { - outline = it - } - } - OutlineType.Oval -> { - - val shape = outline as OvalCropShape - - OvalCropShapeEdit( - aspectRatio = aspectRatio, - dstBitmap = dstBitmap, - title = outline.title, - ovalCropShape = shape - ) { - outline = it - } - } - OutlineType.Polygon -> { - - val shape = outline as PolygonCropShape - - PolygonCropShapeEdit( - aspectRatio = aspectRatio, - dstBitmap = dstBitmap, - title = outline.title, - polygonCropShape = shape - ) { - outline = it - } - } - else -> Unit - } - }, - confirmButton = { - TextButton(onClick = { - - val newOutlines: List = cropFrame.outlines - .toMutableList() - .apply { - add(outline) - } - .toList() - - val newCropFrame = cropFrame.copy( - cropOutlineContainer = getOutlineContainer( - outlineType = outlineType, - index = newOutlines.size - 1, - outlines = newOutlines - ) - ) - - onConfirm(newCropFrame) - }) { - Text("Accept") - } - }, - dismissButton = { - TextButton(onClick = { onDismiss() }) { - Text(text = "Cancel") - } - } - ) -} \ No newline at end of file diff --git a/app/src/main/java/com/smarttoolfactory/composecropper/preferences/frames/edit/CustomPathEdit.kt b/app/src/main/java/com/smarttoolfactory/composecropper/preferences/frames/edit/CustomPathEdit.kt deleted file mode 100644 index a9cac2f..0000000 --- a/app/src/main/java/com/smarttoolfactory/composecropper/preferences/frames/edit/CustomPathEdit.kt +++ /dev/null @@ -1,76 +0,0 @@ -package com.smarttoolfactory.composecropper.preferences.frames.edit - -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.aspectRatio -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.runtime.* -import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.clipToBounds -import androidx.compose.ui.draw.drawWithCache -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.graphics.ImageBitmap -import androidx.compose.ui.graphics.Path -import com.smarttoolfactory.composecropper.preferences.CropTextField -import com.smarttoolfactory.cropper.model.AspectRatio -import com.smarttoolfactory.cropper.model.CustomPathOutline -import com.smarttoolfactory.cropper.util.calculateSizeAndOffsetFromAspectRatio -import com.smarttoolfactory.cropper.util.drawBlockWithCheckerAndLayer -import com.smarttoolfactory.cropper.util.scaleAndTranslatePath - -@Composable -internal fun CustomPathEdit( - aspectRatio: AspectRatio, - dstBitmap: ImageBitmap, - customPathOutline: CustomPathOutline, - onChange: (CustomPathOutline) -> Unit -) { - var newTitle by remember { - mutableStateOf(customPathOutline.title) - } - - Column { - - Box( - modifier = Modifier - .fillMaxWidth() - .clipToBounds() - .aspectRatio(4 / 3f) - .drawWithCache { - - val path = Path().apply { - addPath(customPathOutline.path) - - val (newSize, offset) = calculateSizeAndOffsetFromAspectRatio( - aspectRatio = aspectRatio, - coefficient = 1f, - size = size - ) - scaleAndTranslatePath(newSize.width, newSize.height) - translate(offset) - } - - onDrawWithContent { - drawBlockWithCheckerAndLayer(dstBitmap) { - drawPath(path, Color.Red) - - } - } - } - - ) - - CropTextField( - value = newTitle, - onValueChange = { - newTitle = it - onChange( - customPathOutline.copy( - title = newTitle - ) - ) - - } - ) - } -} diff --git a/app/src/main/java/com/smarttoolfactory/composecropper/preferences/frames/edit/CutCornerCropShapeEdit.kt b/app/src/main/java/com/smarttoolfactory/composecropper/preferences/frames/edit/CutCornerCropShapeEdit.kt deleted file mode 100644 index c7c288a..0000000 --- a/app/src/main/java/com/smarttoolfactory/composecropper/preferences/frames/edit/CutCornerCropShapeEdit.kt +++ /dev/null @@ -1,136 +0,0 @@ -package com.smarttoolfactory.composecropper.preferences.frames.edit - -import androidx.compose.foundation.layout.* -import androidx.compose.foundation.shape.CutCornerShape -import androidx.compose.runtime.* -import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.clipToBounds -import androidx.compose.ui.graphics.ImageBitmap -import androidx.compose.ui.platform.LocalDensity -import androidx.compose.ui.unit.dp -import com.smarttoolfactory.composecropper.preferences.CropTextField -import com.smarttoolfactory.composecropper.preferences.SliderWithValueSelection -import com.smarttoolfactory.cropper.model.AspectRatio -import com.smarttoolfactory.cropper.model.CornerRadiusProperties -import com.smarttoolfactory.cropper.model.CutCornerCropShape -import com.smarttoolfactory.cropper.util.drawOutlineWithBlendModeAndChecker -import kotlin.math.roundToInt - -@Composable -internal fun CutCornerCropShapeEdit( - aspectRatio: AspectRatio, - dstBitmap: ImageBitmap, - title: String, - cutCornerCropShape: CutCornerCropShape, - onChange: (CutCornerCropShape) -> Unit -) { - - var newTitle by remember { - mutableStateOf(title) - } - - val cornerRadius = remember { - cutCornerCropShape.cornerRadius - } - - var topStart by remember { - mutableStateOf( - cornerRadius.topStart - ) - } - - var topEnd by remember { - mutableStateOf( - cornerRadius.topEnd.toFloat() - ) - } - - var bottomStart by remember { - mutableStateOf( - cornerRadius.bottomStart.toFloat() - ) - } - - var bottomEnd by remember { - mutableStateOf( - cornerRadius.bottomEnd.toFloat() - ) - } - - val shape by remember { - derivedStateOf { - CutCornerShape( - topStart = topStart, - topEnd = topEnd, - bottomStart = bottomStart, - bottomEnd = bottomEnd - ) - } - } - - onChange( - cutCornerCropShape.copy( - cornerRadius = CornerRadiusProperties( - topStart = topStart, - topEnd = topEnd, - bottomStart = bottomStart, - bottomEnd = bottomEnd - ), - title = newTitle, - shape = shape - ) - ) - - Column { - - val density = LocalDensity.current - Box( - modifier = Modifier - .fillMaxWidth() - .aspectRatio(4 / 3f) - .clipToBounds() - .drawOutlineWithBlendModeAndChecker( - aspectRatio, - shape, - density, - dstBitmap - ) - ) - - CropTextField( - value = newTitle, - onValueChange = { newTitle = it } - ) - - Spacer(modifier = Modifier.height(10.dp)) - - SliderWithValueSelection( - value = topStart, - title = "Top Start", - text = "${(topStart * 10f).roundToInt() / 10f}%", - onValueChange = { topStart = it }, - valueRange = 0f..100f - ) - SliderWithValueSelection( - value = topEnd, - title = "Top End", - text = "${(topEnd * 10f).roundToInt() / 10f}%", - onValueChange = { topEnd = it }, - valueRange = 0f..100f - ) - SliderWithValueSelection( - value = bottomStart, - title = "Bottom Start", - text = "${(bottomStart * 10f).roundToInt() / 10f}%", - onValueChange = { bottomStart = it }, - valueRange = 0f..100f - ) - SliderWithValueSelection( - value = bottomEnd, - title = "Bottom End", - text = "${(bottomEnd * 10f).roundToInt() / 10f}%", - onValueChange = { bottomEnd = it }, - valueRange = 0f..100f - ) - } -} diff --git a/app/src/main/java/com/smarttoolfactory/composecropper/preferences/frames/edit/ImageMaskEdit.kt b/app/src/main/java/com/smarttoolfactory/composecropper/preferences/frames/edit/ImageMaskEdit.kt deleted file mode 100644 index d80bd18..0000000 --- a/app/src/main/java/com/smarttoolfactory/composecropper/preferences/frames/edit/ImageMaskEdit.kt +++ /dev/null @@ -1,45 +0,0 @@ -package com.smarttoolfactory.composecropper.preferences.frames.edit - -import androidx.compose.foundation.Image -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.runtime.* -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import com.smarttoolfactory.composecropper.preferences.CropTextField -import com.smarttoolfactory.cropper.model.ImageMaskOutline - -@Composable -internal fun ImageMaskEdit( - imageMaskOutline: ImageMaskOutline, - onChange: (ImageMaskOutline) -> Unit -) { - - var newTitle by remember { - mutableStateOf(imageMaskOutline.title) - } - - Column { - - Box( - modifier = Modifier.fillMaxWidth(), - contentAlignment = Alignment.Center - ) { - Image(bitmap = imageMaskOutline.image, contentDescription = "ImageMask") - - } - CropTextField( - value = newTitle, - onValueChange = { - newTitle = it - onChange( - imageMaskOutline.copy( - title = newTitle - ) - ) - - } - ) - } -} diff --git a/app/src/main/java/com/smarttoolfactory/composecropper/preferences/frames/edit/OvalCropShapeEdit.kt b/app/src/main/java/com/smarttoolfactory/composecropper/preferences/frames/edit/OvalCropShapeEdit.kt deleted file mode 100644 index 228516f..0000000 --- a/app/src/main/java/com/smarttoolfactory/composecropper/preferences/frames/edit/OvalCropShapeEdit.kt +++ /dev/null @@ -1,149 +0,0 @@ -package com.smarttoolfactory.composecropper.preferences.frames.edit - -import androidx.compose.foundation.layout.* -import androidx.compose.foundation.shape.GenericShape -import androidx.compose.runtime.* -import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.clipToBounds -import androidx.compose.ui.geometry.Offset -import androidx.compose.ui.geometry.Rect -import androidx.compose.ui.geometry.Size -import androidx.compose.ui.graphics.ImageBitmap -import androidx.compose.ui.platform.LocalDensity -import androidx.compose.ui.unit.LayoutDirection -import androidx.compose.ui.unit.dp -import com.smarttoolfactory.composecropper.preferences.CropTextField -import com.smarttoolfactory.composecropper.preferences.SliderWithValueSelection -import com.smarttoolfactory.cropper.model.AspectRatio -import com.smarttoolfactory.cropper.model.OvalCropShape -import com.smarttoolfactory.cropper.util.drawOutlineWithBlendModeAndChecker - - -@Composable -internal fun OvalCropShapeEdit( - aspectRatio: AspectRatio, - dstBitmap: ImageBitmap, - title: String, - ovalCropShape: OvalCropShape, - onChange: (OvalCropShape) -> Unit -) { - - var newTitle by remember { - mutableStateOf(title) - } - - val ovalProperties = remember { - ovalCropShape.ovalProperties - } - - var startAngle by remember { - mutableStateOf( - ovalProperties.startAngle - ) - } - - var sweepAngle by remember { - mutableStateOf( - ovalProperties.sweepAngle - ) - } - - var offsetX by remember { - mutableStateOf( - ovalProperties.offset.x - ) - } - - var offsetY by remember { - mutableStateOf( - ovalProperties.offset.y - ) - } - - val shape by remember(startAngle, sweepAngle) { - derivedStateOf { - GenericShape { size: Size, _: LayoutDirection -> - val width = size.width - val height = size.height - val diameter = width.coerceAtMost(height) - val left = (width - diameter) / 2 - val top = (height - diameter) / 2 - - val rect = Rect(offset = Offset(left, top), size = Size(diameter, diameter)) - - if (sweepAngle == 360f) { - addOval(rect) - } else { - moveTo(size.width / 2, size.height / 2) - arcTo(rect, startAngle, sweepAngle, false) - - } - - close() - } - } - } - - onChange( - ovalCropShape.copy( - ovalProperties = ovalProperties.copy( - startAngle = startAngle, - sweepAngle = sweepAngle - ), - title = newTitle, - shape = shape - ) - ) - - Column { - - val density = LocalDensity.current - Box( - modifier = Modifier - .fillMaxWidth() - .aspectRatio(4 / 3f) - .clipToBounds() - .drawOutlineWithBlendModeAndChecker( - aspectRatio, - shape, - density, - dstBitmap - ) - ) - - CropTextField( - value = newTitle, - onValueChange = { newTitle = it } - ) - - Spacer(modifier = Modifier.height(10.dp)) - - SliderWithValueSelection( - value = startAngle, - title = "Start Angle", - text = "${startAngle.toInt()}°", - onValueChange = { startAngle = it }, - valueRange = 0f..360f - ) - SliderWithValueSelection( - value = sweepAngle, - title = "Sweep Angle", - text = "${sweepAngle.toInt()}°", - onValueChange = { sweepAngle = it }, - valueRange = 0f..360f - ) - - // TODO Add offset -// Slider( -// value = offsetX, -// onValueChange = { offsetX = it }, -// valueRange = 0f..100f -// ) -// Slider( -// value = offsetY, -// onValueChange = { offsetY = it }, -// valueRange = 0f..100f -// ) - - } -} diff --git a/app/src/main/java/com/smarttoolfactory/composecropper/preferences/frames/edit/PolygonCropShapeEdit.kt b/app/src/main/java/com/smarttoolfactory/composecropper/preferences/frames/edit/PolygonCropShapeEdit.kt deleted file mode 100644 index ac9c87e..0000000 --- a/app/src/main/java/com/smarttoolfactory/composecropper/preferences/frames/edit/PolygonCropShapeEdit.kt +++ /dev/null @@ -1,108 +0,0 @@ -package com.smarttoolfactory.composecropper.preferences.frames.edit - -import androidx.compose.foundation.layout.* -import androidx.compose.runtime.* -import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.clipToBounds -import androidx.compose.ui.graphics.ImageBitmap -import androidx.compose.ui.platform.LocalDensity -import androidx.compose.ui.unit.dp -import com.smarttoolfactory.composecropper.preferences.CropTextField -import com.smarttoolfactory.composecropper.preferences.SliderWithValueSelection -import com.smarttoolfactory.cropper.model.AspectRatio -import com.smarttoolfactory.cropper.model.PolygonCropShape -import com.smarttoolfactory.cropper.util.createPolygonShape -import com.smarttoolfactory.cropper.util.drawOutlineWithBlendModeAndChecker - -@Composable -internal fun PolygonCropShapeEdit( - aspectRatio: AspectRatio, - dstBitmap: ImageBitmap, - title: String, - polygonCropShape: PolygonCropShape, - onChange: (PolygonCropShape) -> Unit -) { - - var newTitle by remember { - mutableStateOf(title) - } - - val polygonProperties = remember { - polygonCropShape.polygonProperties - } - - var sides by remember { - mutableStateOf( - polygonProperties.sides - ) - } - - var angle by remember { - mutableStateOf( - polygonProperties.angle - ) - } - - var shape by remember { - mutableStateOf( - polygonCropShape.shape - ) - } - - onChange( - polygonCropShape.copy( - polygonProperties = polygonProperties.copy( - sides = sides, - angle = angle - ), - title = newTitle, - shape = shape - ) - ) - - Column { - - val density = LocalDensity.current - Box( - modifier = Modifier - .fillMaxWidth() - .aspectRatio(4 / 3f) - .clipToBounds() - .drawOutlineWithBlendModeAndChecker( - aspectRatio, - shape, - density, - dstBitmap - ) - ) - - CropTextField( - value = newTitle, - onValueChange = { newTitle = it } - ) - - Spacer(modifier = Modifier.height(10.dp)) - - SliderWithValueSelection( - value = sides.toFloat(), - title="Sides", - text = "$sides", - onValueChange = { - sides = it.toInt() - shape = createPolygonShape(sides = sides, angle) - }, - valueRange = 3f..15f - ) - - SliderWithValueSelection( - value = angle, - title="Angle", - text = "${angle.toInt()}°", - onValueChange = { - angle = it - shape = createPolygonShape(sides = sides, degrees = angle) - }, - valueRange = 0f..360f - ) - } -} diff --git a/app/src/main/java/com/smarttoolfactory/composecropper/preferences/frames/edit/RoundedCornerCropShapeEdit.kt b/app/src/main/java/com/smarttoolfactory/composecropper/preferences/frames/edit/RoundedCornerCropShapeEdit.kt deleted file mode 100644 index 30feee4..0000000 --- a/app/src/main/java/com/smarttoolfactory/composecropper/preferences/frames/edit/RoundedCornerCropShapeEdit.kt +++ /dev/null @@ -1,136 +0,0 @@ -package com.smarttoolfactory.composecropper.preferences.frames.edit - -import androidx.compose.foundation.layout.* -import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.runtime.* -import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.clipToBounds -import androidx.compose.ui.graphics.ImageBitmap -import androidx.compose.ui.platform.LocalDensity -import androidx.compose.ui.unit.dp -import com.smarttoolfactory.composecropper.preferences.CropTextField -import com.smarttoolfactory.composecropper.preferences.SliderWithValueSelection -import com.smarttoolfactory.cropper.model.AspectRatio -import com.smarttoolfactory.cropper.model.CornerRadiusProperties -import com.smarttoolfactory.cropper.model.RoundedCornerCropShape -import com.smarttoolfactory.cropper.util.drawOutlineWithBlendModeAndChecker -import kotlin.math.roundToInt - -@Composable -internal fun RoundedCornerCropShapeEdit( - aspectRatio: AspectRatio, - dstBitmap: ImageBitmap, - title: String, - roundedCornerCropShape: RoundedCornerCropShape, - onChange: (RoundedCornerCropShape) -> Unit -) { - - var newTitle by remember { - mutableStateOf(title) - } - - val cornerRadius = remember { - roundedCornerCropShape.cornerRadius - } - - var topStart by remember { - mutableStateOf( - cornerRadius.topStart - ) - } - - var topEnd by remember { - mutableStateOf( - cornerRadius.topEnd - ) - } - - var bottomStart by remember { - mutableStateOf( - cornerRadius.bottomStart - ) - } - - var bottomEnd by remember { - mutableStateOf( - cornerRadius.bottomEnd - ) - } - - val shape by remember { - derivedStateOf { - RoundedCornerShape( - topStart = topStart, - topEnd = topEnd, - bottomStart = bottomStart, - bottomEnd = bottomEnd - ) - } - } - - onChange( - roundedCornerCropShape.copy( - cornerRadius = CornerRadiusProperties( - topStart = topStart, - topEnd = topEnd, - bottomStart = bottomStart, - bottomEnd = bottomEnd - ), - title = newTitle, - shape = shape - ) - ) - - Column { - - val density = LocalDensity.current - Box( - modifier = Modifier - .fillMaxWidth() - .aspectRatio(4 / 3f) - .clipToBounds() - .drawOutlineWithBlendModeAndChecker( - aspectRatio, - shape, - density, - dstBitmap - ) - ) - - CropTextField( - value = newTitle, - onValueChange = { newTitle = it } - ) - - Spacer(modifier=Modifier.height(10.dp)) - - SliderWithValueSelection( - value = topStart, - title = "Top Start", - text = "${(topStart * 10f).roundToInt() / 10f}%", - onValueChange = { topStart = it }, - valueRange = 0f..100f - ) - SliderWithValueSelection( - value = topEnd, - title = "Top End", - text = "${(topEnd * 10f).roundToInt() / 10f}%", - onValueChange = { topEnd = it }, - valueRange = 0f..100f - ) - SliderWithValueSelection( - value = bottomStart, - title = "Bottom Start", - text = "${(bottomStart * 10f).roundToInt() / 10f}%", - onValueChange = { bottomStart = it }, - valueRange = 0f..100f - ) - SliderWithValueSelection( - value = bottomEnd, - title = "Bottom End", - text = "${(bottomEnd * 10f).roundToInt() / 10f}%", - onValueChange = { bottomEnd = it }, - valueRange = 0f..100f - ) - } -} diff --git a/app/src/main/java/com/smarttoolfactory/composecropper/preferences/frames/list/CropFrameListDialog.kt b/app/src/main/java/com/smarttoolfactory/composecropper/preferences/frames/list/CropFrameListDialog.kt deleted file mode 100644 index 1c02b62..0000000 --- a/app/src/main/java/com/smarttoolfactory/composecropper/preferences/frames/list/CropFrameListDialog.kt +++ /dev/null @@ -1,474 +0,0 @@ -import android.annotation.SuppressLint -import android.graphics.Bitmap -import android.graphics.ImageDecoder -import android.os.Build -import android.provider.MediaStore -import androidx.activity.compose.rememberLauncherForActivityResult -import androidx.compose.foundation.Image -import androidx.compose.foundation.background -import androidx.compose.foundation.border -import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.* -import androidx.compose.foundation.lazy.grid.GridCells -import androidx.compose.foundation.lazy.grid.LazyVerticalGrid -import androidx.compose.foundation.lazy.grid.itemsIndexed -import androidx.compose.foundation.shape.CircleShape -import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.Add -import androidx.compose.material.icons.filled.Delete -import androidx.compose.material.icons.filled.Edit -import androidx.compose.material3.* -import androidx.compose.runtime.* -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.clip -import androidx.compose.ui.draw.drawWithCache -import androidx.compose.ui.draw.shadow -import androidx.compose.ui.graphics.* -import androidx.compose.ui.graphics.drawscope.translate -import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.platform.LocalDensity -import androidx.compose.ui.text.font.FontWeight -import androidx.compose.ui.text.style.TextOverflow -import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp -import com.google.modernstorage.photopicker.PhotoPicker -import com.smarttoolfactory.composecropper.preferences.frames.edit.CropFrameEditDialog -import com.smarttoolfactory.composecropper.preferences.frames.edit.CropShapeAddDialog -import com.smarttoolfactory.cropper.model.* -import com.smarttoolfactory.cropper.util.buildOutline -import com.smarttoolfactory.cropper.util.scaleAndTranslatePath - -/** - * Dialog for displaying selectable crop frames with edit, delete and new frame options - */ -@Composable -fun CropFrameListDialog( - aspectRatio: AspectRatio, - cropFrame: CropFrame, - onDismiss: () -> Unit, - onConfirm: (CropFrame) -> Unit -) { - var updatedCropFrame by remember { - mutableStateOf(cropFrame) - } - - var selectedIndex by remember { - mutableStateOf(updatedCropFrame.selectedIndex) - } - - val outlineType = cropFrame.outlineType - - var showEditDialog by remember { mutableStateOf(false) } - var showAddDialog by remember { mutableStateOf(false) } - - - if (showEditDialog) { - CropFrameEditDialog( - aspectRatio = aspectRatio, - index = selectedIndex, - cropFrame = updatedCropFrame, - onConfirm = { - updatedCropFrame = it - selectedIndex = updatedCropFrame.selectedIndex - showEditDialog = false - }, - onDismiss = { - showEditDialog = false - } - ) - } - - if (showAddDialog) { - - val outline = updatedCropFrame.cropOutlineContainer.selectedItem - if (outline is CropShape) { - CropShapeAddDialog( - aspectRatio = aspectRatio, - cropFrame = updatedCropFrame.copy(), - onConfirm = { - updatedCropFrame = it - selectedIndex = updatedCropFrame.selectedIndex - showAddDialog = false - }, - onDismiss = { - showAddDialog = false - } - ) - } else if (outline is CropImageMask) { - - PickImageMask { - - val id = updatedCropFrame.outlineCount - val newOutline = - ImageMaskOutline(id = updatedCropFrame.outlineCount, "ImageMask $id", it) - - val newOutlines: List = cropFrame.outlines - .toMutableList() - .apply { - add(newOutline) - } - .toList() - - updatedCropFrame = cropFrame.copy( - cropOutlineContainer = getOutlineContainer( - outlineType = outlineType, - index = newOutlines.size - 1, - outlines = newOutlines - ) - ) - - selectedIndex = updatedCropFrame.selectedIndex - } - } - } - - AlertDialog( - onDismissRequest = { - onDismiss() - }, - title = { - Row( - modifier = Modifier.fillMaxWidth(), - verticalAlignment = Alignment.CenterVertically - ) { - Text( - text = "Frames", - fontSize = 24.sp, - fontWeight = FontWeight.Bold, - color = MaterialTheme.colorScheme.primary - ) - Spacer(modifier = Modifier.weight(1f)) - - val enabled = selectedIndex != 0 - - Icon( - modifier = Modifier - .clip(CircleShape) - .background( - if (enabled) MaterialTheme.colorScheme.error - else Color.LightGray - ) - .size(28.dp) - .clickable( - enabled = enabled, - onClick = { - val outlines = updatedCropFrame.outlines - .toMutableList() - .apply { - removeAt(selectedIndex) - } - updatedCropFrame = updatedCropFrame.copy( - cropOutlineContainer = getOutlineContainer( - updatedCropFrame.outlineType, - outlines.size - 1, - outlines - ) - ) - - selectedIndex = outlines.size - 1 - } - ) - .padding(6.dp), - - imageVector = Icons.Default.Delete, - tint = if (enabled) Color.White else Color.Gray, - contentDescription = "Delete" - ) - - Spacer(modifier = Modifier.width(12.dp)) - Icon( - modifier = Modifier - .clip(CircleShape) - .background(MaterialTheme.colorScheme.primary) - .size(28.dp) - .clickable { - showEditDialog = true - } - .padding(6.dp), - imageVector = Icons.Default.Edit, - tint = Color.White, - contentDescription = "Edit" - ) - } - }, - text = { - CropOutlineGridList( - modifier = Modifier - .fillMaxWidth() - .heightIn(min = 240.dp), - selectedIndex = selectedIndex, - outlineType = updatedCropFrame.outlineType, - outlines = updatedCropFrame.outlines, - aspectRatio = aspectRatio, - onItemClick = { - selectedIndex = it - updatedCropFrame.selectedIndex = selectedIndex - }, - onAddItemClick = { - showAddDialog = true - } - ) - }, - dismissButton = { - TextButton( - onClick = { - onDismiss() - } - ) { - - Text("Cancel") - } - }, - confirmButton = { - TextButton( - onClick = { - onConfirm(updatedCropFrame) - } - ) { - Text("Accept") - } - } - ) -} - -@Composable -private fun CropOutlineGridList( - modifier: Modifier = Modifier, - selectedIndex: Int, - outlineType: OutlineType, - outlines: List, - aspectRatio: AspectRatio, - onItemClick: (Int) -> Unit, - onAddItemClick: () -> Unit -) { - - LazyVerticalGrid( - modifier = modifier, - contentPadding = PaddingValues(2.dp), - columns = GridCells.Fixed(3), - horizontalArrangement = Arrangement.spacedBy(14.dp), - verticalArrangement = Arrangement.spacedBy(14.dp) - ) { - - itemsIndexed( - items = outlines - ) { index, cropOutline -> - - val selected = index == selectedIndex - CropOutlineGridItem( - Modifier - .fillMaxWidth() - .aspectRatio(1f), - cropOutline = cropOutline, - selected = selected, - color = if (selected) MaterialTheme.colorScheme.primary else Color.Gray, - aspectRatio = aspectRatio - ) { - onItemClick(index) - } - } - - if (outlineType != OutlineType.Custom) { - item { - AddItemButton( - Modifier - .fillMaxWidth() - .aspectRatio(1f) - ) { - onAddItemClick() - } - } - } - } -} - -@Composable -private fun AddItemButton( - modifier: Modifier = Modifier, - onClick: () -> Unit -) { - Box( - modifier = modifier - .clickable { - onClick() - } - .padding(2.dp), - contentAlignment = Alignment.Center - ) { - - Icon( - modifier = Modifier - .fillMaxWidth(.7f) - .aspectRatio(1f), - imageVector = Icons.Default.Add, - contentDescription = "Add Outline", - tint = Color.Gray - ) - - } -} - -@Composable -private fun CropOutlineGridItem( - modifier: Modifier = Modifier, - cropOutline: CropOutline, - selected: Boolean, - color: Color, - aspectRatio: AspectRatio, - onClick: () -> Unit, -) { - - Box( - modifier = modifier - .shadow(2.dp, RoundedCornerShape(20)) - .border(2.dp, if (selected) color else Color.Unspecified, RoundedCornerShape(20)) - .background(MaterialTheme.colorScheme.surface) - .clickable { - onClick() - }, - contentAlignment = Alignment.TopEnd - ) { - - CropOutlineDisplay( - modifier = Modifier - .fillMaxWidth() - .aspectRatio(1f), - cropOutline = cropOutline, - aspectRatio, - color - ) - - - Box( - modifier = Modifier - .align(Alignment.BottomStart) - .fillMaxWidth() - .background(MaterialTheme.colorScheme.primaryContainer.copy(alpha = .7f)) - .padding(horizontal = 8.dp, vertical = 6.dp), - contentAlignment = Alignment.Center - ) { - Text( - text = cropOutline.title, - color = MaterialTheme.colorScheme.onPrimaryContainer, - fontSize = 12.sp, - maxLines = 1, - fontWeight = FontWeight.Bold, - overflow = TextOverflow.Ellipsis - ) - } - } -} - -@Composable -private fun CropOutlineDisplay( - modifier: Modifier, - cropOutline: CropOutline, - aspectRatio: AspectRatio, - color: Color -) { - - val density = LocalDensity.current - - Box( - modifier = modifier - .fillMaxWidth() - .aspectRatio(1f), - ) { - - when (cropOutline) { - - is CropShape -> { - - Box( - Modifier - .matchParentSize() - .drawWithCache { - val coefficient = .8f - - val (offset, outline) = buildOutline( - aspectRatio, - coefficient, - cropOutline.shape, - size, - layoutDirection, - density - ) - - onDrawWithContent { - translate( - left = offset.x, - top = offset.y - ) { - drawOutline( - outline = outline, - color = color - ) - } - drawContent() - } - } - ) - } - is CropPath -> { - Box( - Modifier - .matchParentSize() - .padding(12.dp) - .drawWithCache { - - val path = Path().apply { - addPath(cropOutline.path) - scaleAndTranslatePath(size.width, size.height) - } - - onDrawWithContent { - drawPath(path, color) - } - } - ) - } - is CropImageMask -> { - Box( - modifier = Modifier - .matchParentSize() - .padding(4.dp), - contentAlignment = Alignment.Center - ) { - Image(bitmap = cropOutline.image, contentDescription = "ImageMask") - } - } - } - } -} - -@SuppressLint("UnsafeOptInUsageError") -@Composable -private fun PickImageMask( - onImageSelected: (ImageBitmap) -> Unit - -) { - val context = LocalContext.current - - val photoPicker = rememberLauncherForActivityResult(PhotoPicker()) { uris -> - val uri = uris.firstOrNull() ?: return@rememberLauncherForActivityResult - - val bitmap: Bitmap = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { - ImageDecoder.decodeBitmap( - ImageDecoder.createSource(context.contentResolver, uri) - ) { decoder, info, source -> - decoder.allocator = ImageDecoder.ALLOCATOR_SOFTWARE - decoder.isMutableRequired = true - } - } else { - MediaStore.Images.Media.getBitmap(context.contentResolver, uri) - } - - onImageSelected(bitmap.asImageBitmap()) - } - - LaunchedEffect(key1 = Unit) { - photoPicker.launch(PhotoPicker.Args(PhotoPicker.Type.IMAGES_ONLY, 1)) - } -} diff --git a/app/src/main/java/com/smarttoolfactory/composecropper/ui/theme/Color.kt b/app/src/main/java/com/smarttoolfactory/composecropper/ui/theme/Color.kt deleted file mode 100644 index 11ffb2a..0000000 --- a/app/src/main/java/com/smarttoolfactory/composecropper/ui/theme/Color.kt +++ /dev/null @@ -1,11 +0,0 @@ -package com.smarttoolfactory.composecropper.ui.theme - -import androidx.compose.ui.graphics.Color - -val Purple80 = Color(0xFFD0BCFF) -val PurpleGrey80 = Color(0xFFCCC2DC) -val Pink80 = Color(0xFFEFB8C8) - -val Purple40 = Color(0xFF6650a4) -val PurpleGrey40 = Color(0xFF625b71) -val Pink40 = Color(0xFF7D5260) \ No newline at end of file diff --git a/app/src/main/java/com/smarttoolfactory/composecropper/ui/theme/Theme.kt b/app/src/main/java/com/smarttoolfactory/composecropper/ui/theme/Theme.kt deleted file mode 100644 index a486e93..0000000 --- a/app/src/main/java/com/smarttoolfactory/composecropper/ui/theme/Theme.kt +++ /dev/null @@ -1,68 +0,0 @@ -package com.smarttoolfactory.composecropper.ui.theme - -import android.app.Activity -import android.os.Build -import androidx.compose.foundation.isSystemInDarkTheme -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.darkColorScheme -import androidx.compose.material3.dynamicDarkColorScheme -import androidx.compose.material3.dynamicLightColorScheme -import androidx.compose.material3.lightColorScheme -import androidx.compose.runtime.Composable -import androidx.compose.runtime.SideEffect -import androidx.compose.ui.graphics.toArgb -import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.platform.LocalView -import androidx.core.view.ViewCompat - -private val DarkColorScheme = darkColorScheme( - primary = Purple80, - secondary = PurpleGrey80, - tertiary = Pink80 -) - -private val LightColorScheme = lightColorScheme( - primary = Purple40, - secondary = PurpleGrey40, - tertiary = Pink40 - - /* Other default colors to override - background = Color(0xFFFFFBFE), - surface = Color(0xFFFFFBFE), - onPrimary = Color.White, - onSecondary = Color.White, - onTertiary = Color.White, - onBackground = Color(0xFF1C1B1F), - onSurface = Color(0xFF1C1B1F), - */ -) - -@Composable -fun ComposeCropperTheme( - darkTheme: Boolean = isSystemInDarkTheme(), - // Dynamic color is available on Android 12+ - dynamicColor: Boolean = true, - content: @Composable () -> Unit -) { - val colorScheme = when { - dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> { - val context = LocalContext.current - if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context) - } - darkTheme -> DarkColorScheme - else -> LightColorScheme - } - val view = LocalView.current - if (!view.isInEditMode) { - SideEffect { - (view.context as Activity).window.statusBarColor = colorScheme.primary.toArgb() - ViewCompat.getWindowInsetsController(view)?.isAppearanceLightStatusBars = darkTheme - } - } - - MaterialTheme( - colorScheme = colorScheme, - typography = Typography, - content = content - ) -} \ No newline at end of file diff --git a/app/src/main/java/com/smarttoolfactory/composecropper/ui/theme/Type.kt b/app/src/main/java/com/smarttoolfactory/composecropper/ui/theme/Type.kt deleted file mode 100644 index 3d525f6..0000000 --- a/app/src/main/java/com/smarttoolfactory/composecropper/ui/theme/Type.kt +++ /dev/null @@ -1,34 +0,0 @@ -package com.smarttoolfactory.composecropper.ui.theme - -import androidx.compose.material3.Typography -import androidx.compose.ui.text.TextStyle -import androidx.compose.ui.text.font.FontFamily -import androidx.compose.ui.text.font.FontWeight -import androidx.compose.ui.unit.sp - -// Set of Material typography styles to start with -val Typography = Typography( - bodyLarge = TextStyle( - fontFamily = FontFamily.Default, - fontWeight = FontWeight.Normal, - fontSize = 16.sp, - lineHeight = 24.sp, - letterSpacing = 0.5.sp - ) - /* Other default text styles to override - titleLarge = TextStyle( - fontFamily = FontFamily.Default, - fontWeight = FontWeight.Normal, - fontSize = 22.sp, - lineHeight = 28.sp, - letterSpacing = 0.sp - ), - labelSmall = TextStyle( - fontFamily = FontFamily.Default, - fontWeight = FontWeight.Medium, - fontSize = 11.sp, - lineHeight = 16.sp, - letterSpacing = 0.5.sp - ) - */ -) \ No newline at end of file diff --git a/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/app/src/main/res/drawable-v24/ic_launcher_foreground.xml deleted file mode 100644 index 2b068d1..0000000 --- a/app/src/main/res/drawable-v24/ic_launcher_foreground.xml +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/drawable/cinnamon.jpg b/app/src/main/res/drawable/cinnamon.jpg deleted file mode 100755 index 40d9fc0..0000000 Binary files a/app/src/main/res/drawable/cinnamon.jpg and /dev/null differ diff --git a/app/src/main/res/drawable/cloud.png b/app/src/main/res/drawable/cloud.png deleted file mode 100644 index 961414d..0000000 Binary files a/app/src/main/res/drawable/cloud.png and /dev/null differ diff --git a/app/src/main/res/drawable/ic_launcher_background.xml b/app/src/main/res/drawable/ic_launcher_background.xml deleted file mode 100644 index 07d5da9..0000000 --- a/app/src/main/res/drawable/ic_launcher_background.xml +++ /dev/null @@ -1,170 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/app/src/main/res/drawable/landscape1.jpeg b/app/src/main/res/drawable/landscape1.jpeg deleted file mode 100644 index 0e06844..0000000 Binary files a/app/src/main/res/drawable/landscape1.jpeg and /dev/null differ diff --git a/app/src/main/res/drawable/landscape5.jpg b/app/src/main/res/drawable/landscape5.jpg deleted file mode 100644 index 308ca6a..0000000 Binary files a/app/src/main/res/drawable/landscape5.jpg and /dev/null differ diff --git a/app/src/main/res/drawable/squircle.png b/app/src/main/res/drawable/squircle.png deleted file mode 100644 index 411adec..0000000 Binary files a/app/src/main/res/drawable/squircle.png and /dev/null differ diff --git a/app/src/main/res/drawable/sun.png b/app/src/main/res/drawable/sun.png deleted file mode 100755 index 054071c..0000000 Binary files a/app/src/main/res/drawable/sun.png and /dev/null differ diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml deleted file mode 100644 index eca70cf..0000000 --- a/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - \ No newline at end of file diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml deleted file mode 100644 index eca70cf..0000000 --- a/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - \ No newline at end of file diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher.webp b/app/src/main/res/mipmap-hdpi/ic_launcher.webp deleted file mode 100644 index c209e78..0000000 Binary files a/app/src/main/res/mipmap-hdpi/ic_launcher.webp and /dev/null differ diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp deleted file mode 100644 index b2dfe3d..0000000 Binary files a/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp and /dev/null differ diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher.webp b/app/src/main/res/mipmap-mdpi/ic_launcher.webp deleted file mode 100644 index 4f0f1d6..0000000 Binary files a/app/src/main/res/mipmap-mdpi/ic_launcher.webp and /dev/null differ diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp deleted file mode 100644 index 62b611d..0000000 Binary files a/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp and /dev/null differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher.webp b/app/src/main/res/mipmap-xhdpi/ic_launcher.webp deleted file mode 100644 index 948a307..0000000 Binary files a/app/src/main/res/mipmap-xhdpi/ic_launcher.webp and /dev/null differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp deleted file mode 100644 index 1b9a695..0000000 Binary files a/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp and /dev/null differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp b/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp deleted file mode 100644 index 28d4b77..0000000 Binary files a/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp and /dev/null differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp deleted file mode 100644 index 9287f50..0000000 Binary files a/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp and /dev/null differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp deleted file mode 100644 index aa7d642..0000000 Binary files a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp and /dev/null differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp deleted file mode 100644 index 9126ae3..0000000 Binary files a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp and /dev/null differ diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml deleted file mode 100644 index f8c6127..0000000 --- a/app/src/main/res/values/colors.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - #FFBB86FC - #FF6200EE - #FF3700B3 - #FF03DAC5 - #FF018786 - #FF000000 - #FFFFFFFF - \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml deleted file mode 100644 index 789ea53..0000000 --- a/app/src/main/res/values/strings.xml +++ /dev/null @@ -1,3 +0,0 @@ - - Compose Cropper - \ No newline at end of file diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml deleted file mode 100644 index 2d35219..0000000 --- a/app/src/main/res/values/themes.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - -