1010import android .content .Context ;
1111import android .content .res .AssetManager ;
1212import android .graphics .Typeface ;
13+ import android .graphics .fonts .Font ;
14+ import android .graphics .fonts .FontFamily ;
15+ import android .os .Build ;
1316import android .util .SparseArray ;
1417import androidx .annotation .Nullable ;
18+ import androidx .annotation .RequiresApi ;
1519import androidx .core .content .res .ResourcesCompat ;
1620import com .facebook .infer .annotation .Nullsafe ;
21+
22+ import java .io .IOException ;
23+ import java .util .ArrayList ;
1724import java .util .HashMap ;
25+ import java .util .List ;
1826import java .util .Map ;
1927
2028/**
@@ -132,6 +140,28 @@ public void setTypeface(String fontFamilyName, int style, Typeface typeface) {
132140
133141 private static Typeface createAssetTypeface (
134142 String fontFamilyName , int style , AssetManager assetManager ) {
143+ // This logic attempts to safely check if the frontend code is attempting to use
144+ // fallback fonts, and if it is, to use the fallback typeface creation logic.
145+ String [] fontFamilyNames = fontFamilyName != null ? fontFamilyName .split ("," ) : null ;
146+ if (fontFamilyNames != null ) {
147+ for (int i = 0 ; i < fontFamilyNames .length ; i ++) {
148+ fontFamilyNames [i ] = fontFamilyNames [i ].trim ();
149+ }
150+ }
151+
152+ // If there are multiple font family names:
153+ // For newer versions of Android, construct a Typeface with fallbacks
154+ // For older versions of Android, ignore all the fallbacks and just use the first font family
155+ if (fontFamilyNames != null && fontFamilyNames .length > 1 ) {
156+ if (Build .VERSION .SDK_INT >= Build .VERSION_CODES .Q ) {
157+ return createAssetTypefaceWithFallbacks (fontFamilyNames , style , assetManager );
158+ } else {
159+ fontFamilyName = fontFamilyNames [0 ];
160+ }
161+ }
162+
163+ // Lastly, after all those checks above, this is the original RN logic for
164+ // getting the typeface.
135165 String extension = EXTENSIONS [style ];
136166 for (String fileExtension : FILE_EXTENSIONS ) {
137167 String fileName =
@@ -151,6 +181,44 @@ private static Typeface createAssetTypeface(
151181 return Typeface .create (fontFamilyName , style );
152182 }
153183
184+ @ RequiresApi (api = Build .VERSION_CODES .Q )
185+ private static Typeface createAssetTypefaceWithFallbacks (
186+ String [] fontFamilyNames , int style , AssetManager assetManager ) {
187+ List <FontFamily > fontFamilies = new ArrayList <>();
188+
189+ // Iterate over the list of fontFamilyNames, constructing new FontFamily objects
190+ // for use in the CustomFallbackBuilder below.
191+ for (String fontFamilyName : fontFamilyNames ) {
192+ String extension = EXTENSIONS [style ];
193+ for (String fileExtension : FILE_EXTENSIONS ) {
194+ String fileName =
195+ new StringBuilder ()
196+ .append (FONTS_ASSET_PATH )
197+ .append (fontFamilyName )
198+ .append (extension )
199+ .append (fileExtension )
200+ .toString ();
201+ try {
202+ Font font = new Font .Builder (assetManager , fileName ).build ();
203+ FontFamily family = new FontFamily .Builder (font ).build ();
204+ fontFamilies .add (family );
205+ } catch (RuntimeException e ) {
206+ // If the typeface asset does not exist, try another extension.
207+ continue ;
208+ } catch (IOException e ) {
209+ // If the font asset does not exist, try another extension.
210+ continue ;
211+ }
212+ }
213+ }
214+
215+ Typeface .CustomFallbackBuilder fallbackBuilder = new Typeface .CustomFallbackBuilder (fontFamilies .get (0 ));
216+ for (int i = 1 ; i < fontFamilies .size (); i ++) {
217+ fallbackBuilder .addCustomFallback (fontFamilies .get (i ));
218+ }
219+ return fallbackBuilder .build ();
220+ }
221+
154222 /** Responsible for caching typefaces for each custom font family. */
155223 private static class AssetFontFamily {
156224
0 commit comments