@@ -17,6 +17,10 @@ public class FastImageSource {
1717 private static final String ANDROID_RESOURCE_SCHEME = "android.resource" ;
1818 private static final String ANDROID_CONTENT_SCHEME = "content" ;
1919 private static final String LOCAL_FILE_SCHEME = "file" ;
20+ private static final String RAW_RESOURCE_FOLDER = "raw" ;
21+ private static final String HTTP_SCHEME_PREFIX = "http" ;
22+ private static final String DATA_URI_PREFIX = "data:" ;
23+
2024 private final Headers mHeaders ;
2125 private Uri mUri ;
2226 private String mSource ;
@@ -53,19 +57,95 @@ public FastImageSource(Context context, String source, double width, double heig
5357 ImageSource imageSource = new ImageSource (context , source , width , height );
5458 mSource = imageSource .getSource ();
5559 mHeaders = headers == null ? Headers .DEFAULT : headers ;
56- mUri = imageSource .getUri ();
60+ mUri = resolveResourceUri (context , source , imageSource .getUri ());
61+
62+ boolean isUriEmpty = mUri == null || TextUtils .isEmpty (mUri .toString ());
63+ boolean isResourceMissing = isResource () && isUriEmpty ;
5764
58- if (isResource () && TextUtils . isEmpty ( mUri . toString ()) ) {
65+ if (isResourceMissing ) {
5966 throw new Resources .NotFoundException ("Local Resource Not Found. Resource: '" + getSource () + "'." );
6067 }
6168
62- if (isLocalResourceUri (mUri )) {
63- // Convert res:/ scheme to android.resource:// so
64- // glide can understand the uri.
65- mUri = Uri .parse (mUri .toString ().replace ("res:/" , ANDROID_RESOURCE_SCHEME + "://" + context .getPackageName () + "/" ));
69+ boolean shouldConvertLocalResourceScheme = mUri != null && isLocalResourceUri (mUri );
70+
71+ if (shouldConvertLocalResourceScheme ) {
72+ // Convert res:/ scheme to android.resource:// so glide can understand the uri.
73+ String convertedUri = mUri .toString ().replace ("res:/" , ANDROID_RESOURCE_SCHEME + "://" + context .getPackageName () + "/" );
74+ mUri = Uri .parse (convertedUri );
75+ }
76+ }
77+
78+ /**
79+ * Attempts to find a resource URI for the given source.
80+ * First uses the URI from ImageSource (checks drawable folder),
81+ * then falls back to raw folder for assets like SVGs in release builds.
82+ *
83+ * @param context Application context
84+ * @param source Original source string
85+ * @param initialUri URI resolved by ImageSource
86+ * @return Resolved URI or null if not found
87+ */
88+ @ Nullable
89+ private Uri resolveResourceUri (Context context , String source , Uri initialUri ) {
90+ boolean isInitialUriValid = initialUri != null && !TextUtils .isEmpty (initialUri .toString ());
91+
92+ if (isInitialUriValid ) {
93+ return initialUri ;
94+ }
95+
96+ boolean isSourceNull = source == null ;
97+ boolean isRemoteUrl = source != null && source .startsWith (HTTP_SCHEME_PREFIX );
98+ boolean isDataUri = source != null && source .startsWith (DATA_URI_PREFIX );
99+ boolean shouldSkipRawLookup = isSourceNull || isRemoteUrl || isDataUri ;
100+
101+ if (shouldSkipRawLookup ) {
102+ return initialUri ;
103+ }
104+
105+ return tryResolveFromRawFolder (context , source );
106+ }
107+
108+ /**
109+ * Attempts to find a resource in the res/raw folder.
110+ * This is needed because SVG and some other assets are bundled to res/raw/
111+ * in Android release builds, but ImageSource only checks res/drawable/.
112+ *
113+ * @param context Application context
114+ * @param source Original source string
115+ * @return URI pointing to raw resource, or null if not found
116+ */
117+ @ Nullable
118+ private Uri tryResolveFromRawFolder (Context context , String source ) {
119+ String rawResourceName = toAndroidResourceName (source );
120+ int rawResId = context .getResources ().getIdentifier (
121+ rawResourceName ,
122+ RAW_RESOURCE_FOLDER ,
123+ context .getPackageName ()
124+ );
125+
126+ boolean resourceNotFound = rawResId == 0 ;
127+
128+ if (resourceNotFound ) {
129+ return null ;
66130 }
131+
132+ String rawResourceUri = ANDROID_RESOURCE_SCHEME + "://" +
133+ context .getPackageName () + "/" + RAW_RESOURCE_FOLDER + "/" + rawResourceName ;
134+
135+ return Uri .parse (rawResourceUri );
67136 }
68137
138+ /**
139+ * Converts a source name to Android resource name format.
140+ * Android resources must be lowercase and cannot contain hyphens.
141+ * Hyphens are removed to match React Native's bundling behavior.
142+ *
143+ * @param source Original source name
144+ * @return Android-compatible resource name
145+ */
146+ private String toAndroidResourceName (String source ) {
147+ return source .toLowerCase ().replace ("-" , "" );
148+ }
69149
70150 public boolean isBase64Resource () {
71151 return mUri != null && FastImageSource .isBase64Uri (mUri );
0 commit comments