diff --git a/packages/react-native/ReactAndroid/api/ReactAndroid.api b/packages/react-native/ReactAndroid/api/ReactAndroid.api index f879b8f0b242..680096951b9b 100644 --- a/packages/react-native/ReactAndroid/api/ReactAndroid.api +++ b/packages/react-native/ReactAndroid/api/ReactAndroid.api @@ -114,18 +114,15 @@ public class com/facebook/react/ReactActivityDelegate { protected fun composeLaunchOptions ()Landroid/os/Bundle; protected fun createRootView ()Lcom/facebook/react/ReactRootView; protected fun getContext ()Landroid/content/Context; - public fun getCurrentReactContext ()Lcom/facebook/react/bridge/ReactContext; + public final fun getCurrentReactContext ()Lcom/facebook/react/bridge/ReactContext; protected fun getLaunchOptions ()Landroid/os/Bundle; - public fun getMainComponentName ()Ljava/lang/String; protected fun getPlainActivity ()Landroid/app/Activity; protected fun getReactActivity ()Lcom/facebook/react/ReactActivity; - protected fun getReactDelegate ()Lcom/facebook/react/ReactDelegate; public fun getReactHost ()Lcom/facebook/react/ReactHost; public fun getReactInstanceManager ()Lcom/facebook/react/ReactInstanceManager; - protected fun getReactNativeHost ()Lcom/facebook/react/ReactNativeHost; protected fun isFabricEnabled ()Z protected fun isWideColorGamutEnabled ()Z - protected fun loadApp (Ljava/lang/String;)V + public final fun loadApp (Ljava/lang/String;)V public fun onActivityResult (IILandroid/content/Intent;)V public fun onBackPressed ()Z public fun onConfigurationChanged (Landroid/content/res/Configuration;)V diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/ReactActivity.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/ReactActivity.kt index 4eb90518da1d..4d5d77d1e999 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/ReactActivity.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/ReactActivity.kt @@ -29,10 +29,10 @@ public abstract class ReactActivity protected constructor() : get() = reactActivityDelegate.reactHost protected val reactNativeHost: ReactNativeHost - get() = reactActivityDelegate.reactNativeHost + @Suppress("DEPRECATION") get() = reactActivityDelegate.reactNativeHost protected val reactInstanceManager: ReactInstanceManager - get() = reactActivityDelegate.reactInstanceManager + @Suppress("DEPRECATION") get() = reactActivityDelegate.reactInstanceManager /** * Returns the name of the main component registered from JavaScript. This is used to schedule diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/ReactActivityDelegate.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/ReactActivityDelegate.java deleted file mode 100644 index 3b15b287b07a..000000000000 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/ReactActivityDelegate.java +++ /dev/null @@ -1,281 +0,0 @@ -/* - * 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; - -import android.app.Activity; -import android.content.Context; -import android.content.Intent; -import android.content.pm.ActivityInfo; -import android.content.res.Configuration; -import android.os.Build; -import android.os.Bundle; -import android.view.KeyEvent; -import androidx.annotation.Nullable; -import com.facebook.infer.annotation.Assertions; -import com.facebook.react.bridge.Callback; -import com.facebook.react.bridge.ReactContext; -import com.facebook.react.common.annotations.DeprecatedInNewArchitecture; -import com.facebook.react.interfaces.fabric.ReactSurface; -import com.facebook.react.internal.featureflags.ReactNativeNewArchitectureFeatureFlags; -import com.facebook.react.modules.core.PermissionListener; -import com.facebook.systrace.Systrace; - -/** - * Delegate class for {@link ReactActivity}. You can subclass this to provide custom implementations - * for e.g. {@link #getReactNativeHost()}, if your Application class doesn't implement {@link - * ReactApplication}. - */ -public class ReactActivityDelegate { - - private final @Nullable Activity mActivity; - private final @Nullable String mMainComponentName; - - private @Nullable PermissionListener mPermissionListener; - private @Nullable Callback mPermissionsCallback; - private @Nullable ReactDelegate mReactDelegate; - - /** - * Prefer using ReactActivity when possible, as it hooks up all Activity lifecycle methods by - * default. It also implements DefaultHardwareBackBtnHandler, which ReactDelegate requires. - */ - @Deprecated - public ReactActivityDelegate(@Nullable Activity activity, @Nullable String mainComponentName) { - mActivity = activity; - mMainComponentName = mainComponentName; - } - - public ReactActivityDelegate( - @Nullable ReactActivity activity, @Nullable String mainComponentName) { - mActivity = activity; - mMainComponentName = mainComponentName; - } - - /** - * Public API to populate the launch options that will be passed to React. Here you can customize - * the values that will be passed as `initialProperties` to the Renderer. - * - * @return Either null or a key-value map as a Bundle - */ - protected @Nullable Bundle getLaunchOptions() { - return null; - } - - protected @Nullable Bundle composeLaunchOptions() { - return getLaunchOptions(); - } - - /** - * Override to customize ReactRootView creation. - * - *

Not used on bridgeless - */ - protected @Nullable ReactRootView createRootView() { - return null; - } - - /** - * Get the {@link ReactNativeHost} used by this app with Bridge enabled. By default, assumes - * {@link Activity#getApplication()} is an instance of {@link ReactApplication} and calls {@link - * ReactApplication#getReactNativeHost()}. Override this method if your application class does not - * implement {@code ReactApplication} or you simply have a different mechanism for storing a - * {@code ReactNativeHost}, e.g. as a static field somewhere. - */ - @DeprecatedInNewArchitecture(message = "Use getReactHost()") - protected ReactNativeHost getReactNativeHost() { - return ((ReactApplication) getPlainActivity().getApplication()).getReactNativeHost(); - } - - /** - * Get the {@link ReactHost} used by this app with Bridgeless enabled. By default, assumes {@link - * Activity#getApplication()} is an instance of {@link ReactApplication} and calls {@link - * ReactApplication#getReactHost()}. Override this method if your application class does not - * implement {@code ReactApplication} or you simply have a different mechanism for storing a - * {@code ReactHost}, e.g. as a static field somewhere. - */ - public @Nullable ReactHost getReactHost() { - return ((ReactApplication) getPlainActivity().getApplication()).getReactHost(); - } - - protected @Nullable ReactDelegate getReactDelegate() { - return mReactDelegate; - } - - @DeprecatedInNewArchitecture(message = "Use getReactHost()") - public ReactInstanceManager getReactInstanceManager() { - return mReactDelegate.getReactInstanceManager(); - } - - public @Nullable String getMainComponentName() { - return mMainComponentName; - } - - public void onCreate(Bundle savedInstanceState) { - Systrace.traceSection( - Systrace.TRACE_TAG_REACT, - "ReactActivityDelegate.onCreate::init", - () -> { - String mainComponentName = getMainComponentName(); - final Bundle launchOptions = composeLaunchOptions(); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && isWideColorGamutEnabled()) { - mActivity.getWindow().setColorMode(ActivityInfo.COLOR_MODE_WIDE_COLOR_GAMUT); - } - if (ReactNativeNewArchitectureFeatureFlags.enableBridgelessArchitecture()) { - mReactDelegate = - new ReactDelegate( - getPlainActivity(), getReactHost(), mainComponentName, launchOptions); - } else { - mReactDelegate = - new ReactDelegate( - getPlainActivity(), - getReactNativeHost(), - mainComponentName, - launchOptions, - isFabricEnabled()) { - @Override - protected ReactRootView createRootView() { - ReactRootView rootView = ReactActivityDelegate.this.createRootView(); - if (rootView == null) { - rootView = super.createRootView(); - } - return rootView; - } - }; - } - if (mainComponentName != null) { - loadApp(mainComponentName); - } - }); - } - - protected void loadApp(String appKey) { - mReactDelegate.loadApp(appKey); - getPlainActivity().setContentView(mReactDelegate.getReactRootView()); - } - - public void setReactSurface(ReactSurface reactSurface) { - mReactDelegate.setReactSurface(reactSurface); - } - - public void setReactRootView(ReactRootView reactRootView) { - mReactDelegate.setReactRootView(reactRootView); - } - - public void onUserLeaveHint() { - if (mReactDelegate != null) { - mReactDelegate.onUserLeaveHint(); - } - } - - public void onPause() { - mReactDelegate.onHostPause(); - } - - public void onResume() { - mReactDelegate.onHostResume(); - - if (mPermissionsCallback != null) { - mPermissionsCallback.invoke(); - mPermissionsCallback = null; - } - } - - public void onDestroy() { - mReactDelegate.onHostDestroy(); - } - - public void onActivityResult(int requestCode, int resultCode, Intent data) { - mReactDelegate.onActivityResult(requestCode, resultCode, data, true); - } - - public boolean onKeyDown(int keyCode, KeyEvent event) { - return mReactDelegate.onKeyDown(keyCode, event); - } - - public boolean onKeyUp(int keyCode, KeyEvent event) { - return mReactDelegate.shouldShowDevMenuOrReload(keyCode, event); - } - - public boolean onKeyLongPress(int keyCode, KeyEvent event) { - return mReactDelegate.onKeyLongPress(keyCode); - } - - public boolean onBackPressed() { - return mReactDelegate.onBackPressed(); - } - - public boolean onNewIntent(Intent intent) { - return mReactDelegate.onNewIntent(intent); - } - - public void onWindowFocusChanged(boolean hasFocus) { - mReactDelegate.onWindowFocusChanged(hasFocus); - } - - public void onConfigurationChanged(Configuration newConfig) { - mReactDelegate.onConfigurationChanged(newConfig); - } - - public void requestPermissions( - String[] permissions, int requestCode, PermissionListener listener) { - mPermissionListener = listener; - getPlainActivity().requestPermissions(permissions, requestCode); - } - - public void onRequestPermissionsResult( - final int requestCode, final String[] permissions, final int[] grantResults) { - mPermissionsCallback = - args -> { - if (mPermissionListener != null - && mPermissionListener.onRequestPermissionsResult( - requestCode, permissions, grantResults)) { - mPermissionListener = null; - } - }; - } - - protected Context getContext() { - return Assertions.assertNotNull(mActivity); - } - - protected Activity getPlainActivity() { - return ((Activity) getContext()); - } - - protected ReactActivity getReactActivity() { - return ((ReactActivity) getContext()); - } - - /** - * Get the current {@link ReactContext} from ReactHost or ReactInstanceManager - * - *

Do not store a reference to this, if the React instance is reloaded or destroyed, this - * context will no longer be valid. - */ - public @Nullable ReactContext getCurrentReactContext() { - return mReactDelegate.getCurrentReactContext(); - } - - /** - * Override this method if you wish to selectively toggle Fabric for a specific surface. This will - * also control if Concurrent Root (React 18) should be enabled or not. - * - * @return true if Fabric is enabled for this Activity, false otherwise. - */ - protected boolean isFabricEnabled() { - return ReactNativeNewArchitectureFeatureFlags.enableFabricRenderer(); - } - - /** - * Override this method if you wish to selectively toggle wide color gamut for a specific surface. - * - * @return true if wide gamut is enabled for this Activity, false otherwise. - */ - protected boolean isWideColorGamutEnabled() { - return false; - } -} diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/ReactActivityDelegate.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/ReactActivityDelegate.kt new file mode 100644 index 000000000000..89bb3d16e0b2 --- /dev/null +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/ReactActivityDelegate.kt @@ -0,0 +1,248 @@ +/* + * 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 + +import android.app.Activity +import android.content.Context +import android.content.Intent +import android.content.pm.ActivityInfo +import android.content.res.Configuration +import android.os.Build +import android.os.Bundle +import android.view.KeyEvent +import com.facebook.react.bridge.Callback +import com.facebook.react.bridge.ReactContext +import com.facebook.react.interfaces.fabric.ReactSurface +import com.facebook.react.internal.featureflags.ReactNativeNewArchitectureFeatureFlags +import com.facebook.react.modules.core.PermissionListener +import com.facebook.systrace.Systrace + +/** + * Delegate class for [ReactActivity]. You can subclass this to provide custom implementations for + * e.g. [.getReactNativeHost], if your Application class doesn't implement [ ]. + */ +public open class ReactActivityDelegate { + private val activity: ReactActivity? + private val mainComponentName: String? + + private var permissionListener: PermissionListener? = null + private var permissionsCallback: Callback? = null + internal var reactDelegate: ReactDelegate? = null + private set + + protected open val launchOptions: Bundle? + /** + * Public API to populate the launch options that will be passed to React. Here you can + * customize the values that will be passed as `initialProperties` to the Renderer. + * + * @return Either null or a key-value map as a Bundle + */ + get() = null + + @Deprecated("Use reactHost in New Architecture") + internal open val reactNativeHost: ReactNativeHost + /** + * Get the [ReactNativeHost] used by this app with Bridge enabled. By default, assumes + * [Activity.getApplication] is an instance of [ReactApplication] and calls + * [ ][ReactApplication.getReactNativeHost]. Override this method if your application class does + * not implement `ReactApplication` or you simply have a different mechanism for storing a + * `ReactNativeHost`, e.g. as a static field somewhere. + */ + get() = (plainActivity.getApplication() as ReactApplication).reactNativeHost + + public open val reactHost: ReactHost? + /** + * Get the [ReactHost] used by this app with Bridgeless enabled. By default, assumes + * [ ][Activity.getApplication] is an instance of [ReactApplication] and calls + * [ ][ReactApplication.getReactHost]. Override this method if your application class does not + * implement `ReactApplication` or you simply have a different mechanism for storing a + * `ReactHost`, e.g. as a static field somewhere. + */ + get() = (plainActivity.getApplication() as ReactApplication).reactHost + + /** + * Prefer using ReactActivity when possible, as it hooks up all Activity lifecycle methods by + * default. It also implements DefaultHardwareBackBtnHandler, which ReactDelegate requires. + */ + @Deprecated("") + public constructor(activity: Activity?, mainComponentName: String?) { + this.activity = activity as? ReactActivity + this.mainComponentName = mainComponentName + } + + public constructor(activity: ReactActivity?, mainComponentName: String?) { + this.activity = activity + this.mainComponentName = mainComponentName + } + + protected open fun composeLaunchOptions(): Bundle? = launchOptions + + /** + * Override to customize ReactRootView creation. + * + * Not used on bridgeless + */ + protected open fun createRootView(): ReactRootView? = null + + @Deprecated("Use reactHost in New Architecture") + public open val reactInstanceManager: ReactInstanceManager + get() = checkNotNull(reactDelegate).reactInstanceManager + + public open fun onCreate(savedInstanceState: Bundle?) { + Systrace.traceSection(Systrace.TRACE_TAG_REACT, "ReactActivityDelegate.onCreate::init") { + val mainComponentName = mainComponentName + val launchOptions: Bundle? = composeLaunchOptions() + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && isWideColorGamutEnabled) { + activity?.getWindow()?.setColorMode(ActivityInfo.COLOR_MODE_WIDE_COLOR_GAMUT) + } + if (ReactNativeNewArchitectureFeatureFlags.enableBridgelessArchitecture()) { + reactDelegate = ReactDelegate(plainActivity, reactHost, mainComponentName, launchOptions) + } else { + @Suppress("DEPRECATION") + reactDelegate = + object : + ReactDelegate( + plainActivity, + reactNativeHost, + mainComponentName, + launchOptions, + isFabricEnabled) { + override fun createRootView(): ReactRootView { + var rootView = this@ReactActivityDelegate.createRootView() + if (rootView == null) { + rootView = super.createRootView() + } + return rootView + } + } + } + if (mainComponentName != null) { + loadApp(mainComponentName) + } + } + } + + public fun loadApp(appKey: String?) { + reactDelegate?.loadApp(appKey) + plainActivity.setContentView(reactDelegate?.reactRootView) + } + + public open fun setReactSurface(reactSurface: ReactSurface?) { + reactDelegate?.setReactSurface(reactSurface) + } + + public open fun setReactRootView(reactRootView: ReactRootView?) { + reactDelegate?.reactRootView = reactRootView + } + + public open fun onUserLeaveHint() { + reactDelegate?.onUserLeaveHint() + } + + public open fun onPause() { + reactDelegate?.onHostPause() + } + + public open fun onResume() { + reactDelegate?.onHostResume() + permissionsCallback?.invoke() + permissionsCallback = null + } + + public open fun onDestroy() { + reactDelegate?.onHostDestroy() + } + + public open fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { + reactDelegate?.onActivityResult(requestCode, resultCode, data, true) + } + + public open fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean = + reactDelegate?.onKeyDown(keyCode, event) ?: false + + public open fun onKeyUp(keyCode: Int, event: KeyEvent?): Boolean = + reactDelegate?.shouldShowDevMenuOrReload(keyCode, event) ?: false + + public open fun onKeyLongPress(keyCode: Int, event: KeyEvent?): Boolean = + reactDelegate?.onKeyLongPress(keyCode) ?: false + + public open fun onBackPressed(): Boolean = reactDelegate?.onBackPressed() ?: false + + public open fun onNewIntent(intent: Intent?): Boolean = + reactDelegate?.onNewIntent(intent) ?: false + + public open fun onWindowFocusChanged(hasFocus: Boolean) { + reactDelegate?.onWindowFocusChanged(hasFocus) + } + + public open fun onConfigurationChanged(newConfig: Configuration?) { + reactDelegate?.onConfigurationChanged(newConfig) + } + + public open fun requestPermissions( + permissions: Array?, + requestCode: Int, + listener: PermissionListener? + ) { + permissionListener = listener + if (permissions != null) { + plainActivity?.requestPermissions(permissions, requestCode) + } + } + + public open fun onRequestPermissionsResult( + requestCode: Int, + permissions: Array?, + grantResults: IntArray? + ) { + permissionsCallback = Callback { _ -> + if (permissions != null && + grantResults != null && + permissionListener?.onRequestPermissionsResult(requestCode, permissions, grantResults) + ?: false) { + permissionListener = null + } + } + } + + protected open val context: Context + get() = checkNotNull(activity) as Context + + protected open val plainActivity: Activity + get() = (context as Activity) + + protected open val reactActivity: ReactActivity + get() = (context as ReactActivity) + + public val currentReactContext: ReactContext? + /** + * Get the current [ReactContext] from ReactHost or ReactInstanceManager + * + * Do not store a reference to this, if the React instance is reloaded or destroyed, this + * context will no longer be valid. + */ + get() = reactDelegate?.currentReactContext + + protected open val isFabricEnabled: Boolean + /** + * Override this method if you wish to selectively toggle Fabric for a specific surface. This + * will also control if Concurrent Root (React 18) should be enabled or not. + * + * @return true if Fabric is enabled for this Activity, false otherwise. + */ + get() = ReactNativeNewArchitectureFeatureFlags.enableFabricRenderer() + + protected open val isWideColorGamutEnabled: Boolean + /** + * Override this method if you wish to selectively toggle wide color gamut for a specific + * surface. + * + * @return true if wide gamut is enabled for this Activity, false otherwise. + */ + get() = false +} diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/defaults/DefaultReactActivityDelegate.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/defaults/DefaultReactActivityDelegate.kt index 2c2ee7e8d0a8..99f8c478c8bb 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/defaults/DefaultReactActivityDelegate.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/defaults/DefaultReactActivityDelegate.kt @@ -40,5 +40,6 @@ public open class DefaultReactActivityDelegate( @Suppress("UNUSED_PARAMETER") concurrentReactEnabled: Boolean, ) : this(activity, mainComponentName, fabricEnabled) - override fun isFabricEnabled(): Boolean = fabricEnabled + override val isFabricEnabled: Boolean + get() = fabricEnabled }