Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix decoding videos inside asset subfolders. #1489

Merged
merged 1 commit into from Oct 1, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions coil-base/api/coil-base.api
Expand Up @@ -156,6 +156,7 @@ public abstract interface annotation class coil/annotation/ExperimentalCoilApi :
public final class coil/decode/AssetMetadata : coil/decode/ImageSource$Metadata {
public fun <init> (Ljava/lang/String;)V
public final fun getFileName ()Ljava/lang/String;
public final fun getFilePath ()Ljava/lang/String;
}

public final class coil/decode/BitmapFactoryDecoder : coil/decode/Decoder {
Expand Down
11 changes: 9 additions & 2 deletions coil-base/src/main/java/coil/decode/ImageSource.kt
Expand Up @@ -172,10 +172,17 @@ sealed class ImageSource : Closeable {
}

/**
* Metadata containing the [fileName] of an Android asset.
* Metadata containing the [filePath] of an Android asset.
*/
@ExperimentalCoilApi
class AssetMetadata(val fileName: String) : ImageSource.Metadata()
class AssetMetadata(val filePath: String) : ImageSource.Metadata() {

@Deprecated(
message = "Migrate to filePath as it supports assets inside subfolders.",
level = DeprecationLevel.ERROR
)
val fileName: String get() = filePath.substringAfter('/')
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this be substringAfterLast? If filePath has multiple subfolders, then substringAfter will only 'skip' the first.

Practically speaking, I guess it may not matter too much. Anyone who was using filePath before had their file located in the root of the assets folder. And for those files substringAfter and substringAfterLast will yield the same result. 🤷‍♂️

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great point! Fixed here: #1493

}

/**
* Metadata containing the [uri] of a `content` URI.
Expand Down
2 changes: 1 addition & 1 deletion coil-base/src/main/java/coil/fetch/AssetUriFetcher.kt
Expand Up @@ -24,7 +24,7 @@ internal class AssetUriFetcher(
source = ImageSource(
source = options.context.assets.open(path).source().buffer(),
context = options.context,
metadata = AssetMetadata(data.lastPathSegment!!)
metadata = AssetMetadata(path)
),
mimeType = MimeTypeMap.getSingleton().getMimeTypeFromUrl(path),
dataSource = DataSource.DISK
Expand Down
2 changes: 1 addition & 1 deletion coil-gif/src/main/java/coil/decode/ImageDecoderDecoder.kt
Expand Up @@ -108,7 +108,7 @@ class ImageDecoderDecoder @JvmOverloads constructor(

val metadata = metadata
if (metadata is AssetMetadata) {
return ImageDecoder.createSource(options.context.assets, metadata.fileName)
return ImageDecoder.createSource(options.context.assets, metadata.filePath)
}
if (metadata is ContentMetadata) {
return ImageDecoder.createSource(options.context.contentResolver, metadata.uri)
Expand Down
Binary file not shown.
Expand Up @@ -34,7 +34,7 @@ class VideoFrameDecoderTest {

@Test
fun noSetFrameTime() = runTest {
// MediaMetadataRetriever.getFrameAtTime does not work on the emulator pre-API 23.
// MediaMetadataRetriever does not work on the emulator pre-API 23.
assumeTrue(SDK_INT >= 23)

val result = VideoFrameDecoder(
Expand All @@ -55,7 +55,7 @@ class VideoFrameDecoderTest {

@Test
fun specificFrameTime() = runTest {
// MediaMetadataRetriever.getFrameAtTime does not work on the emulator pre-API 23.
// MediaMetadataRetriever does not work on the emulator pre-API 23.
assumeTrue(SDK_INT >= 23)

val result = VideoFrameDecoder(
Expand All @@ -81,7 +81,7 @@ class VideoFrameDecoderTest {

@Test
fun specificFramePercent() = runTest {
// MediaMetadataRetriever.getFrameAtTime does not work on the emulator pre-API 23.
// MediaMetadataRetriever does not work on the emulator pre-API 23.
assumeTrue(SDK_INT >= 23)

val result = VideoFrameDecoder(
Expand All @@ -107,7 +107,7 @@ class VideoFrameDecoderTest {

@Test
fun rotation() = runTest {
// MediaMetadataRetriever.getFrameAtTime does not work on the emulator pre-API 23.
// MediaMetadataRetriever does not work on the emulator pre-API 23.
assumeTrue(SDK_INT >= 23)

val result = VideoFrameDecoder(
Expand All @@ -125,4 +125,28 @@ class VideoFrameDecoderTest {
val expected = context.decodeBitmapAsset("video_frame_rotated.jpg")
actual.assertIsSimilarTo(expected, threshold = 0.97)
}

/** Regression test: https://github.com/coil-kt/coil/issues/1482 */
@Test
fun nestedAsset() = runTest {
// MediaMetadataRetriever does not work on the emulator pre-API 23.
assumeTrue(SDK_INT >= 23)

val path = "nested/video.mp4"
val result = VideoFrameDecoder(
source = ImageSource(
source = context.assets.open(path).source().buffer(),
context = context,
metadata = AssetMetadata(path)
),
options = Options(context)
).decode()

val actual = (result.drawable as? BitmapDrawable)?.bitmap
assertNotNull(actual)
assertFalse(result.isSampled)

val expected = context.decodeBitmapAsset("video_frame_1.jpg")
actual.assertIsSimilarTo(expected)
}
}
2 changes: 1 addition & 1 deletion coil-video/src/main/java/coil/decode/VideoFrameDecoder.kt
Expand Up @@ -182,7 +182,7 @@ class VideoFrameDecoder(
private fun MediaMetadataRetriever.setDataSource(source: ImageSource) {
when (val metadata = source.metadata) {
is AssetMetadata -> {
options.context.assets.openFd(metadata.fileName).use {
options.context.assets.openFd(metadata.filePath).use {
setDataSource(it.fileDescriptor, it.startOffset, it.length)
}
}
Expand Down