Add feature flag to enable vector drawable support (#45394)

Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/45394

Changelog: [Internal]

The current `Image` implementation does not support loading [vector drawables](https://developer.android.com/develop/ui/views/graphics/vector-drawable-resources) on Android, and support has been requested many times ([issue](https://github.com/facebook/react-native/issues/16651), [issue](https://github.com/facebook/react-native/issues/27502)). I am proposing to put support behind this feature flag in order to validate against performance regressions. Adding support will require reading files from disk to determine if the resource is actually an XML file.

Reviewed By: cortinico

Differential Revision: D59647903

fbshipit-source-id: 2445d71b6769266abe4c2dda521cc2be89bf4064
This commit is contained in:
Peter Abbondanzo
2024-07-12 07:34:01 -07:00
committed by Facebook GitHub Bot
parent ab485c6a94
commit bac5f1ff3a
19 changed files with 129 additions and 27 deletions
@@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<9f31fa21b0dd908ec53d1586c0bd271d>>
* @generated SignedSource<<f347d3eae685a0888e834ec8ffffa20e>>
*/
/**
@@ -148,6 +148,12 @@ public object ReactNativeFeatureFlags {
@JvmStatic
public fun lazyAnimationCallbacks(): Boolean = accessor.lazyAnimationCallbacks()
/**
* Adds support for loading vector drawable assets in the Image component (only on Android)
*/
@JvmStatic
public fun loadVectorDrawablesOnImages(): Boolean = accessor.loadVectorDrawablesOnImages()
/**
* Propagate layout direction to Android views.
*/
@@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<718c08632274058c4ece4c15ed06fbfe>>
* @generated SignedSource<<03b78dd68d80c74a7be56aef64daf1e1>>
*/
/**
@@ -40,6 +40,7 @@ public class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAccesso
private var fuseboxEnabledReleaseCache: Boolean? = null
private var initEagerTurboModulesOnNativeModulesQueueAndroidCache: Boolean? = null
private var lazyAnimationCallbacksCache: Boolean? = null
private var loadVectorDrawablesOnImagesCache: Boolean? = null
private var setAndroidLayoutDirectionCache: Boolean? = null
private var useImmediateExecutorInAndroidBridgelessCache: Boolean? = null
private var useModernRuntimeSchedulerCache: Boolean? = null
@@ -229,6 +230,15 @@ public class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAccesso
return cached
}
override fun loadVectorDrawablesOnImages(): Boolean {
var cached = loadVectorDrawablesOnImagesCache
if (cached == null) {
cached = ReactNativeFeatureFlagsCxxInterop.loadVectorDrawablesOnImages()
loadVectorDrawablesOnImagesCache = cached
}
return cached
}
override fun setAndroidLayoutDirection(): Boolean {
var cached = setAndroidLayoutDirectionCache
if (cached == null) {
@@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<9af9576364f2ec8e978604eaa1c15ee8>>
* @generated SignedSource<<b5222bfeef575fb3061ad4be8e4a1a1a>>
*/
/**
@@ -68,6 +68,8 @@ public object ReactNativeFeatureFlagsCxxInterop {
@DoNotStrip @JvmStatic public external fun lazyAnimationCallbacks(): Boolean
@DoNotStrip @JvmStatic public external fun loadVectorDrawablesOnImages(): Boolean
@DoNotStrip @JvmStatic public external fun setAndroidLayoutDirection(): Boolean
@DoNotStrip @JvmStatic public external fun useImmediateExecutorInAndroidBridgeless(): Boolean
@@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<ea19295997ce3b9bdebcc37989d53096>>
* @generated SignedSource<<3e0a7901e165b338871f60c22f1615a9>>
*/
/**
@@ -63,6 +63,8 @@ public open class ReactNativeFeatureFlagsDefaults : ReactNativeFeatureFlagsProvi
override fun lazyAnimationCallbacks(): Boolean = false
override fun loadVectorDrawablesOnImages(): Boolean = false
override fun setAndroidLayoutDirection(): Boolean = true
override fun useImmediateExecutorInAndroidBridgeless(): Boolean = false
@@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<63cb1200d25489b5cc9fcfa0061eccd0>>
* @generated SignedSource<<f81b71950924d0e555b9173c2a98655f>>
*/
/**
@@ -44,6 +44,7 @@ public class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcces
private var fuseboxEnabledReleaseCache: Boolean? = null
private var initEagerTurboModulesOnNativeModulesQueueAndroidCache: Boolean? = null
private var lazyAnimationCallbacksCache: Boolean? = null
private var loadVectorDrawablesOnImagesCache: Boolean? = null
private var setAndroidLayoutDirectionCache: Boolean? = null
private var useImmediateExecutorInAndroidBridgelessCache: Boolean? = null
private var useModernRuntimeSchedulerCache: Boolean? = null
@@ -253,6 +254,16 @@ public class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcces
return cached
}
override fun loadVectorDrawablesOnImages(): Boolean {
var cached = loadVectorDrawablesOnImagesCache
if (cached == null) {
cached = currentProvider.loadVectorDrawablesOnImages()
accessedFeatureFlags.add("loadVectorDrawablesOnImages")
loadVectorDrawablesOnImagesCache = cached
}
return cached
}
override fun setAndroidLayoutDirection(): Boolean {
var cached = setAndroidLayoutDirectionCache
if (cached == null) {
@@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<588266c6ec144651b7fdc84b77b490e7>>
* @generated SignedSource<<77a3249e264ca893ce8f998d424cb3db>>
*/
/**
@@ -63,6 +63,8 @@ public interface ReactNativeFeatureFlagsProvider {
@DoNotStrip public fun lazyAnimationCallbacks(): Boolean
@DoNotStrip public fun loadVectorDrawablesOnImages(): Boolean
@DoNotStrip public fun setAndroidLayoutDirection(): Boolean
@DoNotStrip public fun useImmediateExecutorInAndroidBridgeless(): Boolean
@@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<9389654cf7ca12851a1af307656c807b>>
* @generated SignedSource<<61b408d16b81cca09f270c0a059166a4>>
*/
/**
@@ -159,6 +159,12 @@ class ReactNativeFeatureFlagsProviderHolder
return method(javaProvider_);
}
bool loadVectorDrawablesOnImages() override {
static const auto method =
getReactNativeFeatureFlagsProviderJavaClass()->getMethod<jboolean()>("loadVectorDrawablesOnImages");
return method(javaProvider_);
}
bool setAndroidLayoutDirection() override {
static const auto method =
getReactNativeFeatureFlagsProviderJavaClass()->getMethod<jboolean()>("setAndroidLayoutDirection");
@@ -311,6 +317,11 @@ bool JReactNativeFeatureFlagsCxxInterop::lazyAnimationCallbacks(
return ReactNativeFeatureFlags::lazyAnimationCallbacks();
}
bool JReactNativeFeatureFlagsCxxInterop::loadVectorDrawablesOnImages(
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop> /*unused*/) {
return ReactNativeFeatureFlags::loadVectorDrawablesOnImages();
}
bool JReactNativeFeatureFlagsCxxInterop::setAndroidLayoutDirection(
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop> /*unused*/) {
return ReactNativeFeatureFlags::setAndroidLayoutDirection();
@@ -428,6 +439,9 @@ void JReactNativeFeatureFlagsCxxInterop::registerNatives() {
makeNativeMethod(
"lazyAnimationCallbacks",
JReactNativeFeatureFlagsCxxInterop::lazyAnimationCallbacks),
makeNativeMethod(
"loadVectorDrawablesOnImages",
JReactNativeFeatureFlagsCxxInterop::loadVectorDrawablesOnImages),
makeNativeMethod(
"setAndroidLayoutDirection",
JReactNativeFeatureFlagsCxxInterop::setAndroidLayoutDirection),
@@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<9d36812c37a99a514fc80e83ec056365>>
* @generated SignedSource<<c5a1699b49b23c33607b58ef90a9db5b>>
*/
/**
@@ -90,6 +90,9 @@ class JReactNativeFeatureFlagsCxxInterop
static bool lazyAnimationCallbacks(
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop>);
static bool loadVectorDrawablesOnImages(
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop>);
static bool setAndroidLayoutDirection(
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop>);
@@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<a26a8a821a11fcde6e71dda68b61e9f1>>
* @generated SignedSource<<19c9de459447304ed033bdef70cd2cdf>>
*/
/**
@@ -101,6 +101,10 @@ bool ReactNativeFeatureFlags::lazyAnimationCallbacks() {
return getAccessor().lazyAnimationCallbacks();
}
bool ReactNativeFeatureFlags::loadVectorDrawablesOnImages() {
return getAccessor().loadVectorDrawablesOnImages();
}
bool ReactNativeFeatureFlags::setAndroidLayoutDirection() {
return getAccessor().setAndroidLayoutDirection();
}
@@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<b095e4240d21711ac9818a71cd9b56cb>>
* @generated SignedSource<<a03bd61ef6b064d7f46a02478b8582e3>>
*/
/**
@@ -137,6 +137,11 @@ class ReactNativeFeatureFlags {
*/
RN_EXPORT static bool lazyAnimationCallbacks();
/**
* Adds support for loading vector drawable assets in the Image component (only on Android)
*/
RN_EXPORT static bool loadVectorDrawablesOnImages();
/**
* Propagate layout direction to Android views.
*/
@@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<213b150588cbaf067c937cfa755791ca>>
* @generated SignedSource<<760396297e88583dbed0aea01ff73925>>
*/
/**
@@ -389,6 +389,24 @@ bool ReactNativeFeatureFlagsAccessor::lazyAnimationCallbacks() {
return flagValue.value();
}
bool ReactNativeFeatureFlagsAccessor::loadVectorDrawablesOnImages() {
auto flagValue = loadVectorDrawablesOnImages_.load();
if (!flagValue.has_value()) {
// This block is not exclusive but it is not necessary.
// If multiple threads try to initialize the feature flag, we would only
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(20, "loadVectorDrawablesOnImages");
flagValue = currentProvider_->loadVectorDrawablesOnImages();
loadVectorDrawablesOnImages_ = flagValue;
}
return flagValue.value();
}
bool ReactNativeFeatureFlagsAccessor::setAndroidLayoutDirection() {
auto flagValue = setAndroidLayoutDirection_.load();
@@ -398,7 +416,7 @@ bool ReactNativeFeatureFlagsAccessor::setAndroidLayoutDirection() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(20, "setAndroidLayoutDirection");
markFlagAsAccessed(21, "setAndroidLayoutDirection");
flagValue = currentProvider_->setAndroidLayoutDirection();
setAndroidLayoutDirection_ = flagValue;
@@ -416,7 +434,7 @@ bool ReactNativeFeatureFlagsAccessor::useImmediateExecutorInAndroidBridgeless()
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(21, "useImmediateExecutorInAndroidBridgeless");
markFlagAsAccessed(22, "useImmediateExecutorInAndroidBridgeless");
flagValue = currentProvider_->useImmediateExecutorInAndroidBridgeless();
useImmediateExecutorInAndroidBridgeless_ = flagValue;
@@ -434,7 +452,7 @@ bool ReactNativeFeatureFlagsAccessor::useModernRuntimeScheduler() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(22, "useModernRuntimeScheduler");
markFlagAsAccessed(23, "useModernRuntimeScheduler");
flagValue = currentProvider_->useModernRuntimeScheduler();
useModernRuntimeScheduler_ = flagValue;
@@ -452,7 +470,7 @@ bool ReactNativeFeatureFlagsAccessor::useNativeViewConfigsInBridgelessMode() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(23, "useNativeViewConfigsInBridgelessMode");
markFlagAsAccessed(24, "useNativeViewConfigsInBridgelessMode");
flagValue = currentProvider_->useNativeViewConfigsInBridgelessMode();
useNativeViewConfigsInBridgelessMode_ = flagValue;
@@ -470,7 +488,7 @@ bool ReactNativeFeatureFlagsAccessor::useNewReactImageViewBackgroundDrawing() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(24, "useNewReactImageViewBackgroundDrawing");
markFlagAsAccessed(25, "useNewReactImageViewBackgroundDrawing");
flagValue = currentProvider_->useNewReactImageViewBackgroundDrawing();
useNewReactImageViewBackgroundDrawing_ = flagValue;
@@ -488,7 +506,7 @@ bool ReactNativeFeatureFlagsAccessor::useRuntimeShadowNodeReferenceUpdate() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(25, "useRuntimeShadowNodeReferenceUpdate");
markFlagAsAccessed(26, "useRuntimeShadowNodeReferenceUpdate");
flagValue = currentProvider_->useRuntimeShadowNodeReferenceUpdate();
useRuntimeShadowNodeReferenceUpdate_ = flagValue;
@@ -506,7 +524,7 @@ bool ReactNativeFeatureFlagsAccessor::useRuntimeShadowNodeReferenceUpdateOnLayou
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(26, "useRuntimeShadowNodeReferenceUpdateOnLayout");
markFlagAsAccessed(27, "useRuntimeShadowNodeReferenceUpdateOnLayout");
flagValue = currentProvider_->useRuntimeShadowNodeReferenceUpdateOnLayout();
useRuntimeShadowNodeReferenceUpdateOnLayout_ = flagValue;
@@ -524,7 +542,7 @@ bool ReactNativeFeatureFlagsAccessor::useStateAlignmentMechanism() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(27, "useStateAlignmentMechanism");
markFlagAsAccessed(28, "useStateAlignmentMechanism");
flagValue = currentProvider_->useStateAlignmentMechanism();
useStateAlignmentMechanism_ = flagValue;
@@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<086584dd69b004ee0f083b76d6368f84>>
* @generated SignedSource<<514f185e84b4e4253727009281717dd8>>
*/
/**
@@ -51,6 +51,7 @@ class ReactNativeFeatureFlagsAccessor {
bool fuseboxEnabledRelease();
bool initEagerTurboModulesOnNativeModulesQueueAndroid();
bool lazyAnimationCallbacks();
bool loadVectorDrawablesOnImages();
bool setAndroidLayoutDirection();
bool useImmediateExecutorInAndroidBridgeless();
bool useModernRuntimeScheduler();
@@ -69,7 +70,7 @@ class ReactNativeFeatureFlagsAccessor {
std::unique_ptr<ReactNativeFeatureFlagsProvider> currentProvider_;
bool wasOverridden_;
std::array<std::atomic<const char*>, 28> accessedFeatureFlags_;
std::array<std::atomic<const char*>, 29> accessedFeatureFlags_;
std::atomic<std::optional<bool>> commonTestFlag_;
std::atomic<std::optional<bool>> allowCollapsableChildren_;
@@ -91,6 +92,7 @@ class ReactNativeFeatureFlagsAccessor {
std::atomic<std::optional<bool>> fuseboxEnabledRelease_;
std::atomic<std::optional<bool>> initEagerTurboModulesOnNativeModulesQueueAndroid_;
std::atomic<std::optional<bool>> lazyAnimationCallbacks_;
std::atomic<std::optional<bool>> loadVectorDrawablesOnImages_;
std::atomic<std::optional<bool>> setAndroidLayoutDirection_;
std::atomic<std::optional<bool>> useImmediateExecutorInAndroidBridgeless_;
std::atomic<std::optional<bool>> useModernRuntimeScheduler_;
@@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<108ca764e4098dab0467165c974d1278>>
* @generated SignedSource<<de836863a09942eb45402d3f8900848c>>
*/
/**
@@ -107,6 +107,10 @@ class ReactNativeFeatureFlagsDefaults : public ReactNativeFeatureFlagsProvider {
return false;
}
bool loadVectorDrawablesOnImages() override {
return false;
}
bool setAndroidLayoutDirection() override {
return true;
}
@@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<f3be4f26c4efd42fd88e31985679609d>>
* @generated SignedSource<<2c5dfb357e1b7a1b27e0ff8a023fc54f>>
*/
/**
@@ -45,6 +45,7 @@ class ReactNativeFeatureFlagsProvider {
virtual bool fuseboxEnabledRelease() = 0;
virtual bool initEagerTurboModulesOnNativeModulesQueueAndroid() = 0;
virtual bool lazyAnimationCallbacks() = 0;
virtual bool loadVectorDrawablesOnImages() = 0;
virtual bool setAndroidLayoutDirection() = 0;
virtual bool useImmediateExecutorInAndroidBridgeless() = 0;
virtual bool useModernRuntimeScheduler() = 0;
@@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<aa56a814cd34c4bee4d5f22f665a718f>>
* @generated SignedSource<<b9831738df35c18c28fc5a914fa01de0>>
*/
/**
@@ -137,6 +137,11 @@ bool NativeReactNativeFeatureFlags::lazyAnimationCallbacks(
return ReactNativeFeatureFlags::lazyAnimationCallbacks();
}
bool NativeReactNativeFeatureFlags::loadVectorDrawablesOnImages(
jsi::Runtime& /*runtime*/) {
return ReactNativeFeatureFlags::loadVectorDrawablesOnImages();
}
bool NativeReactNativeFeatureFlags::setAndroidLayoutDirection(
jsi::Runtime& /*runtime*/) {
return ReactNativeFeatureFlags::setAndroidLayoutDirection();
@@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<735fa03ec34232ee74161db833eadf25>>
* @generated SignedSource<<4f72e159b344f68fc39649f929f2b6f1>>
*/
/**
@@ -75,6 +75,8 @@ class NativeReactNativeFeatureFlags
bool lazyAnimationCallbacks(jsi::Runtime& runtime);
bool loadVectorDrawablesOnImages(jsi::Runtime& runtime);
bool setAndroidLayoutDirection(jsi::Runtime& runtime);
bool useImmediateExecutorInAndroidBridgeless(jsi::Runtime& runtime);
@@ -133,6 +133,11 @@ const definitions: FeatureFlagDefinitions = {
description:
'Only enqueue Choreographer calls if there is an ongoing animation, instead of enqueueing every frame.',
},
loadVectorDrawablesOnImages: {
defaultValue: false,
description:
'Adds support for loading vector drawable assets in the Image component (only on Android)',
},
setAndroidLayoutDirection: {
defaultValue: true,
description: 'Propagate layout direction to Android views.',
@@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<07014a208bbac8efec4d01b7f09c6910>>
* @generated SignedSource<<c055f71a8ef290f08cbd8027dd68678b>>
* @flow strict-local
*/
@@ -61,6 +61,7 @@ export type ReactNativeFeatureFlags = {
fuseboxEnabledRelease: Getter<boolean>,
initEagerTurboModulesOnNativeModulesQueueAndroid: Getter<boolean>,
lazyAnimationCallbacks: Getter<boolean>,
loadVectorDrawablesOnImages: Getter<boolean>,
setAndroidLayoutDirection: Getter<boolean>,
useImmediateExecutorInAndroidBridgeless: Getter<boolean>,
useModernRuntimeScheduler: Getter<boolean>,
@@ -196,6 +197,10 @@ export const initEagerTurboModulesOnNativeModulesQueueAndroid: Getter<boolean> =
* Only enqueue Choreographer calls if there is an ongoing animation, instead of enqueueing every frame.
*/
export const lazyAnimationCallbacks: Getter<boolean> = createNativeFlagGetter('lazyAnimationCallbacks', false);
/**
* Adds support for loading vector drawable assets in the Image component (only on Android)
*/
export const loadVectorDrawablesOnImages: Getter<boolean> = createNativeFlagGetter('loadVectorDrawablesOnImages', false);
/**
* Propagate layout direction to Android views.
*/
@@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<bfbcc323e081f7946e950523d5b33176>>
* @generated SignedSource<<681628bb566e851c57e2831ac8f83f4f>>
* @flow strict-local
*/
@@ -43,6 +43,7 @@ export interface Spec extends TurboModule {
+fuseboxEnabledRelease?: () => boolean;
+initEagerTurboModulesOnNativeModulesQueueAndroid?: () => boolean;
+lazyAnimationCallbacks?: () => boolean;
+loadVectorDrawablesOnImages?: () => boolean;
+setAndroidLayoutDirection?: () => boolean;
+useImmediateExecutorInAndroidBridgeless?: () => boolean;
+useModernRuntimeScheduler?: () => boolean;