Skip to content

Commit

Permalink
Add setup for supporting binary XML drawables
Browse files Browse the repository at this point in the history
Reviewed By: oprisnik

Differential Revision: D64332678

fbshipit-source-id: 5238183624f0311e8c8497b3713d1b2a1a52a04d
  • Loading branch information
Abbondanzo authored and facebook-github-bot committed Oct 17, 2024
1 parent de0c977 commit 34c6456
Show file tree
Hide file tree
Showing 3 changed files with 132 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

package com.facebook.imagepipeline.xml

import android.graphics.drawable.Drawable
import com.facebook.imagepipeline.image.DefaultCloseableImage

internal class CloseableXmlImage(private var drawable: Drawable?) : DefaultCloseableImage() {
private var closed = false

override fun getSizeInBytes(): Int {
return getWidth() * getHeight() * 4 // 4 bytes ARGB per pixel
}

override fun close() {
drawable = null
closed = true
}

override fun isClosed(): Boolean {
return closed
}

override fun getWidth(): Int {
return drawable?.intrinsicWidth?.takeIf { it >= 0 } ?: 0
}

override fun getHeight(): Int {
return drawable?.intrinsicHeight?.takeIf { it >= 0 } ?: 0
}

fun buildCopy(): Drawable? {
return drawable?.constantState?.newDrawable()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

package com.facebook.imagepipeline.xml

import android.graphics.drawable.Drawable
import com.facebook.imagepipeline.drawable.DrawableFactory
import com.facebook.imagepipeline.image.CloseableImage

internal class XmlDrawableFactory : DrawableFactory {
override fun supportsImageType(image: CloseableImage): Boolean {
return image is CloseableXmlImage
}

override fun createDrawable(image: CloseableImage): Drawable? {
return (image as? CloseableXmlImage)?.buildCopy()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

package com.facebook.imagepipeline.xml

import android.content.res.Resources
import android.net.Uri
import androidx.core.content.res.ResourcesCompat
import com.facebook.common.logging.FLog
import com.facebook.common.util.UriUtil
import com.facebook.imagepipeline.common.ImageDecodeOptions
import com.facebook.imagepipeline.decoder.ImageDecoder
import com.facebook.imagepipeline.image.CloseableImage
import com.facebook.imagepipeline.image.EncodedImage
import com.facebook.imagepipeline.image.QualityInfo
import java.util.concurrent.ConcurrentHashMap

internal class XmlFormatDecoder(private val resources: Resources) : ImageDecoder {

private val resourceIdCache: MutableMap<String, Int> = ConcurrentHashMap()

override fun decode(
encodedImage: EncodedImage,
length: Int,
qualityInfo: QualityInfo,
options: ImageDecodeOptions
): CloseableImage? {
return try {
val xmlResourceName = encodedImage.source ?: error("No source in encoded image")
val xmlResourceId = getXmlResourceId(xmlResourceName)
val drawable = ResourcesCompat.getDrawable(resources, xmlResourceId, null)
drawable?.let { CloseableXmlImage(it) }
} catch (error: Throwable) {
FLog.e(TAG, "Cannot decode xml", error)
null
}
}

private fun getXmlResourceId(xmlResourceName: String): Int {
return resourceIdCache.getOrPut(xmlResourceName) {
parseImageSourceResourceId(Uri.parse(xmlResourceName))
}
}

/**
* This parsing implementation is only designed to work with URIs that have been generated by
* UriUtil, which inserts the resource ID into the path of the URI.
* - Local resource URI format: res://[resourceId]
* - Qualified resource URI format: res://[package]/[resourceId]
*
* @throws IllegalStateException if the resource ID cannot be parsed from the provided uri
*/
private fun parseImageSourceResourceId(xmlResource: Uri): Int {
return if (UriUtil.isLocalResourceUri(xmlResource) ||
UriUtil.isQualifiedResourceUri(xmlResource)) {
xmlResource.pathSegments.lastOrNull()?.toIntOrNull()
?: error("Unable to read resource ID from ${xmlResource.path}")
} else {
error("Unsupported uri ${xmlResource}")
}
}

companion object {
private const val TAG: String = "XmlFormatDecoder"
}
}

0 comments on commit 34c6456

Please sign in to comment.