diff --git a/modules/features/endofyear/src/main/java/au/com/shiftyjelly/pocketcasts/endofyear/StoryCaptureController.kt b/modules/features/endofyear/src/main/java/au/com/shiftyjelly/pocketcasts/endofyear/StoryCaptureController.kt index 9b70e909546..feafa54665b 100644 --- a/modules/features/endofyear/src/main/java/au/com/shiftyjelly/pocketcasts/endofyear/StoryCaptureController.kt +++ b/modules/features/endofyear/src/main/java/au/com/shiftyjelly/pocketcasts/endofyear/StoryCaptureController.kt @@ -12,10 +12,12 @@ import androidx.compose.ui.graphics.asAndroidBitmap import androidx.compose.ui.graphics.toArgb import androidx.compose.ui.platform.LocalContext import androidx.core.graphics.applyCanvas +import androidx.core.graphics.createBitmap import androidx.core.graphics.drawable.toBitmap import androidx.core.graphics.scale import au.com.shiftyjelly.pocketcasts.endofyear.ui.backgroundColor import au.com.shiftyjelly.pocketcasts.models.to.Story +import au.com.shiftyjelly.pocketcasts.utils.fitToAspectRatio import au.com.shiftyjelly.pocketcasts.utils.log.LogBuffer import au.com.shiftyjelly.pocketcasts.utils.log.LogBuffer.TAG_CRASH import dev.shreyaspatil.capturable.controller.CaptureController @@ -77,7 +79,7 @@ internal fun rememberStoryCaptureController(): StoryCaptureController { delay(50) // A small delay to settle stories animations before capturing a screenshot val controller = captureController(story) val file = runCatching { - val bitmap: Bitmap = withContext(Dispatchers.Default) { + val bitmap = withContext(Dispatchers.Default) { val background = controller.captureAsync().await() .asAndroidBitmap() .copy(Bitmap.Config.ARGB_8888, false) @@ -88,7 +90,7 @@ internal fun rememberStoryCaptureController(): StoryCaptureController { .toBitmap() .scale(width = logoWidth, height = logoHeight) - Bitmap.createBitmap(background.width, background.height, Bitmap.Config.ARGB_8888).applyCanvas { + createBitmap(background.width, background.height).applyCanvas { // Draw captured bitmap drawBitmap(background, 0f, 0f, null) // Hide bottom button behind an empty rect @@ -110,7 +112,7 @@ internal fun rememberStoryCaptureController(): StoryCaptureController { (height - buttonHeightPx + (buttonHeightPx - pcLogo.height) / 2).toFloat(), null, ) - } + }.fitToAspectRatio(9f / 16) } withContext(Dispatchers.IO) { val file = File(context.cacheDir, "pocket-casts-playback-screenshot.png") diff --git a/modules/services/utils/src/main/java/au/com/shiftyjelly/pocketcasts/utils/BitmapUtil.kt b/modules/services/utils/src/main/java/au/com/shiftyjelly/pocketcasts/utils/BitmapUtil.kt new file mode 100644 index 00000000000..11541d217a4 --- /dev/null +++ b/modules/services/utils/src/main/java/au/com/shiftyjelly/pocketcasts/utils/BitmapUtil.kt @@ -0,0 +1,43 @@ +package au.com.shiftyjelly.pocketcasts.utils + +import android.graphics.Bitmap +import android.graphics.Color +import android.graphics.Paint +import androidx.annotation.ColorInt +import androidx.core.graphics.applyCanvas +import kotlin.math.abs +import kotlin.math.roundToInt + +fun Bitmap.fitToAspectRatio( + aspectRatio: Float, + @ColorInt backgroundColor: Int = Color.BLACK, + bitmapConfig: Bitmap.Config = Bitmap.Config.ARGB_8888, +): Bitmap { + val (newWidth, newHeight) = calculateNearestSize(width, height, aspectRatio) + return Bitmap.createBitmap(newWidth, newHeight, bitmapConfig).applyCanvas { + val paint = Paint().apply { + isDither = true + color = backgroundColor + } + drawRect(0f, 0f, width.toFloat(), height.toFloat(), paint) + drawBitmap( + this@fitToAspectRatio, + abs(width - this@fitToAspectRatio.width).toFloat() / 2, + abs(height - this@fitToAspectRatio.height).toFloat() / 2, + null, + ) + } +} + +internal fun calculateNearestSize( + width: Int, + height: Int, + aspectRatio: Float, +): Pair { + check(aspectRatio > 0) { "Aspect ratio must be a positive number: $aspectRatio" } + return when (width.toFloat() / height) { + aspectRatio -> width to height + in 0f..aspectRatio -> (height * aspectRatio).roundToInt() to height + else -> width to (width / aspectRatio).roundToInt() + } +} diff --git a/modules/services/utils/src/test/java/au/com/shiftyjelly/pocketcasts/utils/BitmapUtilTest.kt b/modules/services/utils/src/test/java/au/com/shiftyjelly/pocketcasts/utils/BitmapUtilTest.kt new file mode 100644 index 00000000000..a1d65204f68 --- /dev/null +++ b/modules/services/utils/src/test/java/au/com/shiftyjelly/pocketcasts/utils/BitmapUtilTest.kt @@ -0,0 +1,38 @@ +package au.com.shiftyjelly.pocketcasts.utils + +import org.junit.Assert.assertEquals +import org.junit.Test + +class BitmapUtilTest { + @Test + fun `fail for zero aspect ratio`() { + try { + calculateNearestSize(0, 0, aspectRatio = 0f) + } catch (e: IllegalStateException) { + assertEquals("Aspect ratio must be a positive number: 0.0", e.message) + } + } + + @Test + fun `fail for negative aspect ratio`() { + try { + calculateNearestSize(0, 0, aspectRatio = -1f) + } catch (e: IllegalStateException) { + assertEquals("Aspect ratio must be a positive number: -1.0", e.message) + } + } + + @Test + fun `calculate to fit horizontally`() { + val size = calculateNearestSize(width = 100, height = 200, aspectRatio = 9f / 16) + + assertEquals(113 to 200, size) + } + + @Test + fun `calculate to fit vertically`() { + val size = calculateNearestSize(width = 200, height = 100, aspectRatio = 9f / 16) + + assertEquals(200 to 356, size) + } +} diff --git a/version.properties b/version.properties index 8874f9e8170..736e37ea391 100644 --- a/version.properties +++ b/version.properties @@ -1,2 +1,2 @@ versionName=7.79-rc-1 -versionCode=9293 \ No newline at end of file +versionCode=9294