Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions packages/react-native/ReactAndroid/api/ReactAndroid.api
Original file line number Diff line number Diff line change
Expand Up @@ -2343,9 +2343,11 @@ public class com/facebook/react/fabric/FabricUIManager : com/facebook/react/brid
public fun dispatchCommand (IILcom/facebook/react/bridge/ReadableArray;)V
public fun dispatchCommand (IILjava/lang/String;Lcom/facebook/react/bridge/ReadableArray;)V
public fun dispatchCommand (ILjava/lang/String;Lcom/facebook/react/bridge/ReadableArray;)V
public fun findNextFocusableElement (III)Ljava/lang/Integer;
public fun getColor (I[Ljava/lang/String;)I
public fun getEventDispatcher ()Lcom/facebook/react/uimanager/events/EventDispatcher;
public fun getPerformanceCounters ()Ljava/util/Map;
public fun getRelativeAncestorList (II)[I
public fun getThemeData (I[F)Z
public fun initialize ()V
public fun invalidate ()V
Expand Down Expand Up @@ -3975,6 +3977,7 @@ public abstract interface class com/facebook/react/uimanager/ReactClippingViewGr
public abstract fun getRemoveClippedSubviews ()Z
public abstract fun setRemoveClippedSubviews (Z)V
public abstract fun updateClippingRect ()V
public abstract fun updateClippingRect (Ljava/util/Set;)V
}

public final class com/facebook/react/uimanager/ReactClippingViewGroupHelper {
Expand Down Expand Up @@ -5871,6 +5874,7 @@ public class com/facebook/react/views/scroll/ReactHorizontalScrollView : android
public fun executeKeyEvent (Landroid/view/KeyEvent;)Z
public fun flashScrollIndicators ()V
public fun fling (I)V
public fun focusSearch (Landroid/view/View;I)Landroid/view/View;
public fun getChildVisibleRect (Landroid/view/View;Landroid/graphics/Rect;Landroid/graphics/Point;)Z
public fun getClippingRect (Landroid/graphics/Rect;)V
public fun getFlingAnimator ()Landroid/animation/ValueAnimator;
Expand Down Expand Up @@ -5933,6 +5937,7 @@ public class com/facebook/react/views/scroll/ReactHorizontalScrollView : android
public fun setStateWrapper (Lcom/facebook/react/uimanager/StateWrapper;)V
public fun startFlingAnimator (II)V
public fun updateClippingRect ()V
public fun updateClippingRect (Ljava/util/Set;)V
}

public class com/facebook/react/views/scroll/ReactHorizontalScrollViewManager : com/facebook/react/uimanager/ViewGroupManager, com/facebook/react/views/scroll/ReactScrollViewCommandHelper$ScrollCommandHandler {
Expand Down Expand Up @@ -5998,6 +6003,7 @@ public class com/facebook/react/views/scroll/ReactScrollView : android/widget/Sc
public fun executeKeyEvent (Landroid/view/KeyEvent;)Z
public fun flashScrollIndicators ()V
public fun fling (I)V
public fun focusSearch (Landroid/view/View;I)Landroid/view/View;
public fun getChildVisibleRect (Landroid/view/View;Landroid/graphics/Rect;Landroid/graphics/Point;)Z
public fun getClippingRect (Landroid/graphics/Rect;)V
public fun getFlingAnimator ()Landroid/animation/ValueAnimator;
Expand Down Expand Up @@ -6061,6 +6067,7 @@ public class com/facebook/react/views/scroll/ReactScrollView : android/widget/Sc
public fun setStateWrapper (Lcom/facebook/react/uimanager/StateWrapper;)V
public fun startFlingAnimator (II)V
public fun updateClippingRect ()V
public fun updateClippingRect (Ljava/util/Set;)V
}

public final class com/facebook/react/views/scroll/ReactScrollViewCommandHelper {
Expand Down Expand Up @@ -6118,6 +6125,7 @@ public final class com/facebook/react/views/scroll/ReactScrollViewHelper {
public static final fun emitScrollEvent (Landroid/view/ViewGroup;FF)V
public static final fun emitScrollMomentumBeginEvent (Landroid/view/ViewGroup;II)V
public static final fun emitScrollMomentumEndEvent (Landroid/view/ViewGroup;)V
public static final fun findNextFocusableView (Landroid/view/ViewGroup;Landroid/view/View;IZ)Landroid/view/View;
public static final fun forceUpdateState (Landroid/view/ViewGroup;)V
public static final fun getDefaultScrollAnimationDuration (Landroid/content/Context;)I
public static final fun getNextFlingStartValue (Landroid/view/ViewGroup;III)I
Expand All @@ -6127,6 +6135,7 @@ public final class com/facebook/react/views/scroll/ReactScrollViewHelper {
public final fun registerFlingAnimator (Landroid/view/ViewGroup;)V
public static final fun removeLayoutChangeListener (Lcom/facebook/react/views/scroll/ReactScrollViewHelper$LayoutChangeListener;)V
public static final fun removeScrollListener (Lcom/facebook/react/views/scroll/ReactScrollViewHelper$ScrollListener;)V
public static final fun resolveAbsoluteDirection (IZI)I
public static final fun smoothScrollTo (Landroid/view/ViewGroup;II)V
public static final fun updateFabricScrollState (Landroid/view/ViewGroup;)V
public final fun updateFabricScrollState (Landroid/view/ViewGroup;II)V
Expand Down Expand Up @@ -6926,6 +6935,7 @@ public class com/facebook/react/views/view/ReactViewGroup : android/view/ViewGro
public fun setRemoveClippedSubviews (Z)V
public fun setTranslucentBackgroundDrawable (Landroid/graphics/drawable/Drawable;)V
public fun updateClippingRect ()V
public fun updateClippingRect (Ljava/util/Set;)V
public fun updateDrawingOrder ()V
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import androidx.annotation.AnyThread;
import androidx.annotation.Nullable;
import androidx.annotation.UiThread;
import androidx.core.view.ViewCompat.FocusRealDirection;
import com.facebook.common.logging.FLog;
import com.facebook.infer.annotation.Assertions;
import com.facebook.infer.annotation.Nullsafe;
Expand Down Expand Up @@ -262,6 +263,52 @@ public <T extends View> int addRootView(
return rootTag;
}

/**
* Find the next focusable element's id and position relative to the parent from the shadow tree
* based on the current focusable element and the direction.
*
* @return A NextFocusableNode object where the 'id' is the reactId/Tag of the next focusable
* view, returns null if no view could be found
*/
public @Nullable Integer findNextFocusableElement(
int parentTag, int focusedTag, @FocusRealDirection int direction) {
if (mBinding == null) {
return null;
}

int generalizedDirection;

switch (direction) {
case View.FOCUS_DOWN:
generalizedDirection = 0;
break;
case View.FOCUS_UP:
generalizedDirection = 1;
break;
case View.FOCUS_RIGHT:
generalizedDirection = 2;
break;
case View.FOCUS_LEFT:
generalizedDirection = 3;
break;
default:
return null;
}

int serializedNextFocusableNodeMetrics =
mBinding.findNextFocusableElement(parentTag, focusedTag, generalizedDirection);

if (serializedNextFocusableNodeMetrics == -1) {
return null;
}

return serializedNextFocusableNodeMetrics;
}

public @Nullable int[] getRelativeAncestorList(int rootTag, int childTag) {
return mBinding != null ? mBinding.getRelativeAncestorList(rootTag, childTag) : null;
}

@Override
@AnyThread
@ThreadConfined(ANY)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,10 @@ internal class FabricUIManagerBinding : HybridClassBase() {
isMountable: Boolean
)

external fun findNextFocusableElement(parentTag: Int, focusedTag: Int, direction: Int): Int

external fun getRelativeAncestorList(rootTag: Int, childTag: Int): IntArray

external fun stopSurface(surfaceId: Int)

external fun stopSurfaceWithSurfaceHandler(surfaceHandler: SurfaceHandlerBinding)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<8a0be46378bf8810ac52a44024bfd587>>
* @generated SignedSource<<2b53d8dbd34ed8f3f2b4310a2a597618>>
*/

/**
Expand Down Expand Up @@ -84,6 +84,12 @@ public object ReactNativeFeatureFlags {
@JvmStatic
public fun enableCppPropsIteratorSetter(): Boolean = accessor.enableCppPropsIteratorSetter()

/**
* This enables the fabric implementation of focus search so that we can focus clipped elements
*/
@JvmStatic
public fun enableCustomFocusSearchOnClippedElementsAndroid(): Boolean = accessor.enableCustomFocusSearchOnClippedElementsAndroid()

/**
* Feature flag to configure eager attachment of the root view/initialisation of the JS code.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<5e462e70ca55296d1651678a7243508f>>
* @generated SignedSource<<2ebb741f6b7d8e293ee112ca68dac748>>
*/

/**
Expand All @@ -29,6 +29,7 @@ internal class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAcces
private var enableAccumulatedUpdatesInRawPropsAndroidCache: Boolean? = null
private var enableBridgelessArchitectureCache: Boolean? = null
private var enableCppPropsIteratorSetterCache: Boolean? = null
private var enableCustomFocusSearchOnClippedElementsAndroidCache: Boolean? = null
private var enableEagerRootViewAttachmentCache: Boolean? = null
private var enableFabricLogsCache: Boolean? = null
private var enableFabricRendererCache: Boolean? = null
Expand Down Expand Up @@ -143,6 +144,15 @@ internal class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAcces
return cached
}

override fun enableCustomFocusSearchOnClippedElementsAndroid(): Boolean {
var cached = enableCustomFocusSearchOnClippedElementsAndroidCache
if (cached == null) {
cached = ReactNativeFeatureFlagsCxxInterop.enableCustomFocusSearchOnClippedElementsAndroid()
enableCustomFocusSearchOnClippedElementsAndroidCache = cached
}
return cached
}

override fun enableEagerRootViewAttachment(): Boolean {
var cached = enableEagerRootViewAttachmentCache
if (cached == null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<f72540f3d9ad5f95ff3991e096a72c97>>
* @generated SignedSource<<f2807c302082db2133c4ded1468904c5>>
*/

/**
Expand Down Expand Up @@ -46,6 +46,8 @@ public object ReactNativeFeatureFlagsCxxInterop {

@DoNotStrip @JvmStatic public external fun enableCppPropsIteratorSetter(): Boolean

@DoNotStrip @JvmStatic public external fun enableCustomFocusSearchOnClippedElementsAndroid(): Boolean

@DoNotStrip @JvmStatic public external fun enableEagerRootViewAttachment(): Boolean

@DoNotStrip @JvmStatic public external fun enableFabricLogs(): Boolean
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<d123160bb9ffbc3b82b06ad2513a69c6>>
* @generated SignedSource<<2f333814db6601fe63060af1e7fb6d5e>>
*/

/**
Expand Down Expand Up @@ -41,6 +41,8 @@ public open class ReactNativeFeatureFlagsDefaults : ReactNativeFeatureFlagsProvi

override fun enableCppPropsIteratorSetter(): Boolean = false

override fun enableCustomFocusSearchOnClippedElementsAndroid(): Boolean = true

override fun enableEagerRootViewAttachment(): Boolean = false

override fun enableFabricLogs(): Boolean = false
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<9ef4812c333ca4fa1b05c66d0c59e303>>
* @generated SignedSource<<fa88f340c35fb2fe20223b9bda83f1f2>>
*/

/**
Expand Down Expand Up @@ -33,6 +33,7 @@ internal class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcc
private var enableAccumulatedUpdatesInRawPropsAndroidCache: Boolean? = null
private var enableBridgelessArchitectureCache: Boolean? = null
private var enableCppPropsIteratorSetterCache: Boolean? = null
private var enableCustomFocusSearchOnClippedElementsAndroidCache: Boolean? = null
private var enableEagerRootViewAttachmentCache: Boolean? = null
private var enableFabricLogsCache: Boolean? = null
private var enableFabricRendererCache: Boolean? = null
Expand Down Expand Up @@ -156,6 +157,16 @@ internal class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcc
return cached
}

override fun enableCustomFocusSearchOnClippedElementsAndroid(): Boolean {
var cached = enableCustomFocusSearchOnClippedElementsAndroidCache
if (cached == null) {
cached = currentProvider.enableCustomFocusSearchOnClippedElementsAndroid()
accessedFeatureFlags.add("enableCustomFocusSearchOnClippedElementsAndroid")
enableCustomFocusSearchOnClippedElementsAndroidCache = cached
}
return cached
}

override fun enableEagerRootViewAttachment(): Boolean {
var cached = enableEagerRootViewAttachmentCache
if (cached == null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<f3f828ba38e2b88b94a30234c46da607>>
* @generated SignedSource<<f11813bd6beb2a198df81975e9f69fea>>
*/

/**
Expand Down Expand Up @@ -41,6 +41,8 @@ public interface ReactNativeFeatureFlagsProvider {

@DoNotStrip public fun enableCppPropsIteratorSetter(): Boolean

@DoNotStrip public fun enableCustomFocusSearchOnClippedElementsAndroid(): Boolean

@DoNotStrip public fun enableEagerRootViewAttachment(): Boolean

@DoNotStrip public fun enableFabricLogs(): Boolean
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ public interface ReactClippingViewGroup {
*/
public fun updateClippingRect()

public fun updateClippingRect(excludedView: Set<Int>?)

/**
* Get rectangular bounds to which view is currently clipped to. Called only on views that has set
* `removeCLippedSubviews` property value to `true`.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import static com.facebook.react.views.scroll.ReactScrollViewHelper.SNAP_ALIGNMENT_DISABLED;
import static com.facebook.react.views.scroll.ReactScrollViewHelper.SNAP_ALIGNMENT_END;
import static com.facebook.react.views.scroll.ReactScrollViewHelper.SNAP_ALIGNMENT_START;
import static com.facebook.react.views.scroll.ReactScrollViewHelper.findNextFocusableView;

import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
Expand All @@ -31,6 +32,7 @@
import android.widget.OverScroller;
import androidx.annotation.Nullable;
import androidx.core.view.ViewCompat;
import androidx.core.view.ViewCompat.FocusRealDirection;
import com.facebook.common.logging.FLog;
import com.facebook.infer.annotation.Assertions;
import com.facebook.infer.annotation.Nullsafe;
Expand All @@ -39,6 +41,7 @@
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.common.ReactConstants;
import com.facebook.react.common.build.ReactBuildConfig;
import com.facebook.react.internal.featureflags.ReactNativeFeatureFlags;
import com.facebook.react.uimanager.BackgroundStyleApplicator;
import com.facebook.react.uimanager.LengthPercentage;
import com.facebook.react.uimanager.LengthPercentageType;
Expand All @@ -64,6 +67,7 @@
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;

/** Similar to {@link ReactScrollView} but only supports horizontal scrolling. */
@Nullsafe(Nullsafe.Mode.LOCAL)
Expand Down Expand Up @@ -771,8 +775,26 @@ protected void onDetachedFromWindow() {
}
}

@Override
public @Nullable View focusSearch(View focused, @FocusRealDirection int direction) {
if (ReactNativeFeatureFlags.enableCustomFocusSearchOnClippedElementsAndroid()) {
@Nullable View nextfocusableView = findNextFocusableView(this, focused, direction, true);

if (nextfocusableView != null) {
return nextfocusableView;
}
}

return super.focusSearch(focused, direction);
}

@Override
public void updateClippingRect() {
updateClippingRect(null);
}

@Override
public void updateClippingRect(@Nullable Set<Integer> excludedViewId) {
if (!mRemoveClippedSubviews) {
return;
}
Expand All @@ -784,7 +806,7 @@ public void updateClippingRect() {
ReactClippingViewGroupHelper.calculateClippingRect(this, mClippingRect);
View contentView = getContentView();
if (contentView instanceof ReactClippingViewGroup) {
((ReactClippingViewGroup) contentView).updateClippingRect();
((ReactClippingViewGroup) contentView).updateClippingRect(excludedViewId);
}
} finally {
Systrace.endSection(Systrace.TRACE_TAG_REACT);
Expand Down
Loading
Loading