Skip to content

Commit 25d189b

Browse files
dsn5ftimhappi
authored andcommitted
[TextAppearance] Update font loading to load synchronously for system font families
Also avoids creating the Typeface twice because previously shouldLoadFontSynchronously() and getFont() would both create a Typeface in some cases PiperOrigin-RevId: 625813788
1 parent 51f7d5d commit 25d189b

File tree

1 file changed

+81
-11
lines changed

1 file changed

+81
-11
lines changed

lib/java/com/google/android/material/resources/TextAppearance.java

Lines changed: 81 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
import com.google.android.material.R;
2020

21+
import android.annotation.SuppressLint;
2122
import android.content.Context;
2223
import android.content.res.ColorStateList;
2324
import android.content.res.Resources;
@@ -27,7 +28,9 @@
2728
import android.os.Build.VERSION;
2829
import android.os.Build.VERSION_CODES;
2930
import android.text.TextPaint;
31+
import android.util.AttributeSet;
3032
import android.util.Log;
33+
import android.util.Xml;
3134
import androidx.annotation.FontRes;
3235
import androidx.annotation.NonNull;
3336
import androidx.annotation.Nullable;
@@ -38,6 +41,7 @@
3841
import androidx.core.content.res.ResourcesCompat;
3942
import androidx.core.content.res.ResourcesCompat.FontCallback;
4043
import androidx.core.provider.FontsContractCompat.FontRequestCallback;
44+
import org.xmlpull.v1.XmlPullParser;
4145

4246
/**
4347
* Utility class that contains the data from parsing a TextAppearance style resource.
@@ -75,6 +79,7 @@ public class TextAppearance {
7579
@FontRes private final int fontFamilyResourceId;
7680

7781
private boolean fontResolved = false;
82+
private boolean systemFontLoadAttempted = false;
7883
private Typeface font;
7984

8085
/** Parses the given TextAppearance style resource. */
@@ -169,9 +174,7 @@ public Typeface getFont(@NonNull Context context) {
169174
*/
170175
public void getFontAsync(
171176
@NonNull Context context, @NonNull final TextAppearanceFontCallback callback) {
172-
if (shouldLoadFontSynchronously(context)) {
173-
getFont(context);
174-
} else {
177+
if (!maybeLoadFontSynchronously(context)) {
175178
// No-op if font already resolved.
176179
createFallbackFont();
177180
}
@@ -325,8 +328,8 @@ public void updateMeasureState(
325328
@NonNull Context context,
326329
@NonNull TextPaint textPaint,
327330
@NonNull TextAppearanceFontCallback callback) {
328-
if (shouldLoadFontSynchronously(context)) {
329-
updateTextPaintMeasureState(context, textPaint, getFont(context));
331+
if (maybeLoadFontSynchronously(context) && fontResolved && font != null) {
332+
updateTextPaintMeasureState(context, textPaint, font);
330333
} else {
331334
getFontAsync(context, textPaint, callback);
332335
}
@@ -375,14 +378,81 @@ public void setTextSize(float textSize) {
375378
this.textSize = textSize;
376379
}
377380

378-
private boolean shouldLoadFontSynchronously(Context context) {
381+
private boolean maybeLoadFontSynchronously(Context context) {
379382
if (TextAppearanceConfig.shouldLoadFontSynchronously()) {
383+
getFont(context);
384+
return true;
385+
}
386+
if (fontResolved) {
387+
return true;
388+
}
389+
if (fontFamilyResourceId == 0) {
390+
return false;
391+
}
392+
Typeface cachedFont = ResourcesCompat.getCachedFont(context, fontFamilyResourceId);
393+
if (cachedFont != null) {
394+
font = cachedFont;
395+
fontResolved = true;
396+
return true;
397+
}
398+
Typeface systemFont = getSystemTypeface(context);
399+
if (systemFont != null) {
400+
font = systemFont;
401+
fontResolved = true;
380402
return true;
381403
}
382-
Typeface typeface =
383-
(fontFamilyResourceId != 0)
384-
? ResourcesCompat.getCachedFont(context, fontFamilyResourceId)
385-
: null;
386-
return (typeface != null);
404+
return false;
405+
}
406+
407+
@Nullable
408+
private Typeface getSystemTypeface(Context context) {
409+
if (systemFontLoadAttempted) {
410+
// Only attempt to load the system font once.
411+
return null;
412+
}
413+
systemFontLoadAttempted = true;
414+
415+
String systemFontFamily = readFontProviderSystemFontFamily(context, fontFamilyResourceId);
416+
if (systemFontFamily == null) {
417+
return null;
418+
}
419+
420+
Typeface regularSystemTypeface = Typeface.create(systemFontFamily, Typeface.NORMAL);
421+
if (regularSystemTypeface == Typeface.DEFAULT) {
422+
// If Typeface#create returned Typeface.DEFAULT then systemFontFamily is not present on the
423+
// device as a system font, so we will have to load the font asynchronously.
424+
return null;
425+
}
426+
427+
return Typeface.create(regularSystemTypeface, textStyle);
428+
}
429+
430+
@SuppressLint("ResourceType")
431+
@Nullable
432+
private static String readFontProviderSystemFontFamily(
433+
Context context, @FontRes int fontFamilyResourceId) {
434+
Resources resources = context.getResources();
435+
if (fontFamilyResourceId == 0
436+
|| !resources.getResourceTypeName(fontFamilyResourceId).equals("font")) {
437+
return null;
438+
}
439+
440+
try {
441+
XmlPullParser xpp = resources.getXml(fontFamilyResourceId);
442+
while (xpp.getEventType() != XmlPullParser.END_DOCUMENT) {
443+
if (xpp.getEventType() == XmlPullParser.START_TAG && xpp.getName().equals("font-family")) {
444+
AttributeSet attrs = Xml.asAttributeSet(xpp);
445+
TypedArray a = resources.obtainAttributes(attrs, androidx.core.R.styleable.FontFamily);
446+
String systemFontFamily =
447+
a.getString(androidx.core.R.styleable.FontFamily_fontProviderSystemFontFamily);
448+
a.recycle();
449+
return systemFontFamily;
450+
}
451+
xpp.next();
452+
}
453+
} catch (Throwable t) {
454+
// Fail silently if we can't find fontProviderSystemFontFamily for any reason.
455+
}
456+
return null;
387457
}
388458
}

0 commit comments

Comments
 (0)