/* * Copyright (c) Meta Platforms, Inc. and affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ package com.facebook.react.uimanager.common import android.view.View /** * Utility object providing helper methods for working with React Native views. * * This object contains utilities for determining which UIManager (Legacy/Paper or Fabric) a view * belongs to, based on view tags and surface IDs. These utilities are essential for routing events * and operations to the correct UIManager implementation. * * @see UIManagerType */ public object ViewUtil { /** * Constant representing the absence of a surface ID. * * This value (-1) is used as a placeholder when no surface ID is available, typically indicating * that the view or event originated from the legacy (Paper) UIManager rather than Fabric. */ public const val NO_SURFACE_ID: Int = -1 /** * Counter for uniquely identifying views. - % 2 === 0 means it is a Fabric tag. See * https://github.com/facebook/react/pull/12587 * * @param viewTag tag of the view this is event is dispatched to */ @JvmStatic @UIManagerType public fun getUIManagerType(viewTag: Int): Int = if (viewTag % 2 == 0) { UIManagerType.FABRIC } else { UIManagerType.LEGACY } /** * Overload for [getUIManagerType] that uses the view's id to determine if it originated from * Fabric */ @JvmStatic @UIManagerType public fun getUIManagerType(view: View): Int = getUIManagerType(view.id) /** * Version of getUIManagerType that uses both surfaceId and viewTag heuristics * * @param viewTag tag of the view this is event is dispatched to * @param surfaceId ID of the corresponding surface */ @Suppress("DEPRECATION") @JvmStatic @UIManagerType public fun getUIManagerType(viewTag: Int, surfaceId: Int): Int { // We have a contract that Fabric events *always* have a SurfaceId passed in, and non-Fabric // events NEVER have a SurfaceId passed in (the default/placeholder of -1 is passed in instead). // // Why does this matter? // Events can be sent to Views that are part of the View hierarchy *but not directly managed // by React Native*. For example, embedded custom hierarchies, Litho hierarchies, etc. // In those cases it's important to know that the Event should be sent to the Fabric or // non-Fabric UIManager, and we cannot use the ViewTag for inference since it's not controlled // by RN and is essentially a random number. // At some point it would be great to pass the SurfaceContext here instead. @UIManagerType val uiManagerType = if (surfaceId == -1) UIManagerType.LEGACY else UIManagerType.FABRIC if (uiManagerType == UIManagerType.LEGACY && !isRootTag(viewTag)) { // TODO (T123064648): Some events for Fabric still didn't have the surfaceId set, so if it's // not a React RootView, double check if the tag belongs to Fabric. if (viewTag % 2 == 0) { return UIManagerType.FABRIC } } return uiManagerType } /** * @param viewTag react tag * @return if the react tag received by parameter is a RootTag or not. */ @Deprecated( "You should not check the tag of the view to inspect if it's the rootTag. " + "Relying on this logic could make your app/library break in the future.", ReplaceWith(""), ) @JvmStatic public fun isRootTag(viewTag: Int): Boolean = viewTag % 10 == 1 }