Skip to content

Commit

Permalink
Share Playback Stories in 9:16 aspect ratio
Browse files Browse the repository at this point in the history
  • Loading branch information
MiSikora committed Dec 10, 2024
1 parent 0ff4314 commit ce54d20
Show file tree
Hide file tree
Showing 3 changed files with 86 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand All @@ -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
Expand All @@ -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")
Expand Down
Original file line number Diff line number Diff line change
@@ -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<Int, Int> {
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()
}
}
Original file line number Diff line number Diff line change
@@ -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)
}
}

0 comments on commit ce54d20

Please sign in to comment.