mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
Replace custom XmlFormat with Fresco built-in (#48533)
Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/48533 Replaces the custom `XmlFormat` introduced in https://github.com/facebook/react-native/pull/46711 with the built-in support from Fresco. Fresco utilizes a very similar approach to load binary XML files and offers the XML format as part of its built-in `DefaultImageFormats` Changelog: [Android][Changed] - Replaced custom XML decoder with Fresco's built-in decoder Reviewed By: NickGerleman Differential Revision: D66553842 fbshipit-source-id: 096e60140f893d461be1cdb48e250749e58bbb4c
This commit is contained in:
committed by
Facebook GitHub Bot
parent
819b5c2c8d
commit
6feb90bb29
@@ -3311,12 +3311,6 @@ public final class com/facebook/react/modules/fresco/SystraceRequestListener : c
|
||||
public fun requiresExtraMap (Ljava/lang/String;)Z
|
||||
}
|
||||
|
||||
public final class com/facebook/react/modules/fresco/XmlFormat {
|
||||
public static final field INSTANCE Lcom/facebook/react/modules/fresco/XmlFormat;
|
||||
public final fun addDecodingCapability (Lcom/facebook/imagepipeline/decoder/ImageDecoderConfig$Builder;Landroid/content/Context;)Lcom/facebook/imagepipeline/decoder/ImageDecoderConfig$Builder;
|
||||
public final fun getDrawableFactory ()Lcom/facebook/imagepipeline/drawable/DrawableFactory;
|
||||
}
|
||||
|
||||
public final class com/facebook/react/modules/i18nmanager/I18nManagerModule : com/facebook/fbreact/specs/NativeI18nManagerSpec {
|
||||
public static final field Companion Lcom/facebook/react/modules/i18nmanager/I18nManagerModule$Companion;
|
||||
public static final field NAME Ljava/lang/String;
|
||||
|
||||
+10
-16
@@ -10,11 +10,10 @@ package com.facebook.react.modules.fresco
|
||||
import com.facebook.common.logging.FLog
|
||||
import com.facebook.drawee.backends.pipeline.DraweeConfig
|
||||
import com.facebook.drawee.backends.pipeline.Fresco
|
||||
import com.facebook.imagepipeline.backends.okhttp3.OkHttpImagePipelineConfigFactory.newBuilder
|
||||
import com.facebook.imagepipeline.backends.okhttp3.OkHttpImagePipelineConfigFactory
|
||||
import com.facebook.imagepipeline.core.DownsampleMode
|
||||
import com.facebook.imagepipeline.core.ImagePipeline
|
||||
import com.facebook.imagepipeline.core.ImagePipelineConfig
|
||||
import com.facebook.imagepipeline.decoder.ImageDecoderConfig
|
||||
import com.facebook.imagepipeline.listener.RequestListener
|
||||
import com.facebook.react.bridge.LifecycleEventListener
|
||||
import com.facebook.react.bridge.ReactApplicationContext
|
||||
@@ -80,9 +79,6 @@ constructor(
|
||||
if (!hasBeenInitialized()) {
|
||||
val pipelineConfig = config ?: getDefaultConfig(reactContext)
|
||||
val draweeConfigBuilder = DraweeConfig.newBuilder()
|
||||
if (ReactNativeFeatureFlags.loadVectorDrawablesOnImages()) {
|
||||
draweeConfigBuilder.addCustomDrawableFactory(XmlFormat.getDrawableFactory())
|
||||
}
|
||||
Fresco.initialize(
|
||||
reactContext.applicationContext,
|
||||
pipelineConfig,
|
||||
@@ -158,22 +154,20 @@ constructor(
|
||||
requestListeners.add(SystraceRequestListener())
|
||||
val client = OkHttpClientProvider.createClient()
|
||||
|
||||
// Add support for XML drawable images
|
||||
val decoderConfigBuilder = ImageDecoderConfig.Builder()
|
||||
if (ReactNativeFeatureFlags.loadVectorDrawablesOnImages()) {
|
||||
XmlFormat.addDecodingCapability(decoderConfigBuilder, context)
|
||||
}
|
||||
|
||||
// make sure to forward cookies for any requests via the okHttpClient
|
||||
// so that image requests to endpoints that use cookies still work
|
||||
val container = OkHttpCompat.getCookieJarContainer(client)
|
||||
val handler = ForwardingCookieHandler()
|
||||
container.setCookieJar(JavaNetCookieJar(handler))
|
||||
return newBuilder(context.applicationContext, client)
|
||||
.setNetworkFetcher(ReactOkHttpNetworkFetcher(client))
|
||||
.setImageDecoderConfig(decoderConfigBuilder.build())
|
||||
.setDownsampleMode(DownsampleMode.AUTO)
|
||||
.setRequestListeners(requestListeners)
|
||||
val builder =
|
||||
OkHttpImagePipelineConfigFactory.newBuilder(context.applicationContext, client)
|
||||
.setNetworkFetcher(ReactOkHttpNetworkFetcher(client))
|
||||
.setDownsampleMode(DownsampleMode.AUTO)
|
||||
.setRequestListeners(requestListeners)
|
||||
builder
|
||||
.experiment()
|
||||
.setBinaryXmlEnabled(ReactNativeFeatureFlags.loadVectorDrawablesOnImages())
|
||||
return builder
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
-143
@@ -1,143 +0,0 @@
|
||||
/*
|
||||
* 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.react.modules.fresco
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.net.Uri
|
||||
import com.facebook.common.logging.FLog
|
||||
import com.facebook.imageformat.ImageFormat
|
||||
import com.facebook.imageformat.ImageFormat.FormatChecker
|
||||
import com.facebook.imageformat.ImageFormatCheckerUtils
|
||||
import com.facebook.imagepipeline.common.ImageDecodeOptions
|
||||
import com.facebook.imagepipeline.decoder.ImageDecoder
|
||||
import com.facebook.imagepipeline.decoder.ImageDecoderConfig
|
||||
import com.facebook.imagepipeline.drawable.DrawableFactory
|
||||
import com.facebook.imagepipeline.image.CloseableImage
|
||||
import com.facebook.imagepipeline.image.DefaultCloseableImage
|
||||
import com.facebook.imagepipeline.image.EncodedImage
|
||||
import com.facebook.imagepipeline.image.QualityInfo
|
||||
|
||||
public object XmlFormat {
|
||||
|
||||
public fun addDecodingCapability(
|
||||
builder: ImageDecoderConfig.Builder,
|
||||
context: Context,
|
||||
): ImageDecoderConfig.Builder {
|
||||
return builder.addDecodingCapability(
|
||||
XmlFormat.FORMAT,
|
||||
XmlFormat.XmlFormatChecker(),
|
||||
XmlFormat.XmlFormatDecoder(context),
|
||||
)
|
||||
}
|
||||
|
||||
public fun getDrawableFactory(): DrawableFactory {
|
||||
return XmlDrawableFactory()
|
||||
}
|
||||
|
||||
private val FORMAT: ImageFormat = ImageFormat("XML", "xml")
|
||||
private const val TAG: String = "XmlFormat"
|
||||
/**
|
||||
* These are the first 4 bytes of a binary XML file. We can only support binary XML files and not
|
||||
* raw XML files because Android explicitly disallows raw XML files when inflating drawables.
|
||||
* Binary XML files are created at build time by Android's AAPT.
|
||||
*
|
||||
* @see
|
||||
* https://developer.android.com/reference/android/view/LayoutInflater#inflate(org.xmlpull.v1.XmlPullParser,%20android.view.ViewGroup)
|
||||
*/
|
||||
private val BINARY_XML_HEADER: ByteArray =
|
||||
byteArrayOf(
|
||||
3.toByte(),
|
||||
0.toByte(),
|
||||
8.toByte(),
|
||||
0.toByte(),
|
||||
)
|
||||
|
||||
private class XmlFormatChecker : FormatChecker {
|
||||
override val headerSize: Int = BINARY_XML_HEADER.size
|
||||
|
||||
override fun determineFormat(headerBytes: ByteArray, headerSize: Int): ImageFormat {
|
||||
return when {
|
||||
headerSize < BINARY_XML_HEADER.size -> ImageFormat.UNKNOWN
|
||||
ImageFormatCheckerUtils.startsWithPattern(headerBytes, BINARY_XML_HEADER) -> FORMAT
|
||||
else -> ImageFormat.UNKNOWN
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class CloseableXmlImage(val name: String, val drawable: Drawable) :
|
||||
DefaultCloseableImage() {
|
||||
private var closed = false
|
||||
|
||||
override fun getSizeInBytes(): Int {
|
||||
return getWidth() * getHeight() * 4 // 4 bytes ARGB per pixel
|
||||
}
|
||||
|
||||
override fun close() {
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
private class XmlFormatDecoder(private val context: Context) : ImageDecoder {
|
||||
override fun decode(
|
||||
encodedImage: EncodedImage,
|
||||
length: Int,
|
||||
qualityInfo: QualityInfo,
|
||||
options: ImageDecodeOptions
|
||||
): CloseableImage? {
|
||||
return try {
|
||||
val xmlResourceName = encodedImage.source ?: error("No source in encoded image")
|
||||
// Use insecure URI parser since we do not care about the validity of the URI
|
||||
val xmlResource = Uri.parse(xmlResourceName)
|
||||
// Only support binary XML files from resources, not assets or raw files
|
||||
val xmlResourceId = parseImageSourceResourceId(xmlResource)
|
||||
// Use application context to avoid leaking the activity
|
||||
val drawable = context.applicationContext.resources.getDrawable(xmlResourceId, null)
|
||||
CloseableXmlImage(xmlResourceName, drawable)
|
||||
} catch (error: Throwable) {
|
||||
FLog.e(TAG, "Cannot decode xml ${error}", error)
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This parsing implementation is only designed to work with URI's that have been generated by
|
||||
* the ResourceDrawableIdHelper that ImageSource uses. It will ignore package names and schemes
|
||||
* in its quest to extract a basic integer resource ID.
|
||||
*
|
||||
* ResourceDrawableIdHelper generates URIs in the format of res:/[resourceId]
|
||||
*
|
||||
* @throws IllegalStateException if the resource ID cannot be parsed from the provided uri
|
||||
*/
|
||||
private fun parseImageSourceResourceId(xmlResource: Uri): Int {
|
||||
return xmlResource.pathSegments.lastOrNull()?.toIntOrNull() ?: error("Invalid resource id")
|
||||
}
|
||||
}
|
||||
|
||||
private class XmlDrawableFactory : DrawableFactory {
|
||||
override fun supportsImageType(image: CloseableImage): Boolean {
|
||||
return image is CloseableXmlImage
|
||||
}
|
||||
|
||||
override fun createDrawable(image: CloseableImage): Drawable? {
|
||||
return (image as CloseableXmlImage).drawable
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user