diff --git a/app/src/main/java/com/fibelatti/photowidget/configure/PhotoWidgetConfigureScreen.kt b/app/src/main/java/com/fibelatti/photowidget/configure/PhotoWidgetConfigureScreen.kt index fd07de9..c88e3c2 100644 --- a/app/src/main/java/com/fibelatti/photowidget/configure/PhotoWidgetConfigureScreen.kt +++ b/app/src/main/java/com/fibelatti/photowidget/configure/PhotoWidgetConfigureScreen.kt @@ -215,28 +215,15 @@ private fun PhotoWidgetConfigureContent( ) } - val allPhotosCropped = remember(photos) { photos.all { it.isCropped } } - FilledTonalButton( onClick = onAddToHomeClick, modifier = Modifier .fillMaxWidth() .padding(start = 16.dp, top = 32.dp, end = 16.dp), - enabled = allPhotosCropped, ) { Text(text = stringResource(id = R.string.photo_widget_configure_add_to_home)) } - AnimatedVisibility(visible = !allPhotosCropped) { - Text( - text = stringResource(id = R.string.photo_widget_configure_cropping_required), - modifier = Modifier.padding(start = 72.dp, top = 8.dp, end = 72.dp), - textAlign = TextAlign.Center, - color = MaterialTheme.colorScheme.onSurfaceVariant, - style = MaterialTheme.typography.labelSmall, - ) - } - Spacer(modifier = Modifier.size(32.dp)) } } @@ -358,8 +345,6 @@ private fun PhotoPicker( modifier = modifier.fillMaxWidth(), verticalArrangement = Arrangement.spacedBy(8.dp), ) { - val croppedPhotos = photos.count { it.isCropped } - Text( text = stringResource( id = if (photos.isEmpty()) { @@ -367,8 +352,6 @@ private fun PhotoPicker( } else { R.string.photo_widget_configure_selected_photos }, - croppedPhotos, - photos.size, ), modifier = Modifier.padding(horizontal = 16.dp), style = MaterialTheme.typography.titleMedium, @@ -426,24 +409,6 @@ private fun PhotoPicker( role = Role.Image, onClick = { onPhotoClick(photo) }, ), - badge = { - if (!photo.isCropped) { - Image( - painter = painterResource(id = R.drawable.ic_crop), - contentDescription = "", - modifier = Modifier - .padding(all = 8.dp) - .background( - color = MaterialTheme.colorScheme.errorContainer, - shape = CircleShape, - ) - .padding(all = 4.dp) - .size(size = 12.dp) - .align(Alignment.BottomEnd), - colorFilter = ColorFilter.tint(color = MaterialTheme.colorScheme.onErrorContainer), - ) - } - }, ) } } @@ -566,11 +531,11 @@ fun ShapedPhoto( height = photoBitmap.height, ) - photoBitmap.withPolygonalShape(shape).asImageBitmap() + photoBitmap.withPolygonalShape(roundedPolygon = shape).asImageBitmap() } } else { remember(photo, aspectRatio) { - photoBitmap.withRoundedCorners().asImageBitmap() + photoBitmap.withRoundedCorners(desiredAspectRatio = aspectRatio).asImageBitmap() } } diff --git a/app/src/main/java/com/fibelatti/photowidget/configure/PhotoWidgetConfigureViewModel.kt b/app/src/main/java/com/fibelatti/photowidget/configure/PhotoWidgetConfigureViewModel.kt index c8bdff9..9becc02 100644 --- a/app/src/main/java/com/fibelatti/photowidget/configure/PhotoWidgetConfigureViewModel.kt +++ b/app/src/main/java/com/fibelatti/photowidget/configure/PhotoWidgetConfigureViewModel.kt @@ -118,19 +118,13 @@ class PhotoWidgetConfigureViewModel @Inject constructor( current.copy( photos = current.photos.map { photo -> if (photo.path == path) { - photo.copy( - isCropped = true, - timestamp = System.currentTimeMillis(), - ) + photo.copy(timestamp = System.currentTimeMillis()) } else { photo } }, selectedPhoto = if (current.selectedPhoto?.path == path) { - current.selectedPhoto.copy( - isCropped = true, - timestamp = System.currentTimeMillis(), - ) + current.selectedPhoto.copy(timestamp = System.currentTimeMillis()) } else { current.selectedPhoto }, diff --git a/app/src/main/java/com/fibelatti/photowidget/model/LocalPhoto.kt b/app/src/main/java/com/fibelatti/photowidget/model/LocalPhoto.kt index 2a0a1aa..d288e83 100644 --- a/app/src/main/java/com/fibelatti/photowidget/model/LocalPhoto.kt +++ b/app/src/main/java/com/fibelatti/photowidget/model/LocalPhoto.kt @@ -3,6 +3,5 @@ package com.fibelatti.photowidget.model data class LocalPhoto( val name: String, val path: String, - val isCropped: Boolean, val timestamp: Long = System.currentTimeMillis(), ) diff --git a/app/src/main/java/com/fibelatti/photowidget/model/PhotoWidgetAspectRatio.kt b/app/src/main/java/com/fibelatti/photowidget/model/PhotoWidgetAspectRatio.kt index fd20cb9..1b8b1a4 100644 --- a/app/src/main/java/com/fibelatti/photowidget/model/PhotoWidgetAspectRatio.kt +++ b/app/src/main/java/com/fibelatti/photowidget/model/PhotoWidgetAspectRatio.kt @@ -2,6 +2,8 @@ package com.fibelatti.photowidget.model import androidx.annotation.StringRes import com.fibelatti.photowidget.R +import kotlin.math.max +import kotlin.math.min enum class PhotoWidgetAspectRatio( val x: Float, @@ -26,5 +28,9 @@ enum class PhotoWidgetAspectRatio( ), ; - val aspectRatio: Float get(): Float = x / y + val aspectRatio: Float + get() = x / y + + val scale: Float + get() = min(x, y) / max(x, y) } diff --git a/app/src/main/java/com/fibelatti/photowidget/model/PhotoWidgetShapeBuilder.kt b/app/src/main/java/com/fibelatti/photowidget/model/PhotoWidgetShapeBuilder.kt index 5dafe7f..5c232a5 100644 --- a/app/src/main/java/com/fibelatti/photowidget/model/PhotoWidgetShapeBuilder.kt +++ b/app/src/main/java/com/fibelatti/photowidget/model/PhotoWidgetShapeBuilder.kt @@ -139,7 +139,7 @@ object PhotoWidgetShapeBuilder { } } - private fun calculateMatrix(bounds: RectF, width: Int, height: Int): Matrix { + fun calculateMatrix(bounds: RectF, width: Int, height: Int): Matrix { val scale = calculateScale(bounds = bounds, width = width, height = height) val scaledLeft = scale * bounds.left val scaledTop = scale * bounds.top diff --git a/app/src/main/java/com/fibelatti/photowidget/platform/BitmapKtx.kt b/app/src/main/java/com/fibelatti/photowidget/platform/BitmapKtx.kt index b350169..e9f3cdb 100644 --- a/app/src/main/java/com/fibelatti/photowidget/platform/BitmapKtx.kt +++ b/app/src/main/java/com/fibelatti/photowidget/platform/BitmapKtx.kt @@ -2,6 +2,7 @@ package com.fibelatti.photowidget.platform import android.graphics.Bitmap import android.graphics.Canvas +import android.graphics.Matrix import android.graphics.Paint import android.graphics.PorterDuff import android.graphics.PorterDuffXfermode @@ -9,33 +10,95 @@ import android.graphics.Rect import androidx.core.graphics.toRectF import androidx.graphics.shapes.RoundedPolygon import androidx.graphics.shapes.drawPolygon +import com.fibelatti.photowidget.model.PhotoWidgetAspectRatio +import com.fibelatti.photowidget.model.PhotoWidgetShapeBuilder +import kotlin.math.min +import kotlin.math.roundToInt fun Bitmap.withRoundedCorners( + desiredAspectRatio: PhotoWidgetAspectRatio, radius: Float = 64f, -): Bitmap = withTransformation { canvas, rect, paint -> +): Bitmap = withTransformation(desiredAspectRatio = desiredAspectRatio) { canvas, rect, paint -> canvas.drawRoundRect(rect.toRectF(), radius, radius, paint) } fun Bitmap.withPolygonalShape( roundedPolygon: RoundedPolygon, -): Bitmap = withTransformation { canvas, _, paint -> - canvas.drawPolygon(polygon = roundedPolygon, paint = paint) +): Bitmap = withTransformation(desiredAspectRatio = PhotoWidgetAspectRatio.SQUARE) { canvas, rect, paint -> + canvas.drawPolygon( + polygon = roundedPolygon.also { + it.transform( + matrix = PhotoWidgetShapeBuilder.calculateMatrix( + bounds = rect.toRectF(), + width = rect.width(), + height = rect.height(), + ), + ) + }, + paint = paint, + ) } -private inline fun Bitmap.withTransformation(body: (Canvas, Rect, Paint) -> Unit): Bitmap { - val output = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888) +private inline fun Bitmap.withTransformation( + desiredAspectRatio: PhotoWidgetAspectRatio, + body: (Canvas, Rect, Paint) -> Unit, +): Bitmap { + val source = when (desiredAspectRatio) { + PhotoWidgetAspectRatio.SQUARE -> { + val size = min(height, width) + + val top = (height - size) / 2 + val left = (width - size) / 2 + + Rect(left, top, left + size, top + size) + } + + PhotoWidgetAspectRatio.TALL -> { + val baseWidth = (height * PhotoWidgetAspectRatio.TALL.scale) + + val scaledWidth = baseWidth.roundToInt().coerceAtMost(width) + val scaledHeight = if (baseWidth > width) { + (width / baseWidth.roundToInt()) * height + } else { + height + } + + val top = (height - scaledHeight) / 2 + val left = (width - scaledWidth) / 2 + + Rect(left, top, left + scaledWidth, top + scaledHeight) + } + + PhotoWidgetAspectRatio.WIDE -> { + val baseHeight = (width * PhotoWidgetAspectRatio.WIDE.scale) + + val scaledHeight = baseHeight.roundToInt().coerceAtMost(height) + val scaledWidth = if (baseHeight > height) { + (height / baseHeight.roundToInt()) * width + } else { + width + } + + val top = (height - scaledHeight) / 2 + val left = (width - scaledWidth) / 2 + + Rect(left, top, left + scaledWidth, top + scaledHeight) + } + } + val destination = Rect(0, 0, source.width(), source.height()) + + val output = Bitmap.createBitmap(source.width(), source.height(), Bitmap.Config.ARGB_8888) val canvas = Canvas(output) val paint = Paint().apply { isAntiAlias = true } - val rect = Rect(0, 0, width, height) canvas.drawARGB(0, 0, 0, 0) - body(canvas, rect, paint) + body(canvas, if (PhotoWidgetAspectRatio.SQUARE == desiredAspectRatio) source else destination, paint) paint.setXfermode(PorterDuffXfermode(PorterDuff.Mode.SRC_IN)) - canvas.drawBitmap(this, rect, rect, paint) + canvas.drawBitmap(this, source, destination, paint) return output } diff --git a/app/src/main/java/com/fibelatti/photowidget/widget/PhotoWidgetProvider.kt b/app/src/main/java/com/fibelatti/photowidget/widget/PhotoWidgetProvider.kt index c3ff4ca..de7ddc3 100644 --- a/app/src/main/java/com/fibelatti/photowidget/widget/PhotoWidgetProvider.kt +++ b/app/src/main/java/com/fibelatti/photowidget/widget/PhotoWidgetProvider.kt @@ -112,9 +112,9 @@ class PhotoWidgetProvider : AppWidgetProvider() { height = bitmap.height, ) - bitmap.withPolygonalShape(shape) + bitmap.withPolygonalShape(roundedPolygon = shape) } else { - bitmap.withRoundedCorners() + bitmap.withRoundedCorners(desiredAspectRatio = aspectRatio) } return RemoteViews(context.packageName, R.layout.photo_widget).apply { diff --git a/app/src/main/java/com/fibelatti/photowidget/widget/PhotoWidgetStorage.kt b/app/src/main/java/com/fibelatti/photowidget/widget/PhotoWidgetStorage.kt index a58c593..e949e67 100644 --- a/app/src/main/java/com/fibelatti/photowidget/widget/PhotoWidgetStorage.kt +++ b/app/src/main/java/com/fibelatti/photowidget/widget/PhotoWidgetStorage.kt @@ -87,7 +87,6 @@ class PhotoWidgetStorage @Inject constructor(@ApplicationContext context: Contex return@withContext LocalPhoto( name = newPhotoName, path = croppedPhoto.path, - isCropped = false, ) } @@ -100,7 +99,6 @@ class PhotoWidgetStorage @Inject constructor(@ApplicationContext context: Contex LocalPhoto( name = file, path = "$dir/$file", - isCropped = true, ) } } diff --git a/app/src/main/res/values-pt/strings.xml b/app/src/main/res/values-pt/strings.xml index a8d89eb..eeb0a46 100644 --- a/app/src/main/res/values-pt/strings.xml +++ b/app/src/main/res/values-pt/strings.xml @@ -38,7 +38,7 @@ As alterações serão perdidas, voltar mesmo assim? Escolha uma foto - Fotos escolhidas (%1$d/%2$d recortadas) + Fotos escolhidas Recortar Remover @@ -55,7 +55,6 @@ Forma Adicionar à tela inicial - Você deve recortar todas as fotos antes de adicionar este widget à sua tela inicial Sim diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 805a6c6..a404d08 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -38,7 +38,7 @@ Any changes will be lost, go back anyway? Select a photo - Selected photos (%1$d/%2$d cropped) + Selected photos Crop Remove @@ -55,7 +55,6 @@ Applied shape Add to home screen - You must crop all photos before adding this widget to your home screen Yes