From c1c22ebacc4097ce56f19385161ebb23ee1624b3 Mon Sep 17 00:00:00 2001 From: Nicola Corti Date: Wed, 29 Mar 2023 05:09:32 -0700 Subject: [PATCH] Convert the app template to Kotlin (#36696) Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/36696 As the title says, we're converting the new app template to Kotlin. This will reduce the template size and make it more aligned to market standards. Changelog: [Android] [Changed] - Convert the app template to Kotlin Reviewed By: mdvacca Differential Revision: D44142081 fbshipit-source-id: 6111360b6580460eba0341e47c55704cc673e444 --- .../template/android/app/build.gradle | 1 + .../template/android/build.gradle | 2 + .../java/com/helloworld/ReactNativeFlipper.kt | 60 +++++++++++++++++++ .../main/java/com/helloworld/MainActivity.kt | 22 +++++++ .../java/com/helloworld/MainApplication.kt | 38 ++++++++++++ .../java/com/helloworld/ReactNativeFlipper.kt | 21 +++++++ 6 files changed, 144 insertions(+) create mode 100644 template/android/app/src/debug/java/com/helloworld/ReactNativeFlipper.kt create mode 100644 template/android/app/src/main/java/com/helloworld/MainActivity.kt create mode 100644 template/android/app/src/main/java/com/helloworld/MainApplication.kt create mode 100644 template/android/app/src/release/java/com/helloworld/ReactNativeFlipper.kt diff --git a/packages/react-native/template/android/app/build.gradle b/packages/react-native/template/android/app/build.gradle index 2866a7f85a2730..5b1826b4127e0b 100644 --- a/packages/react-native/template/android/app/build.gradle +++ b/packages/react-native/template/android/app/build.gradle @@ -1,4 +1,5 @@ apply plugin: "com.android.application" +apply plugin: "org.jetbrains.kotlin.android" apply plugin: "com.facebook.react" /** diff --git a/packages/react-native/template/android/build.gradle b/packages/react-native/template/android/build.gradle index 34ea71819406f6..cdbc9026f3552a 100644 --- a/packages/react-native/template/android/build.gradle +++ b/packages/react-native/template/android/build.gradle @@ -9,6 +9,7 @@ buildscript { // We use NDK 23 which has both M1 support and is the side-by-side NDK version from AGP. ndkVersion = "23.1.7779620" + kotlinVersion = "1.7.22" } repositories { google() @@ -17,5 +18,6 @@ buildscript { dependencies { classpath("com.android.tools.build:gradle") classpath("com.facebook.react:react-native-gradle-plugin") + classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion") } } diff --git a/template/android/app/src/debug/java/com/helloworld/ReactNativeFlipper.kt b/template/android/app/src/debug/java/com/helloworld/ReactNativeFlipper.kt new file mode 100644 index 00000000000000..a3658a497ef8f2 --- /dev/null +++ b/template/android/app/src/debug/java/com/helloworld/ReactNativeFlipper.kt @@ -0,0 +1,60 @@ +/** + * 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.helloworld + +import android.content.Context +import com.facebook.flipper.android.AndroidFlipperClient +import com.facebook.flipper.android.utils.FlipperUtils +import com.facebook.flipper.plugins.crashreporter.CrashReporterPlugin +import com.facebook.flipper.plugins.databases.DatabasesFlipperPlugin +import com.facebook.flipper.plugins.fresco.FrescoFlipperPlugin +import com.facebook.flipper.plugins.inspector.DescriptorMapping +import com.facebook.flipper.plugins.inspector.InspectorFlipperPlugin +import com.facebook.flipper.plugins.network.FlipperOkhttpInterceptor +import com.facebook.flipper.plugins.network.NetworkFlipperPlugin +import com.facebook.flipper.plugins.sharedpreferences.SharedPreferencesFlipperPlugin +import com.facebook.react.ReactInstanceEventListener +import com.facebook.react.ReactInstanceManager +import com.facebook.react.bridge.ReactContext +import com.facebook.react.modules.network.NetworkingModule + +/** + * Class responsible of loading Flipper inside your React Native application. This is the debug + * flavor of it. Here you can add your own plugins and customize the Flipper setup. + */ +object ReactNativeFlipper { + fun initializeFlipper(context: Context, reactInstanceManager: ReactInstanceManager) { + if (FlipperUtils.shouldEnableFlipper(context)) { + val client = AndroidFlipperClient.getInstance(context) + client.addPlugin(InspectorFlipperPlugin(context, DescriptorMapping.withDefaults())) + client.addPlugin(DatabasesFlipperPlugin(context)) + client.addPlugin(SharedPreferencesFlipperPlugin(context)) + client.addPlugin(CrashReporterPlugin.getInstance()) + val networkFlipperPlugin = NetworkFlipperPlugin() + NetworkingModule.setCustomClientBuilder { builder -> + builder.addNetworkInterceptor(FlipperOkhttpInterceptor(networkFlipperPlugin)) + } + client.addPlugin(networkFlipperPlugin) + client.start() + + // Fresco Plugin needs to ensure that ImagePipelineFactory is initialized + // Hence we run if after all native modules have been initialized + val currentReactContext = reactInstanceManager.currentReactContext + if (currentReactContext == null) { + reactInstanceManager.addReactInstanceEventListener( + object : ReactInstanceEventListener { + override fun onReactContextInitialized(context: ReactContext) { + reactInstanceManager.removeReactInstanceEventListener(this) + context.runOnNativeModulesQueueThread { client.addPlugin(FrescoFlipperPlugin()) } + } + }) + } else { + client.addPlugin(FrescoFlipperPlugin()) + } + } + } +} diff --git a/template/android/app/src/main/java/com/helloworld/MainActivity.kt b/template/android/app/src/main/java/com/helloworld/MainActivity.kt new file mode 100644 index 00000000000000..d7a3dee1a87bb3 --- /dev/null +++ b/template/android/app/src/main/java/com/helloworld/MainActivity.kt @@ -0,0 +1,22 @@ +package com.helloworld + +import com.facebook.react.ReactActivity +import com.facebook.react.ReactActivityDelegate +import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint.fabricEnabled +import com.facebook.react.defaults.DefaultReactActivityDelegate + +class MainActivity : ReactActivity() { + + /** + * Returns the name of the main component registered from JavaScript. This is used to schedule + * rendering of the component. + */ + override fun getMainComponentName(): String = "HelloWorld" + + /** + * Returns the instance of the [ReactActivityDelegate]. We use [DefaultReactActivityDelegate] + * which allows you to enable New Architecture with a single boolean flags [fabricEnabled] + */ + override fun createReactActivityDelegate(): ReactActivityDelegate = + DefaultReactActivityDelegate(this, mainComponentName, fabricEnabled) +} diff --git a/template/android/app/src/main/java/com/helloworld/MainApplication.kt b/template/android/app/src/main/java/com/helloworld/MainApplication.kt new file mode 100644 index 00000000000000..6ca847e0e0bd5a --- /dev/null +++ b/template/android/app/src/main/java/com/helloworld/MainApplication.kt @@ -0,0 +1,38 @@ +package com.helloworld + +import android.app.Application +import com.facebook.react.PackageList +import com.facebook.react.ReactApplication +import com.facebook.react.ReactNativeHost +import com.facebook.react.ReactPackage +import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint.load +import com.facebook.react.defaults.DefaultReactNativeHost +import com.facebook.soloader.SoLoader + +class MainApplication : Application(), ReactApplication { + + private val reactNativeHost: ReactNativeHost = + object : DefaultReactNativeHost(this) { + override fun getPackages(): List { + // Packages that cannot be autolinked yet can be added manually here, for example: + // packages.add(new MyReactNativePackage()); + return PackageList(this).packages + } + override fun getJSMainModuleName(): String = "index" + override fun getUseDeveloperSupport(): Boolean = BuildConfig.DEBUG + override val isNewArchEnabled: Boolean = BuildConfig.IS_NEW_ARCHITECTURE_ENABLED + override val isHermesEnabled: Boolean = BuildConfig.IS_HERMES_ENABLED + } + + override fun getReactNativeHost(): ReactNativeHost = reactNativeHost + + override fun onCreate() { + super.onCreate() + SoLoader.init(this, false) + if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) { + // If you opted-in for the New Architecture, we load the native entry point for this app. + load() + } + ReactNativeFlipper.initializeFlipper(this, reactNativeHost.reactInstanceManager) + } +} diff --git a/template/android/app/src/release/java/com/helloworld/ReactNativeFlipper.kt b/template/android/app/src/release/java/com/helloworld/ReactNativeFlipper.kt new file mode 100644 index 00000000000000..e9b640550e3fe1 --- /dev/null +++ b/template/android/app/src/release/java/com/helloworld/ReactNativeFlipper.kt @@ -0,0 +1,21 @@ +/** + * 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.helloworld + +import android.content.Context +import com.facebook.react.ReactInstanceManager + +/** + * Class responsible of loading Flipper inside your React Native application. This is the release + * flavor of it so it's empty as we don't want to load Flipper. + */ +object ReactNativeFlipper { + @Suppress("UNUSED_PARAMETER") + fun initializeFlipper(context: Context, reactInstanceManager: ReactInstanceManager) { + // Do nothing as we don't want to initialize Flipper on Release. + } +}