From 6178d55188266235d2f268fda6b2414df658aad1 Mon Sep 17 00:00:00 2001 From: Steven Petryk Date: Mon, 14 Nov 2022 10:56:42 -0800 Subject: [PATCH 1/8] iOS: Allow passing a comma-separated list to `fontFamily` --- Libraries/Text/RCTTextAttributes.m | 52 +++++++++++++++++++++++++----- 1 file changed, 44 insertions(+), 8 deletions(-) diff --git a/Libraries/Text/RCTTextAttributes.m b/Libraries/Text/RCTTextAttributes.m index 01131cfb20e2fb..8042075f4b66ec 100644 --- a/Libraries/Text/RCTTextAttributes.m +++ b/Libraries/Text/RCTTextAttributes.m @@ -192,14 +192,50 @@ - (NSParagraphStyle *)effectiveParagraphStyle - (UIFont *)effectiveFont { - // FIXME: RCTFont has thread-safety issues and must be rewritten. - return [RCTFont updateFont:nil - withFamily:_fontFamily - size:@(isnan(_fontSize) ? 0 : _fontSize) - weight:_fontWeight - style:_fontStyle - variant:_fontVariant - scaleMultiplier:self.effectiveFontSizeMultiplier]; + NSArray *rawFontFamilies = [_fontFamily componentsSeparatedByString:@","]; + + if (rawFontFamilies.count == 0) { + return [RCTFont updateFont:nil + withFamily:_fontFamily + size:@(isnan(_fontSize) ? 0 : _fontSize) + weight:_fontWeight + style:_fontStyle + variant:_fontVariant + scaleMultiplier:self.effectiveFontSizeMultiplier]; + } + + NSMutableArray *fonts = [NSMutableArray new]; + for (NSString *rawFontFamily in rawFontFamilies) { + NSString *fontFamily = [rawFontFamily stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]; + if (fontFamily.length == 0) { + continue; + } + + UIFont *font = [RCTFont updateFont:nil + withFamily:fontFamily + size:@(isnan(_fontSize) ? 0 : _fontSize) + weight:_fontWeight + style:_fontStyle + variant:_fontVariant + scaleMultiplier:self.effectiveFontSizeMultiplier]; + + if (font) { + [fonts addObject:font]; + } + } + + UIFont *primaryFont = fonts[0]; + + NSMutableArray *fontDescriptors = [NSMutableArray new]; + for (NSUInteger i = 1; i < fonts.count; i++) { + UIFont *font = fonts[i]; + [fontDescriptors addObject:font.fontDescriptor]; + } + + UIFontDescriptor *fontDescriptor = [primaryFont.fontDescriptor fontDescriptorByAddingAttributes: + @{UIFontDescriptorCascadeListAttribute: fontDescriptors}]; + + return [UIFont fontWithDescriptor:fontDescriptor size:primaryFont.pointSize]; } - (CGFloat)effectiveFontSizeMultiplier From e69c6d997444475fdbb4cdd4530375a0dd400d49 Mon Sep 17 00:00:00 2001 From: Steven Petryk Date: Mon, 14 Nov 2022 11:11:33 -0800 Subject: [PATCH 2/8] Try setting an obvious fallback on Android --- .../react/views/text/ReactFontManager.java | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactFontManager.java b/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactFontManager.java index d81490fd82cb25..52b50e56a72320 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactFontManager.java +++ b/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactFontManager.java @@ -10,10 +10,14 @@ import android.content.Context; import android.content.res.AssetManager; import android.graphics.Typeface; +import android.graphics.fonts.Font; +import android.graphics.fonts.FontFamily; import android.util.SparseArray; import androidx.annotation.Nullable; import androidx.core.content.res.ResourcesCompat; import com.facebook.infer.annotation.Nullsafe; + +import java.io.IOException; import java.util.HashMap; import java.util.Map; @@ -142,10 +146,25 @@ private static Typeface createAssetTypeface( .append(fileExtension) .toString(); try { + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.Q) { + Font font = new Font.Builder(assetManager, fileName).build(); + FontFamily family = new FontFamily.Builder(font).build(); + + Typeface.CustomFallbackBuilder fallbackBuilder = new Typeface.CustomFallbackBuilder(family); + // TODO - just for testing + fallbackBuilder.setSystemFallback("serif"); + + return fallbackBuilder.build(); + } + + // Earlier versions of Android are unable to have fallbacks specified return Typeface.createFromAsset(assetManager, fileName); } catch (RuntimeException e) { // If the typeface asset does not exist, try another extension. continue; + } catch (IOException e) { + // If the font asset does not exist, try another extension. + continue; } } return Typeface.create(fontFamilyName, style); From 6ed4cf6136edba8ce7a2b4c4eb053392a9463dbd Mon Sep 17 00:00:00 2001 From: Steven Petryk Date: Mon, 14 Nov 2022 15:10:36 -0800 Subject: [PATCH 3/8] Remove code around publishing RN --- ReactAndroid/build.gradle | 51 --------------------------------------- 1 file changed, 51 deletions(-) diff --git a/ReactAndroid/build.gradle b/ReactAndroid/build.gradle index 6da5abd7711f3c..b2a2219387df1c 100644 --- a/ReactAndroid/build.gradle +++ b/ReactAndroid/build.gradle @@ -449,55 +449,4 @@ afterEvaluate { // before configureNdkBuildDebug could be executed. configureNdkBuildDebug.dependsOn(preBuild) configureNdkBuildRelease.dependsOn(preBuild) - - publishing { - publications { - release(MavenPublication) { - // Applies the component for the release build variant. - from components.release - - // Add additional sourcesJar to artifacts - artifact(androidSourcesJar) - - // You can then customize attributes of the publication as shown below. - artifactId = POM_ARTIFACT_ID - groupId = GROUP - version = VERSION_NAME - - pom { - name = POM_NAME - description = "A framework for building native apps with React" - url = "https://github.com/facebook/react-native" - - developers { - developer { - id = "facebook" - name = "Facebook" - } - } - - licenses { - license { - name = "MIT License" - url = "https://github.com/facebook/react-native/blob/HEAD/LICENSE" - distribution = "repo" - } - } - - scm { - url = "https://github.com/facebook/react-native.git" - connection = "scm:git:https://github.com/facebook/react-native.git" - developerConnection = "scm:git:git@github.com:facebook/react-native.git" - } - } - } - } - - repositories { - maven { - name = "discord" - url = uri("gcs://discord-maven") - } - } - } } From d05512441a136d57f2c04ab0742741f38aaf3b13 Mon Sep 17 00:00:00 2001 From: Steven Petryk Date: Mon, 14 Nov 2022 15:32:28 -0800 Subject: [PATCH 4/8] Update broken reducedMotionEnabled stuff --- .../modules/accessibilityinfo/AccessibilityInfoModule.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/ReactAndroid/src/main/java/com/facebook/react/modules/accessibilityinfo/AccessibilityInfoModule.java b/ReactAndroid/src/main/java/com/facebook/react/modules/accessibilityinfo/AccessibilityInfoModule.java index 02800eeda895b8..0ea17b3b52cda6 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/modules/accessibilityinfo/AccessibilityInfoModule.java +++ b/ReactAndroid/src/main/java/com/facebook/react/modules/accessibilityinfo/AccessibilityInfoModule.java @@ -108,9 +108,10 @@ public String getName() { @TargetApi(Build.VERSION_CODES.LOLLIPOP) private boolean getIsReduceMotionEnabledValue() { - float defaultAnimationScale = Float.parseFloat(Settings.Global.TRANSITION_ANIMATION_SCALE); - float animationScale = Settings.Global.getFloat(mContentResolver, defaultAnimationScale); - return animationScale == 0f; + String value = + Settings.Global.getString(mContentResolver, Settings.Global.TRANSITION_ANIMATION_SCALE); + + return value != null && value.equals("0.0"); } @Override From 4b856dd12ceb09d5dc18e01e1b0114776a672735 Mon Sep 17 00:00:00 2001 From: Steven Petryk Date: Mon, 14 Nov 2022 16:17:06 -0800 Subject: [PATCH 5/8] Allow comma-separated fontFamily lists --- .../react/views/text/ReactFontManager.java | 75 +++++++++++++++---- 1 file changed, 60 insertions(+), 15 deletions(-) diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactFontManager.java b/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactFontManager.java index 52b50e56a72320..efe2a019d6da70 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactFontManager.java +++ b/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactFontManager.java @@ -12,13 +12,17 @@ import android.graphics.Typeface; import android.graphics.fonts.Font; import android.graphics.fonts.FontFamily; +import android.os.Build; import android.util.SparseArray; import androidx.annotation.Nullable; +import androidx.annotation.RequiresApi; import androidx.core.content.res.ResourcesCompat; import com.facebook.infer.annotation.Nullsafe; import java.io.IOException; +import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.Map; /** @@ -136,6 +140,20 @@ public void setTypeface(String fontFamilyName, int style, Typeface typeface) { private static Typeface createAssetTypeface( String fontFamilyName, int style, AssetManager assetManager) { + String[] fontFamilyNames = fontFamilyName != null ? fontFamilyName.split(",") : null; + if (fontFamilyNames != null) { + for (int i = 0; i < fontFamilyNames.length; i++) { + fontFamilyNames[i] = fontFamilyNames[i].trim(); + } + } + if (fontFamilyNames != null && fontFamilyNames.length > 1) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + return createAssetTypefaceWithFallbacks(fontFamilyNames, style, assetManager); + } else { + fontFamilyName = fontFamilyNames[0]; + } + } + String extension = EXTENSIONS[style]; for (String fileExtension : FILE_EXTENSIONS) { String fileName = @@ -146,30 +164,57 @@ private static Typeface createAssetTypeface( .append(fileExtension) .toString(); try { - if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.Q) { - Font font = new Font.Builder(assetManager, fileName).build(); - FontFamily family = new FontFamily.Builder(font).build(); - - Typeface.CustomFallbackBuilder fallbackBuilder = new Typeface.CustomFallbackBuilder(family); - // TODO - just for testing - fallbackBuilder.setSystemFallback("serif"); - - return fallbackBuilder.build(); - } - - // Earlier versions of Android are unable to have fallbacks specified return Typeface.createFromAsset(assetManager, fileName); } catch (RuntimeException e) { // If the typeface asset does not exist, try another extension. continue; - } catch (IOException e) { - // If the font asset does not exist, try another extension. - continue; } } return Typeface.create(fontFamilyName, style); } + @RequiresApi(api = Build.VERSION_CODES.Q) + private static Typeface createAssetTypefaceWithFallbacks( + String[] fontFamilyNames, int style, AssetManager assetManager) { + // Create a new list of fontFamilies + List fontFamilies = new ArrayList<>(); + + // For each font family name, create a new fontFamily and add it to the list + for (String fontFamilyName : fontFamilyNames) { + String extension = EXTENSIONS[style]; + for (String fileExtension : FILE_EXTENSIONS) { + String fileName = + new StringBuilder() + .append(FONTS_ASSET_PATH) + .append(fontFamilyName) + .append(extension) + .append(fileExtension) + .toString(); + try { + Font font = new Font.Builder(assetManager, fileName).build(); + FontFamily family = new FontFamily.Builder(font).build(); + fontFamilies.add(family); + } catch (RuntimeException e) { + // If the typeface asset does not exist, try another extension. + continue; + } catch (IOException e) { + // If the font asset does not exist, try another extension. + continue; + } + } + } + + // Using the first fontFamily, construct a new CustomFallbackBuilder typeface + Typeface.CustomFallbackBuilder fallbackBuilder = new Typeface.CustomFallbackBuilder(fontFamilies.get(0)); + // For each fontFamily, add it as a fallback + for (int i = 1; i < fontFamilies.size(); i++) { + fallbackBuilder.addCustomFallback(fontFamilies.get(i)); + } + + // Return the built typeface + return fallbackBuilder.build(); + } + /** Responsible for caching typefaces for each custom font family. */ private static class AssetFontFamily { From 3c8764d843032769a8813ca6389ac697e4ebba47 Mon Sep 17 00:00:00 2001 From: Steven Petryk Date: Tue, 15 Nov 2022 10:13:00 -0800 Subject: [PATCH 6/8] Preserve original RN logic for safety purposes --- Libraries/Text/RCTTextAttributes.m | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Libraries/Text/RCTTextAttributes.m b/Libraries/Text/RCTTextAttributes.m index 8042075f4b66ec..2bf3816b852c19 100644 --- a/Libraries/Text/RCTTextAttributes.m +++ b/Libraries/Text/RCTTextAttributes.m @@ -194,7 +194,9 @@ - (UIFont *)effectiveFont { NSArray *rawFontFamilies = [_fontFamily componentsSeparatedByString:@","]; - if (rawFontFamilies.count == 0) { + // If _fontFamily is nil or has a single fontFamily, then use the original RN logic. + if (rawFontFamilies.count <= 1) { + // FIXME: RCTFont has thread-safety issues and must be rewritten. return [RCTFont updateFont:nil withFamily:_fontFamily size:@(isnan(_fontSize) ? 0 : _fontSize) From 4a3920cebaccf9f00a0051624683f085b2f27284 Mon Sep 17 00:00:00 2001 From: Steven Petryk Date: Tue, 15 Nov 2022 10:32:36 -0800 Subject: [PATCH 7/8] Update Java comments --- .../react/views/text/ReactFontManager.java | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactFontManager.java b/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactFontManager.java index efe2a019d6da70..35d8c41fda8207 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactFontManager.java +++ b/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactFontManager.java @@ -140,12 +140,18 @@ public void setTypeface(String fontFamilyName, int style, Typeface typeface) { private static Typeface createAssetTypeface( String fontFamilyName, int style, AssetManager assetManager) { + // This logic attempts to safely check if the frontend code is attempting to use + // fallback fonts, and if it is, to use the fallback typeface creation logic. String[] fontFamilyNames = fontFamilyName != null ? fontFamilyName.split(",") : null; if (fontFamilyNames != null) { for (int i = 0; i < fontFamilyNames.length; i++) { fontFamilyNames[i] = fontFamilyNames[i].trim(); } } + + // If there are multiple font family names: + // For newer versions of Android, construct a Typeface with fallbacks + // For older versions of Android, ignore all the fallbacks and just use the first font family if (fontFamilyNames != null && fontFamilyNames.length > 1) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { return createAssetTypefaceWithFallbacks(fontFamilyNames, style, assetManager); @@ -154,6 +160,8 @@ private static Typeface createAssetTypeface( } } + // Lastly, after all those checks above, this is the original RN logic for + // getting the typeface. String extension = EXTENSIONS[style]; for (String fileExtension : FILE_EXTENSIONS) { String fileName = @@ -176,10 +184,10 @@ private static Typeface createAssetTypeface( @RequiresApi(api = Build.VERSION_CODES.Q) private static Typeface createAssetTypefaceWithFallbacks( String[] fontFamilyNames, int style, AssetManager assetManager) { - // Create a new list of fontFamilies List fontFamilies = new ArrayList<>(); - // For each font family name, create a new fontFamily and add it to the list + // Iterate over the list of fontFamilyNames, constructing new FontFamily objects + // for use in the CustomFallbackBuilder below. for (String fontFamilyName : fontFamilyNames) { String extension = EXTENSIONS[style]; for (String fileExtension : FILE_EXTENSIONS) { @@ -204,14 +212,10 @@ private static Typeface createAssetTypefaceWithFallbacks( } } - // Using the first fontFamily, construct a new CustomFallbackBuilder typeface Typeface.CustomFallbackBuilder fallbackBuilder = new Typeface.CustomFallbackBuilder(fontFamilies.get(0)); - // For each fontFamily, add it as a fallback for (int i = 1; i < fontFamilies.size(); i++) { fallbackBuilder.addCustomFallback(fontFamilies.get(i)); } - - // Return the built typeface return fallbackBuilder.build(); } From bfd630b9180830892bad9b5d0d35ef60489503e1 Mon Sep 17 00:00:00 2001 From: Steven Petryk Date: Tue, 15 Nov 2022 11:44:42 -0800 Subject: [PATCH 8/8] Revert gradle changes --- ReactAndroid/build.gradle | 51 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/ReactAndroid/build.gradle b/ReactAndroid/build.gradle index b2a2219387df1c..6da5abd7711f3c 100644 --- a/ReactAndroid/build.gradle +++ b/ReactAndroid/build.gradle @@ -449,4 +449,55 @@ afterEvaluate { // before configureNdkBuildDebug could be executed. configureNdkBuildDebug.dependsOn(preBuild) configureNdkBuildRelease.dependsOn(preBuild) + + publishing { + publications { + release(MavenPublication) { + // Applies the component for the release build variant. + from components.release + + // Add additional sourcesJar to artifacts + artifact(androidSourcesJar) + + // You can then customize attributes of the publication as shown below. + artifactId = POM_ARTIFACT_ID + groupId = GROUP + version = VERSION_NAME + + pom { + name = POM_NAME + description = "A framework for building native apps with React" + url = "https://github.com/facebook/react-native" + + developers { + developer { + id = "facebook" + name = "Facebook" + } + } + + licenses { + license { + name = "MIT License" + url = "https://github.com/facebook/react-native/blob/HEAD/LICENSE" + distribution = "repo" + } + } + + scm { + url = "https://github.com/facebook/react-native.git" + connection = "scm:git:https://github.com/facebook/react-native.git" + developerConnection = "scm:git:git@github.com:facebook/react-native.git" + } + } + } + } + + repositories { + maven { + name = "discord" + url = uri("gcs://discord-maven") + } + } + } }