Description
#7905 previous topic 0.75.x
The Solution for Android 0.76.1
react-native-navigation\lib\android\app\src\main\java\com\reactnativenavigation\utils\ReactTypefaceUtils.java
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
// This file was copied over from react-native
// to provide backwards compatibility < rn 0.62
//
// TODO: Remove me and use com.facebook.react.views.text.ReactTypefaceUtils
// once we drop support for rn < 0.62
package com.reactnativenavigation.utils;
import android.content.res.AssetManager;
import android.graphics.Typeface;
import android.text.TextUtils;
import androidx.annotation.Nullable;
import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.views.text.ReactFontManager;
import com.facebook.react.common.ReactConstants;
import java.util.ArrayList;
import java.util.List;
public class ReactTypefaceUtils {
public static final int UNSET = -1;
public static int parseFontWeight(@Nullable String fontWeightString) {
int fontWeightNumeric =
fontWeightString != null ? parseNumericFontWeight(fontWeightString) : UNSET;
int fontWeight = fontWeightNumeric != UNSET ? fontWeightNumeric : Typeface.NORMAL;
if (fontWeight == 700 || "bold".equals(fontWeightString)) fontWeight = Typeface.BOLD;
else if (fontWeight == 400 || "normal".equals(fontWeightString)) fontWeight = Typeface.NORMAL;
return fontWeight;
}
public static int parseFontStyle(@Nullable String fontStyleString) {
int fontStyle = UNSET;
if ("italic".equals(fontStyleString)) {
fontStyle = Typeface.ITALIC;
} else if ("normal".equals(fontStyleString)) {
fontStyle = Typeface.NORMAL;
}
return fontStyle;
}
public static @Nullable String parseFontVariant(@Nullable ReadableArray fontVariantArray) {
if (fontVariantArray == null || fontVariantArray.size() == 0) {
return null;
}
List<String> features = new ArrayList<>();
for (int i = 0; i < fontVariantArray.size(); i++) {
// see https://docs.microsoft.com/en-us/typography/opentype/spec/featurelist
String fontVariant = fontVariantArray.getString(i);
if (fontVariant != null) {
switch (fontVariant) {
case "small-caps":
features.add("'smcp'");
break;
case "oldstyle-nums":
features.add("'onum'");
break;
case "lining-nums":
features.add("'lnum'");
break;
case "tabular-nums":
features.add("'tnum'");
break;
case "proportional-nums":
features.add("'pnum'");
break;
}
}
}
return TextUtils.join(", ", features);
}
public static Typeface applyStyles(
@Nullable Typeface typeface,
int style,
int weight,
@Nullable String family,
AssetManager assetManager) {
int oldStyle;
if (typeface == null) {
oldStyle = 0;
} else {
oldStyle = typeface.getStyle();
}
int want = 0;
if ((weight == Typeface.BOLD)
|| ((oldStyle & Typeface.BOLD) != 0 && weight == ReactConstants.UNSET)) {
want |= Typeface.BOLD;
}
if ((style == Typeface.ITALIC)
|| ((oldStyle & Typeface.ITALIC) != 0 && style == ReactConstants.UNSET)) {
want |= Typeface.ITALIC;
}
if (family != null) {
typeface = ReactFontManager.getInstance().getTypeface(family, want, weight, assetManager);
} else if (typeface != null) {
// TODO(t9055065): Fix custom fonts getting applied to text children with different style
typeface = Typeface.create(typeface, want);
}
if (typeface != null) {
return typeface;
} else {
return Typeface.defaultFromStyle(want);
}
}
/**
* Return -1 if the input string is not a valid numeric fontWeight (100, 200, ..., 900), otherwise
* return the weight.
*/
private static int parseNumericFontWeight(String fontWeightString) {
// This should be much faster than using regex to verify input and Integer.parseInt
return fontWeightString.length() == 3
&& fontWeightString.endsWith("00")
&& fontWeightString.charAt(0) <= '9'
&& fontWeightString.charAt(0) >= '1'
? 100 * (fontWeightString.charAt(0) - '0')
: UNSET;
}
}
react-native-navigation\lib\android\app\src\main\java\com\reactnativenavigation\utils\ReactViewGroup.kt
package com.reactnativenavigation.utils
import com.facebook.react.views.view.ReactViewBackgroundDrawable
import com.facebook.react.views.view.ReactViewGroup
val ReactViewGroup.borderRadius: Float
get() = 0f
react-native-navigation\lib\android\app\src\main\java\com\reactnativenavigation\viewcontrollers\viewcontroller\LayoutDirectionApplier.kt
package com.reactnativenavigation.viewcontrollers.viewcontroller
import com.facebook.react.ReactInstanceManager
import com.facebook.react.modules.i18nmanager.I18nUtil
import com.reactnativenavigation.options.Options
import com.facebook.react.bridge.ReactContext
class LayoutDirectionApplier {
fun apply(root: ViewController<*>, options: Options, instanceManager: ReactInstanceManager) {
// currentReactContext'in null olup olmadığını kontrol ediyoruz
val reactContext = instanceManager.currentReactContext as? ReactContext
if (options.layout.direction.hasValue() && reactContext != null) {
root.activity.window.decorView.layoutDirection = options.layout.direction.get()
I18nUtil.getInstance().allowRTL(reactContext, options.layout.direction.isRtl)
I18nUtil.getInstance().forceRTL(reactContext, options.layout.direction.isRtl)
}
}
}
#7930
Hermes Enabled: libhermes_executor.so
Hermes Disabled: libjscexecutor.so
troubleshooting your mistakes
react-native-navigation\lib\android\app\src\main\java\com\reactnativenavigation\NavigationApplication.java
package com.reactnativenavigation;
import android.app.Application;
import com.facebook.react.ReactApplication;
import com.facebook.react.ReactNativeHost;
import com.facebook.soloader.SoLoader;
import com.facebook.react.soloader.OpenSourceMergedSoMapping;
import com.reactnativenavigation.react.ReactGateway;
import com.reactnativenavigation.viewcontrollers.externalcomponent.ExternalComponentCreator;
import java.util.HashMap;
import java.util.Map;
import androidx.annotation.NonNull;
public abstract class NavigationApplication extends Application implements ReactApplication {
private ReactGateway reactGateway;
public static NavigationApplication instance;
final Map<String, ExternalComponentCreator> externalComponents = new HashMap<>();
@Override
public void onCreate() {
super.onCreate();
instance = this;
try {
SoLoader.init(this, OpenSourceMergedSoMapping.INSTANCE);
} catch (Exception e) {
e.printStackTrace();
}
reactGateway = createReactGateway();
}
/**
* Subclasses of NavigationApplication may override this method to create the singleton instance
* of {@link ReactGateway}. For example, subclasses may wish to provide a custom {@link ReactNativeHost}
* with the ReactGateway. This method will be called exactly once, in the application's {@link #onCreate()} method.
*
* Custom {@link ReactNativeHost}s must be sure to include {@link com.reactnativenavigation.react.NavigationPackage}
*
* @return a singleton {@link ReactGateway}
*/
protected ReactGateway createReactGateway() {
return new ReactGateway(getReactNativeHost());
}
public ReactGateway getReactGateway() {
return reactGateway;
}
/**
* Generally no need to override this; override for custom permission handling.
*/
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
}
/**
* Register a native View which can be displayed using the given {@code name}
* @param name Unique name used to register the native view
* @param creator Used to create the view at runtime
*/
@SuppressWarnings("unused")
public void registerExternalComponent(String name, ExternalComponentCreator creator) {
if (externalComponents.containsKey(name)) {
throw new RuntimeException("A component has already been registered with this name: " + name);
}
externalComponents.put(name, creator);
}
public final Map<String, ExternalComponentCreator> getExternalComponents() {
return externalComponents;
}
}
react-native-navigation\lib\android\app\src\reactNative71\java\com\reactnativenavigation\react\modal\ModalContentLayout.kt
package com.reactnativenavigation.react.modal
import android.content.Context
import android.view.MotionEvent
import android.view.View
import com.facebook.react.bridge.*
import com.facebook.react.uimanager.*
import com.facebook.react.uimanager.events.EventDispatcher
import com.facebook.react.views.view.ReactViewGroup
class ModalContentLayout(context: Context?) : ReactViewGroup(context), RootView {
private var hasAdjustedSize = false
private var viewWidth = 0
private var viewHeight = 0
private val mJSTouchDispatcher = JSTouchDispatcher(this)
override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
super.onSizeChanged(w, h, oldw, oldh)
viewWidth = w
viewHeight = h
this.updateFirstChildView()
}
private fun updateFirstChildView() {
if (this.childCount > 0) {
hasAdjustedSize = false
val viewTag = getChildAt(0).id
val reactContext: ReactContext = this.getReactContext()
reactContext.runOnNativeModulesQueueThread(object : GuardedRunnable(reactContext) {
override fun runGuarded() {
val uiManager = this@ModalContentLayout.getReactContext().getNativeModule(
UIManagerModule::class.java
) as UIManagerModule
uiManager.updateNodeSize(
viewTag,
this@ModalContentLayout.viewWidth,
this@ModalContentLayout.viewHeight
)
}
})
} else {
hasAdjustedSize = true
}
}
override fun addView(child: View?, index: Int, params: LayoutParams?) {
super.addView(child, index, params)
if (hasAdjustedSize) {
updateFirstChildView()
}
}
override fun onChildStartedNativeGesture(child: View, androidEvent: MotionEvent?) {
androidEvent?.let {
mJSTouchDispatcher.onChildStartedNativeGesture(it, this.getEventDispatcher()!!)
}
}
override fun onChildStartedNativeGesture(androidEvent: MotionEvent?) {
androidEvent?.let {
mJSTouchDispatcher.onChildStartedNativeGesture(it, this.getEventDispatcher()!!)
}
}
override fun onChildEndedNativeGesture(child: View, androidEvent: MotionEvent?) {
androidEvent?.let {
mJSTouchDispatcher.onChildEndedNativeGesture(it, this.getEventDispatcher()!!)
}
}
override fun requestDisallowInterceptTouchEvent(disallowIntercept: Boolean) {}
private fun getEventDispatcher(): EventDispatcher? {
val reactContext: ReactContext = this.getReactContext()
return reactContext.getNativeModule(UIManagerModule::class.java)?.eventDispatcher
}
override fun handleException(t: Throwable?) {
getReactContext().handleException(RuntimeException(t))
}
private fun getReactContext(): ReactContext {
return this.context as ReactContext
}
override fun onInterceptTouchEvent(event: MotionEvent?): Boolean {
event?.let {
mJSTouchDispatcher.handleTouchEvent(it, getEventDispatcher()!!)
}
return super.onInterceptTouchEvent(event)
}
override fun onTouchEvent(event: MotionEvent?): Boolean {
event?.let {
mJSTouchDispatcher.handleTouchEvent(it, getEventDispatcher()!!)
}
super.onTouchEvent(event)
return true
}
}
react-native: 0.76.1
react-native-navigation: 7.40.3
java: 17
node: 22.5.1
gradle: 8.10
buildToolsVersion = "35.0.0"
minSdkVersion = 24 (react-native-community/discussions-and-proposals#802)
compileSdkVersion = 35
targetSdkVersion = 35
ndkVersion = "27.0.12077973"
kotlinVersion = "2.0.20"
android\gradle.properties
newArchEnabled=false RN 0.76.x by default, it comes true.
hermesEnabled=true
It has only been tested for Android.
### Tasks
- [ ] https://github.com/wix/react-native-navigation/issues/7905