升级React Native到v0.71.3并启用新架构

This commit is contained in:
lyswhut 2023-03-04 23:06:14 +08:00
parent 0c1fd5e696
commit 5712e419d6
44 changed files with 6550 additions and 10446 deletions

View File

@ -1,6 +0,0 @@
[android]
target = Google Inc.:Google APIs:23
[maven_repositories]
central = https://repo1.maven.org/maven2

View File

@ -8,8 +8,7 @@ on:
jobs: jobs:
Android: Android:
name: Android name: Android
# runs-on: ubuntu-latest runs-on: ubuntu-latest
runs-on: macos-latest
steps: steps:
- name: Check out git repository - name: Check out git repository
uses: actions/checkout@v3 uses: actions/checkout@v3
@ -19,12 +18,6 @@ jobs:
with: with:
node-version: '18' node-version: '18'
- name: Install Java
uses: actions/setup-java@v3
with:
distribution: 'temurin'
java-version: '11'
- name: Cache Gradle Wrapper - name: Cache Gradle Wrapper
uses: actions/cache@v3 uses: actions/cache@v3
with: with:
@ -54,7 +47,7 @@ jobs:
run: | run: |
cd android cd android
echo ${{ secrets.KEYSTORE_STORE_FILE_BASE64 }} | base64 --decode > app/${{ secrets.KEYSTORE_STORE_FILE }} echo ${{ secrets.KEYSTORE_STORE_FILE_BASE64 }} | base64 --decode > app/${{ secrets.KEYSTORE_STORE_FILE }}
./gradlew assembleRelease --no-daemon -PMYAPP_UPLOAD_STORE_FILE='${{ secrets.KEYSTORE_STORE_FILE }}' -PMYAPP_UPLOAD_KEY_ALIAS='${{ secrets.KEYSTORE_KEY_ALIAS }}' -PMYAPP_UPLOAD_STORE_PASSWORD='${{ secrets.KEYSTORE_PASSWORD }}' -PMYAPP_UPLOAD_KEY_PASSWORD='${{ secrets.KEYSTORE_KEY_PASSWORD }}' ./gradlew assembleRelease -PMYAPP_UPLOAD_STORE_FILE='${{ secrets.KEYSTORE_STORE_FILE }}' -PMYAPP_UPLOAD_KEY_ALIAS='${{ secrets.KEYSTORE_KEY_ALIAS }}' -PMYAPP_UPLOAD_STORE_PASSWORD='${{ secrets.KEYSTORE_PASSWORD }}' -PMYAPP_UPLOAD_KEY_PASSWORD='${{ secrets.KEYSTORE_KEY_PASSWORD }}'
# Push tag to GitHub if package.json version's tag is not tagged # Push tag to GitHub if package.json version's tag is not tagged
- name: Get package version - name: Get package version
@ -63,8 +56,7 @@ jobs:
- name: Generate file MD5 - name: Generate file MD5
run: | run: |
cd android/app/build/outputs/apk/release cd android/app/build/outputs/apk/release
md5 *.apk md5sum *.apk
# md5sum *.apk
- name: Upload Artifact arm64-v8a - name: Upload Artifact arm64-v8a
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v3

View File

@ -8,8 +8,7 @@ on:
jobs: jobs:
Android: Android:
name: Android name: Android
# runs-on: ubuntu-latest runs-on: ubuntu-latest
runs-on: macos-latest
steps: steps:
- name: Check out git repository - name: Check out git repository
uses: actions/checkout@v3 uses: actions/checkout@v3
@ -19,12 +18,6 @@ jobs:
with: with:
node-version: '18' node-version: '18'
- name: Install Java
uses: actions/setup-java@v3
with:
distribution: 'temurin'
java-version: '11'
- name: Cache Gradle Wrapper - name: Cache Gradle Wrapper
uses: actions/cache@v3 uses: actions/cache@v3
with: with:
@ -54,7 +47,7 @@ jobs:
run: | run: |
cd android cd android
echo ${{ secrets.KEYSTORE_STORE_FILE_BASE64 }} | base64 --decode > app/${{ secrets.KEYSTORE_STORE_FILE }} echo ${{ secrets.KEYSTORE_STORE_FILE_BASE64 }} | base64 --decode > app/${{ secrets.KEYSTORE_STORE_FILE }}
./gradlew assembleRelease --no-daemon -PMYAPP_UPLOAD_STORE_FILE='${{ secrets.KEYSTORE_STORE_FILE }}' -PMYAPP_UPLOAD_KEY_ALIAS='${{ secrets.KEYSTORE_KEY_ALIAS }}' -PMYAPP_UPLOAD_STORE_PASSWORD='${{ secrets.KEYSTORE_PASSWORD }}' -PMYAPP_UPLOAD_KEY_PASSWORD='${{ secrets.KEYSTORE_KEY_PASSWORD }}' ./gradlew assembleRelease -PMYAPP_UPLOAD_STORE_FILE='${{ secrets.KEYSTORE_STORE_FILE }}' -PMYAPP_UPLOAD_KEY_ALIAS='${{ secrets.KEYSTORE_KEY_ALIAS }}' -PMYAPP_UPLOAD_STORE_PASSWORD='${{ secrets.KEYSTORE_PASSWORD }}' -PMYAPP_UPLOAD_KEY_PASSWORD='${{ secrets.KEYSTORE_KEY_PASSWORD }}'
# Push tag to GitHub if package.json version's tag is not tagged # Push tag to GitHub if package.json version's tag is not tagged
- name: Get package version - name: Get package version
@ -73,7 +66,7 @@ jobs:
run: | run: |
echo -e '\n### File MD5\n```' >> ./publish/changeLog.md echo -e '\n### File MD5\n```' >> ./publish/changeLog.md
cd android/app/build/outputs/apk/release cd android/app/build/outputs/apk/release
md5 *.apk >> ../../../../../../publish/changeLog.md md5sum *.apk >> ../../../../../../publish/changeLog.md
echo -e '```\n' >> ../../../../../../publish/changeLog.md echo -e '```\n' >> ../../../../../../publish/changeLog.md
- name: Release - name: Release

View File

@ -1 +0,0 @@
16

View File

@ -1 +1 @@
2.7.5 2.7.6

View File

@ -1,6 +1,6 @@
source 'https://rubygems.org' source 'https://rubygems.org'
# You may use http://rbenv.org/ or https://rvm.io/ to install and use this version # You may use http://rbenv.org/ or https://rvm.io/ to install and use this version
ruby '2.7.5' ruby File.read(File.join(__dir__, '.ruby-version')).strip
gem 'cocoapods', '~> 1.11', '>= 1.11.2' gem 'cocoapods', '~> 1.11', '>= 1.11.3'

View File

@ -46,7 +46,7 @@
#### 项目启动步骤 #### 项目启动步骤
1. 启动模拟器或连接真实设备 1. 启动模拟器或连接真实设备
2. **启动开发服务器:** 在项目根目录打开命令行,执行命令:`npm run ar`,若开发服务器意外停止了,可以执行`npm start`重新启动 2. **启动开发服务器:** 在项目根目录打开命令行,执行命令:`npm run dev`,若开发服务器意外停止了,可以执行`npm start`重新启动
3. **开发:** 修改项目下的JS即可实时看到修改后的效果 3. **开发:** 修改项目下的JS即可实时看到修改后的效果
#### Native开发 #### Native开发

1
_node-version Normal file
View File

@ -0,0 +1 @@
18

View File

@ -1,55 +0,0 @@
# To learn about Buck see [Docs](https://buckbuild.com/).
# To run your application with Buck:
# - install Buck
# - `npm start` - to start the packager
# - `cd android`
# - `keytool -genkey -v -keystore keystores/debug.keystore -storepass android -alias androiddebugkey -keypass android -dname "CN=Android Debug,O=Android,C=US"`
# - `./gradlew :app:copyDownloadableDepsToLibs` - make all Gradle compile dependencies available to Buck
# - `buck install -r android/app` - compile, install and run application
#
load(":build_defs.bzl", "create_aar_targets", "create_jar_targets")
lib_deps = []
create_aar_targets(glob(["libs/*.aar"]))
create_jar_targets(glob(["libs/*.jar"]))
android_library(
name = "all-libs",
exported_deps = lib_deps,
)
android_library(
name = "app-code",
srcs = glob([
"src/main/java/**/*.java",
]),
deps = [
":all-libs",
":build_config",
":res",
],
)
android_build_config(
name = "build_config",
package = "cn.toside.music.mobile",
)
android_resource(
name = "res",
package = "cn.toside.music.mobile",
res = "src/main/res",
)
android_binary(
name = "app",
keystore = "//android/keystores:debug",
manifest = "src/main/AndroidManifest.xml",
package_type = "debug",
deps = [
":app-code",
],
)

View File

@ -1,129 +1,89 @@
apply plugin: "com.android.application" apply plugin: "com.android.application"
apply plugin: "com.facebook.react"
import com.android.build.OutputFile import com.android.build.OutputFile
import groovy.json.JsonSlurper import groovy.json.JsonSlurper
import org.apache.tools.ant.taskdefs.condition.Os import org.apache.tools.ant.taskdefs.condition.Os
/** /**
* The react.gradle file registers a task for each build variant (e.g. bundleDebugJsAndAssets * This is the configuration block to customize your React Native Android app.
* and bundleReleaseJsAndAssets). * By default you don't need to apply any configuration, just uncomment the lines you need.
* These basically call `react-native bundle` with the correct arguments during the Android build
* cycle. By default, bundleDebugJsAndAssets is skipped, as in debug/dev mode we prefer to load the
* bundle directly from the development server. Below you can see all the possible configurations
* and their defaults. If you decide to add a configuration block, make sure to add it before the
* `apply from: "../../node_modules/react-native/react.gradle"` line.
*
* project.ext.react = [
* // the name of the generated asset file containing your JS bundle
* bundleAssetName: "index.android.bundle",
*
* // the entry file for bundle generation. If none specified and
* // "index.android.js" exists, it will be used. Otherwise "index.js" is
* // default. Can be overridden with ENTRY_FILE environment variable.
* entryFile: "index.android.js",
*
* // https://reactnative.dev/docs/performance#enable-the-ram-format
* bundleCommand: "ram-bundle",
*
* // whether to bundle JS and assets in debug mode
* bundleInDebug: false,
*
* // whether to bundle JS and assets in release mode
* bundleInRelease: true,
*
* // whether to bundle JS and assets in another build variant (if configured).
* // See http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Build-Variants
* // The configuration property can be in the following formats
* // 'bundleIn${productFlavor}${buildType}'
* // 'bundleIn${buildType}'
* // bundleInFreeDebug: true,
* // bundleInPaidRelease: true,
* // bundleInBeta: true,
*
* // whether to disable dev mode in custom build variants (by default only disabled in release)
* // for example: to disable dev mode in the staging build type (if configured)
* devDisabledInStaging: true,
* // The configuration property can be in the following formats
* // 'devDisabledIn${productFlavor}${buildType}'
* // 'devDisabledIn${buildType}'
*
* // the root of your project, i.e. where "package.json" lives
* root: "../../",
*
* // where to put the JS bundle asset in debug mode
* jsBundleDirDebug: "$buildDir/intermediates/assets/debug",
*
* // where to put the JS bundle asset in release mode
* jsBundleDirRelease: "$buildDir/intermediates/assets/release",
*
* // where to put drawable resources / React Native assets, e.g. the ones you use via
* // require('./image.png')), in debug mode
* resourcesDirDebug: "$buildDir/intermediates/res/merged/debug",
*
* // where to put drawable resources / React Native assets, e.g. the ones you use via
* // require('./image.png')), in release mode
* resourcesDirRelease: "$buildDir/intermediates/res/merged/release",
*
* // by default the gradle tasks are skipped if none of the JS files or assets change; this means
* // that we don't look at files in android/ or ios/ to determine whether the tasks are up to
* // date; if you have any other folders that you want to ignore for performance reasons (gradle
* // indexes the entire tree), add them here. Alternatively, if you have JS files in android/
* // for example, you might want to remove it from here.
* inputExcludes: ["android/**", "ios/**"],
*
* // override which node gets called and with what additional arguments
* nodeExecutableAndArgs: ["node"],
*
* // supply additional arguments to the packager
* extraPackagerArgs: []
* ]
*/ */
react {
/* Folders */
// The root of your project, i.e. where "package.json" lives. Default is '..'
// root = file("../")
// The folder where the react-native NPM package is. Default is ../node_modules/react-native
// reactNativeDir = file("../node_modules/react-native")
// The folder where the react-native Codegen package is. Default is ../node_modules/react-native-codegen
// codegenDir = file("../node_modules/react-native-codegen")
// The cli.js file which is the React Native CLI entrypoint. Default is ../node_modules/react-native/cli.js
// cliFile = file("../node_modules/react-native/cli.js")
project.ext.react = [ /* Variants */
enableHermes: true, // clean and rebuild if changing // The list of variants to that are debuggable. For those we're going to
] // skip the bundling of the JS bundle and the assets. By default is just 'debug'.
// If you add flavors like lite, prod, etc. you'll have to list your debuggableVariants.
// debuggableVariants = ["liteDebug", "prodDebug"]
apply from: "../../node_modules/react-native/react.gradle" /* Bundling */
// A list containing the node command and its flags. Default is just 'node'.
// nodeExecutableAndArgs = ["node"]
//
// The command to run when bundling. By default is 'bundle'
// bundleCommand = "ram-bundle"
//
// The path to the CLI configuration file. Default is empty.
// bundleConfig = file(../rn-cli.config.js)
//
// The name of the generated asset file containing your JS bundle
// bundleAssetName = "MyApplication.android.bundle"
//
// The entry file for bundle generation. Default is 'index.android.js' or 'index.js'
// entryFile = file("../js/MyApplication.android.js")
//
// A list of extra flags to pass to the 'bundle' commands.
// See https://github.com/react-native-community/cli/blob/main/docs/commands.md#bundle
// extraPackagerArgs = []
/* Hermes Commands */
// The hermes compiler command to run. By default it is 'hermesc'
// hermesCommand = "$rootDir/my-custom-hermesc/bin/hermesc"
//
// The list of flags to pass to the Hermes compiler. By default is "-O", "-output-source-map"
// hermesFlags = ["-O", "-output-source-map"]
}
/** /**
* Set this to true to create two separate APKs instead of one: * Set this to true to create four separate APKs instead of one,
* - An APK that only works on ARM devices * one for each native architecture. This is useful if you don't
* - An APK that only works on x86 devices * use App Bundles (https://developer.android.com/guide/app-bundle/)
* The advantage is the size of the APK is reduced by about 4MB. * and want to have separate APKs to upload to the Play Store.
* Upload all the APKs to the Play Store and people will download
* the correct one based on the CPU architecture of their device.
*/ */
def enableSeparateBuildPerCPUArchitecture = true def enableSeparateBuildPerCPUArchitecture = true
/** /**
* Run Proguard to shrink the Java bytecode in release builds. * Set this to true to Run Proguard on Release builds to minify the Java bytecode.
*/ */
def enableProguardInReleaseBuilds = true def enableProguardInReleaseBuilds = true
/** /**
* The preferred build flavor of JavaScriptCore. * The preferred build flavor of JavaScriptCore (JSC)
* *
* For example, to use the international variant, you can use: * For example, to use the international variant, you can use:
* `def jscFlavor = 'org.webkit:android-jsc-intl:+'` * `def jscFlavor = 'org.webkit:android-jsc-intl:+'`
* *
* The international variant includes ICU i18n library and necessary data * The international variant includes ICU i18n library and necessary data
* allowing to use e.g. `Date.toLocaleString` and `String.localeCompare` that * allowing to use e.g. `Date.toLocaleString` and `String.localeCompare` that
* give correct results when using with locales other than en-US. Note that * give correct results when using with locales other than en-US. Note that
* this variant is about 6MiB larger per architecture than default. * this variant is about 6MiB larger per architecture than default.
*/ */
def jscFlavor = 'org.webkit:android-jsc:+' def jscFlavor = 'org.webkit:android-jsc:+'
/** /**
* Whether to enable the Hermes VM. * Private function to get the list of Native Architectures you want to build.
* * This reads the value from reactNativeArchitectures in your gradle.properties
* This should be set on project.ext.react and that value will be read here. If it is not set * file and works together with the --active-arch-only flag of react-native run-android.
* on project.ext.react, JavaScript will not be compiled to Hermes Bytecode
* and the benefits of using Hermes will therefore be sharply reduced.
*/
def enableHermes = project.ext.react.get("enableHermes", false);
/**
* Architectures to build native code for.
*/ */
def reactNativeArchitectures() { def reactNativeArchitectures() {
def value = project.getProperties().get("reactNativeArchitectures") def value = project.getProperties().get("reactNativeArchitectures")
@ -146,72 +106,13 @@ android {
compileSdkVersion rootProject.ext.compileSdkVersion compileSdkVersion rootProject.ext.compileSdkVersion
namespace "cn.toside.music.mobile"
defaultConfig { defaultConfig {
applicationId "cn.toside.music.mobile" applicationId "cn.toside.music.mobile"
minSdkVersion rootProject.ext.minSdkVersion minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion
versionCode verCode versionCode verCode
versionName verName versionName verName
buildConfigField "boolean", "IS_NEW_ARCHITECTURE_ENABLED", isNewArchitectureEnabled().toString()
if (isNewArchitectureEnabled()) {
// We configure the CMake build only if you decide to opt-in for the New Architecture.
externalNativeBuild {
cmake {
arguments "-DPROJECT_BUILD_DIR=$buildDir",
"-DREACT_ANDROID_DIR=$rootDir/../node_modules/react-native/ReactAndroid",
"-DREACT_ANDROID_BUILD_DIR=$rootDir/../node_modules/react-native/ReactAndroid/build",
"-DNODE_MODULES_DIR=$rootDir/../node_modules",
"-DANDROID_STL=c++_shared"
}
}
if (!enableSeparateBuildPerCPUArchitecture) {
ndk {
abiFilters (*reactNativeArchitectures())
}
}
}
}
if (isNewArchitectureEnabled()) {
// We configure the NDK build only if you decide to opt-in for the New Architecture.
externalNativeBuild {
cmake {
path "$projectDir/src/main/jni/CMakeLists.txt"
}
}
def reactAndroidProjectDir = project(':ReactAndroid').projectDir
def packageReactNdkDebugLibs = tasks.register("packageReactNdkDebugLibs", Copy) {
dependsOn(":ReactAndroid:packageReactNdkDebugLibsForBuck")
from("$reactAndroidProjectDir/src/main/jni/prebuilt/lib")
into("$buildDir/react-ndk/exported")
}
def packageReactNdkReleaseLibs = tasks.register("packageReactNdkReleaseLibs", Copy) {
dependsOn(":ReactAndroid:packageReactNdkReleaseLibsForBuck")
from("$reactAndroidProjectDir/src/main/jni/prebuilt/lib")
into("$buildDir/react-ndk/exported")
}
afterEvaluate {
// If you wish to add a custom TurboModule or component locally,
// you should uncomment this line.
// preBuild.dependsOn("generateCodegenArtifactsFromSchema")
preDebugBuild.dependsOn(packageReactNdkDebugLibs)
preReleaseBuild.dependsOn(packageReactNdkReleaseLibs)
// Due to a bug inside AGP, we have to explicitly set a dependency
// between configureCMakeDebug* tasks and the preBuild tasks.
// This can be removed once this is solved: https://issuetracker.google.com/issues/207403732
configureCMakeRelWithDebInfo.dependsOn(preReleaseBuild)
configureCMakeDebug.dependsOn(preDebugBuild)
reactNativeArchitectures().each { architecture ->
tasks.findByName("configureCMakeDebug[${architecture}]")?.configure {
dependsOn("preDebugBuild")
}
tasks.findByName("configureCMakeRelWithDebInfo[${architecture}]")?.configure {
dependsOn("preReleaseBuild")
}
}
}
} }
splits { splits {
@ -285,67 +186,22 @@ android {
} }
dependencies { dependencies {
implementation fileTree(dir: "libs", include: ["*.jar"]) // The version of react-native is set by the React Native Gradle Plugin
implementation("com.facebook.react:react-android")
//noinspection GradleDynamicVersion implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.0.0")
implementation "com.facebook.react:react-native:+" // From node_modules
implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.0.0"
debugImplementation("com.facebook.flipper:flipper:${FLIPPER_VERSION}") {
exclude group:'com.facebook.fbjni'
}
debugImplementation("com.facebook.flipper:flipper:${FLIPPER_VERSION}")
debugImplementation("com.facebook.flipper:flipper-network-plugin:${FLIPPER_VERSION}") { debugImplementation("com.facebook.flipper:flipper-network-plugin:${FLIPPER_VERSION}") {
exclude group:'com.facebook.flipper'
exclude group:'com.squareup.okhttp3', module:'okhttp' exclude group:'com.squareup.okhttp3', module:'okhttp'
} }
debugImplementation("com.facebook.flipper:flipper-fresco-plugin:${FLIPPER_VERSION}") { debugImplementation("com.facebook.flipper:flipper-fresco-plugin:${FLIPPER_VERSION}")
exclude group:'com.facebook.flipper' if (hermesEnabled.toBoolean()) {
} implementation("com.facebook.react:hermes-android")
if (enableHermes) {
//noinspection GradleDynamicVersion
implementation("com.facebook.react:hermes-engine:+") { // From node_modules
exclude group:'com.facebook.fbjni'
}
} else { } else {
implementation jscFlavor implementation jscFlavor
} }
} }
if (isNewArchitectureEnabled()) {
// If new architecture is enabled, we let you build RN from source
// Otherwise we fallback to a prebuilt .aar bundled in the NPM package.
// This will be applied to all the imported transtitive dependency.
configurations.all {
resolutionStrategy.dependencySubstitution {
substitute(module("com.facebook.react:react-native"))
.using(project(":ReactAndroid"))
.because("On New Architecture we're building React Native from source")
substitute(module("com.facebook.react:hermes-engine"))
.using(project(":ReactAndroid:hermes-engine"))
.because("On New Architecture we're building Hermes from source")
}
}
}
// Run this once to be able to run the application with BUCK
// puts all compile dependencies into folder libs for BUCK to use
task copyDownloadableDepsToLibs(type: Copy) {
from configurations.implementation
into 'libs'
}
apply from: file("../../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesAppBuildGradle(project) apply from: file("../../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesAppBuildGradle(project)
def isNewArchitectureEnabled() {
// To opt-in for the New Architecture, you can either:
// - Set `newArchEnabled` to true inside the `gradle.properties` file
// - Invoke gradle with `-newArchEnabled=true`
// - Set an environment variable `ORG_GRADLE_PROJECT_newArchEnabled=true`
return project.hasProperty("newArchEnabled") && project.newArchEnabled == "true"
}
apply from: "../../node_modules/react-native-vector-icons/fonts.gradle"

View File

@ -1,19 +0,0 @@
"""Helper definitions to glob .aar and .jar targets"""
def create_aar_targets(aarfiles):
for aarfile in aarfiles:
name = "aars__" + aarfile[aarfile.rindex("/") + 1:aarfile.rindex(".aar")]
lib_deps.append(":" + name)
android_prebuilt_aar(
name = name,
aar = aarfile,
)
def create_jar_targets(jarfiles):
for jarfile in jarfiles:
name = "jars__" + jarfile[jarfile.rindex("/") + 1:jarfile.rindex(".jar")]
lib_deps.append(":" + name)
prebuilt_jar(
name = name,
binary_jar = jarfile,
)

View File

@ -8,6 +8,3 @@
# http://developer.android.com/guide/developing/tools/proguard.html # http://developer.android.com/guide/developing/tools/proguard.html
# Add any project specific keep options here: # Add any project specific keep options here:
# -keep class com.facebook.hermes.unicode.** { *; }
# -keep class com.facebook.jni.** { *; }

View File

@ -17,7 +17,6 @@ import com.facebook.flipper.plugins.inspector.DescriptorMapping;
import com.facebook.flipper.plugins.inspector.InspectorFlipperPlugin; import com.facebook.flipper.plugins.inspector.InspectorFlipperPlugin;
import com.facebook.flipper.plugins.network.FlipperOkhttpInterceptor; import com.facebook.flipper.plugins.network.FlipperOkhttpInterceptor;
import com.facebook.flipper.plugins.network.NetworkFlipperPlugin; import com.facebook.flipper.plugins.network.NetworkFlipperPlugin;
import com.facebook.flipper.plugins.react.ReactFlipperPlugin;
import com.facebook.flipper.plugins.sharedpreferences.SharedPreferencesFlipperPlugin; import com.facebook.flipper.plugins.sharedpreferences.SharedPreferencesFlipperPlugin;
import com.facebook.react.ReactInstanceEventListener; import com.facebook.react.ReactInstanceEventListener;
import com.facebook.react.ReactInstanceManager; import com.facebook.react.ReactInstanceManager;
@ -25,13 +24,16 @@ import com.facebook.react.bridge.ReactContext;
import com.facebook.react.modules.network.NetworkingModule; import com.facebook.react.modules.network.NetworkingModule;
import okhttp3.OkHttpClient; import okhttp3.OkHttpClient;
/**
* 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.
*/
public class ReactNativeFlipper { public class ReactNativeFlipper {
public static void initializeFlipper(Context context, ReactInstanceManager reactInstanceManager) { public static void initializeFlipper(Context context, ReactInstanceManager reactInstanceManager) {
if (FlipperUtils.shouldEnableFlipper(context)) { if (FlipperUtils.shouldEnableFlipper(context)) {
final FlipperClient client = AndroidFlipperClient.getInstance(context); final FlipperClient client = AndroidFlipperClient.getInstance(context);
client.addPlugin(new InspectorFlipperPlugin(context, DescriptorMapping.withDefaults())); client.addPlugin(new InspectorFlipperPlugin(context, DescriptorMapping.withDefaults()));
client.addPlugin(new ReactFlipperPlugin());
client.addPlugin(new DatabasesFlipperPlugin(context)); client.addPlugin(new DatabasesFlipperPlugin(context));
client.addPlugin(new SharedPreferencesFlipperPlugin(context)); client.addPlugin(new SharedPreferencesFlipperPlugin(context));
client.addPlugin(CrashReporterPlugin.getInstance()); client.addPlugin(CrashReporterPlugin.getInstance());

View File

@ -1,15 +1,12 @@
package cn.toside.music.mobile; package cn.toside.music.mobile;
import com.facebook.react.ReactRootView;
import com.reactnativenavigation.NavigationActivity; import com.reactnativenavigation.NavigationActivity;
import com.facebook.react.ReactActivityDelegate; import com.facebook.react.ReactActivityDelegate;
import com.facebook.react.ReactRootView; import com.facebook.react.defaults.DefaultReactActivityDelegate;
public class MainActivity extends NavigationActivity { public class MainActivity extends NavigationActivity {
public static class MainActivityDelegate extends ReactActivityDelegate { public static class MainActivityDelegate extends ReactActivityDelegate {
public MainActivityDelegate(NavigationActivity activity, String mainComponentName) { public MainActivityDelegate(NavigationActivity activity, String mainComponentName) {
super(activity, mainComponentName); super(activity, mainComponentName);
@ -30,4 +27,21 @@ public class MainActivity extends NavigationActivity {
return BuildConfig.IS_NEW_ARCHITECTURE_ENABLED; return BuildConfig.IS_NEW_ARCHITECTURE_ENABLED;
} }
} }
/**
* Returns the instance of the {@link ReactActivityDelegate}. Here we use a util class {@link
* DefaultReactActivityDelegate} which allows you to easily enable Fabric and Concurrent React
* (aka React 18) with two boolean flags.
*/
// @Override
// protected ReactActivityDelegate createReactActivityDelegate() {
// return new DefaultReactActivityDelegate(
// this,
// getMainComponentName(),
// // If you opted-in for the New Architecture, we enable the Fabric Renderer.
// DefaultNewArchitectureEntryPoint.getFabricEnabled(), // fabricEnabled
// // If you opted-in for the New Architecture, we enable Concurrent React (i.e. React 18).
// DefaultNewArchitectureEntryPoint.getConcurrentReactEnabled() // concurrentRootEnabled
// );
// }
} }

View File

@ -1,17 +1,13 @@
package cn.toside.music.mobile; package cn.toside.music.mobile;
import android.app.Application; import android.app.Application;
import android.content.Context;
import com.facebook.react.PackageList; import com.facebook.react.PackageList;
import com.reactnativenavigation.NavigationApplication; import com.reactnativenavigation.NavigationApplication;
import com.facebook.react.ReactInstanceManager;
import com.facebook.react.ReactNativeHost; import com.facebook.react.ReactNativeHost;
import com.reactnativenavigation.react.NavigationReactNativeHost;
import com.facebook.react.ReactPackage; import com.facebook.react.ReactPackage;
import com.facebook.react.config.ReactFeatureFlags; import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint;
import com.facebook.react.defaults.DefaultReactNativeHost;
import com.facebook.soloader.SoLoader; import com.facebook.soloader.SoLoader;
import cn.toside.music.mobile.newarchitecture.MainApplicationReactNativeHost;
import java.lang.reflect.InvocationTargetException;
import java.util.List; import java.util.List;
import cn.toside.music.mobile.cache.CachePackage; import cn.toside.music.mobile.cache.CachePackage;
@ -22,7 +18,7 @@ import cn.toside.music.mobile.utils.UtilsPackage;
public class MainApplication extends NavigationApplication { public class MainApplication extends NavigationApplication {
private final ReactNativeHost mReactNativeHost = private final ReactNativeHost mReactNativeHost =
new NavigationReactNativeHost(this) { new DefaultReactNativeHost(this) {
@Override @Override
public boolean getUseDeveloperSupport() { public boolean getUseDeveloperSupport() {
return BuildConfig.DEBUG; return BuildConfig.DEBUG;
@ -45,57 +41,31 @@ public class MainApplication extends NavigationApplication {
protected String getJSMainModuleName() { protected String getJSMainModuleName() {
return "index"; return "index";
} }
};
private final ReactNativeHost mNewArchitectureNativeHost = @Override
new MainApplicationReactNativeHost(this); protected boolean isNewArchEnabled() {
return BuildConfig.IS_NEW_ARCHITECTURE_ENABLED;
}
@Override
protected Boolean isHermesEnabled() {
return BuildConfig.IS_HERMES_ENABLED;
}
};
@Override @Override
public ReactNativeHost getReactNativeHost() { public ReactNativeHost getReactNativeHost() {
if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) { return mReactNativeHost;
return mNewArchitectureNativeHost;
} else {
return mReactNativeHost;
}
} }
@Override @Override
public void onCreate() { public void onCreate() {
super.onCreate(); super.onCreate();
// If you opted-in for the New Architecture, we enable the TurboModule system
ReactFeatureFlags.useTurboModules = BuildConfig.IS_NEW_ARCHITECTURE_ENABLED;
initializeFlipper(this, getReactNativeHost().getReactInstanceManager()); if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) {
} // If you opted-in for the New Architecture, we load the native entry point for this app.
DefaultNewArchitectureEntryPoint.load();
/**
* Loads Flipper in React Native templates. Call this in the onCreate method with something like
* initializeFlipper(this, getReactNativeHost().getReactInstanceManager());
*
* @param context
* @param reactInstanceManager
*/
private static void initializeFlipper(
Context context, ReactInstanceManager reactInstanceManager) {
if (BuildConfig.DEBUG) {
try {
/*
We use reflection here to pick up the class that initializes Flipper,
since Flipper library is not available in release mode
*/
Class<?> aClass = Class.forName("cn.toside.music.mobile.ReactNativeFlipper");
aClass
.getMethod("initializeFlipper", Context.class, ReactInstanceManager.class)
.invoke(null, context, reactInstanceManager);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
} }
ReactNativeFlipper.initializeFlipper(this, getReactNativeHost().getReactInstanceManager());
} }
} }

View File

@ -1,116 +0,0 @@
package cn.toside.music.mobile.newarchitecture;
import android.app.Application;
import androidx.annotation.NonNull;
import com.facebook.react.PackageList;
import com.facebook.react.ReactInstanceManager;
import com.facebook.react.ReactNativeHost;
import com.facebook.react.ReactPackage;
import com.facebook.react.ReactPackageTurboModuleManagerDelegate;
import com.facebook.react.bridge.JSIModulePackage;
import com.facebook.react.bridge.JSIModuleProvider;
import com.facebook.react.bridge.JSIModuleSpec;
import com.facebook.react.bridge.JSIModuleType;
import com.facebook.react.bridge.JavaScriptContextHolder;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.UIManager;
import com.facebook.react.fabric.ComponentFactory;
import com.facebook.react.fabric.CoreComponentsRegistry;
import com.facebook.react.fabric.FabricJSIModuleProvider;
import com.facebook.react.fabric.ReactNativeConfig;
import com.facebook.react.uimanager.ViewManagerRegistry;
import cn.toside.music.mobile.BuildConfig;
import cn.toside.music.mobile.newarchitecture.components.MainComponentsRegistry;
import cn.toside.music.mobile.newarchitecture.modules.MainApplicationTurboModuleManagerDelegate;
import java.util.ArrayList;
import java.util.List;
/**
* A {@link ReactNativeHost} that helps you load everything needed for the New Architecture, both
* TurboModule delegates and the Fabric Renderer.
*
* <p>Please note that this class is used ONLY if you opt-in for the New Architecture (see the
* `newArchEnabled` property). Is ignored otherwise.
*/
public class MainApplicationReactNativeHost extends ReactNativeHost {
public MainApplicationReactNativeHost(Application application) {
super(application);
}
@Override
public boolean getUseDeveloperSupport() {
return BuildConfig.DEBUG;
}
@Override
protected List<ReactPackage> getPackages() {
List<ReactPackage> packages = new PackageList(this).getPackages();
// Packages that cannot be autolinked yet can be added manually here, for example:
// packages.add(new MyReactNativePackage());
// TurboModules must also be loaded here providing a valid TurboReactPackage implementation:
// packages.add(new TurboReactPackage() { ... });
// If you have custom Fabric Components, their ViewManagers should also be loaded here
// inside a ReactPackage.
return packages;
}
@Override
protected String getJSMainModuleName() {
return "index";
}
@NonNull
@Override
protected ReactPackageTurboModuleManagerDelegate.Builder
getReactPackageTurboModuleManagerDelegateBuilder() {
// Here we provide the ReactPackageTurboModuleManagerDelegate Builder. This is necessary
// for the new architecture and to use TurboModules correctly.
return new MainApplicationTurboModuleManagerDelegate.Builder();
}
@Override
protected JSIModulePackage getJSIModulePackage() {
return new JSIModulePackage() {
@Override
public List<JSIModuleSpec> getJSIModules(
final ReactApplicationContext reactApplicationContext,
final JavaScriptContextHolder jsContext) {
final List<JSIModuleSpec> specs = new ArrayList<>();
// Here we provide a new JSIModuleSpec that will be responsible of providing the
// custom Fabric Components.
specs.add(
new JSIModuleSpec() {
@Override
public JSIModuleType getJSIModuleType() {
return JSIModuleType.UIManager;
}
@Override
public JSIModuleProvider<UIManager> getJSIModuleProvider() {
final ComponentFactory componentFactory = new ComponentFactory();
CoreComponentsRegistry.register(componentFactory);
// Here we register a Components Registry.
// The one that is generated with the template contains no components
// and just provides you the one from React Native core.
MainComponentsRegistry.register(componentFactory);
final ReactInstanceManager reactInstanceManager = getReactInstanceManager();
ViewManagerRegistry viewManagerRegistry =
new ViewManagerRegistry(
reactInstanceManager.getOrCreateViewManagers(reactApplicationContext));
return new FabricJSIModuleProvider(
reactApplicationContext,
componentFactory,
ReactNativeConfig.DEFAULT_CONFIG,
viewManagerRegistry);
}
});
return specs;
}
};
}
}

View File

@ -1,36 +0,0 @@
package cn.toside.music.mobile.newarchitecture.components;
import com.facebook.jni.HybridData;
import com.facebook.proguard.annotations.DoNotStrip;
import com.facebook.react.fabric.ComponentFactory;
import com.facebook.soloader.SoLoader;
/**
* Class responsible to load the custom Fabric Components. This class has native methods and needs a
* corresponding C++ implementation/header file to work correctly (already placed inside the jni/
* folder for you).
*
* <p>Please note that this class is used ONLY if you opt-in for the New Architecture (see the
* `newArchEnabled` property). Is ignored otherwise.
*/
@DoNotStrip
public class MainComponentsRegistry {
static {
SoLoader.loadLibrary("fabricjni");
}
@DoNotStrip private final HybridData mHybridData;
@DoNotStrip
private native HybridData initHybrid(ComponentFactory componentFactory);
@DoNotStrip
private MainComponentsRegistry(ComponentFactory componentFactory) {
mHybridData = initHybrid(componentFactory);
}
@DoNotStrip
public static MainComponentsRegistry register(ComponentFactory componentFactory) {
return new MainComponentsRegistry(componentFactory);
}
}

View File

@ -1,48 +0,0 @@
package cn.toside.music.mobile.newarchitecture.modules;
import com.facebook.jni.HybridData;
import com.facebook.react.ReactPackage;
import com.facebook.react.ReactPackageTurboModuleManagerDelegate;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.soloader.SoLoader;
import java.util.List;
/**
* Class responsible to load the TurboModules. This class has native methods and needs a
* corresponding C++ implementation/header file to work correctly (already placed inside the jni/
* folder for you).
*
* <p>Please note that this class is used ONLY if you opt-in for the New Architecture (see the
* `newArchEnabled` property). Is ignored otherwise.
*/
public class MainApplicationTurboModuleManagerDelegate
extends ReactPackageTurboModuleManagerDelegate {
private static volatile boolean sIsSoLibraryLoaded;
protected MainApplicationTurboModuleManagerDelegate(
ReactApplicationContext reactApplicationContext, List<ReactPackage> packages) {
super(reactApplicationContext, packages);
}
protected native HybridData initHybrid();
native boolean canCreateTurboModule(String moduleName);
public static class Builder extends ReactPackageTurboModuleManagerDelegate.Builder {
protected MainApplicationTurboModuleManagerDelegate build(
ReactApplicationContext context, List<ReactPackage> packages) {
return new MainApplicationTurboModuleManagerDelegate(context, packages);
}
}
@Override
protected synchronized void maybeLoadOtherSoLibraries() {
if (!sIsSoLibraryLoaded) {
// If you change the name of your application .so file in the Android.mk file,
// make sure you update the name here as well.
SoLoader.loadLibrary("cn_toside_music_mobile_appmodules");
sIsSoLibraryLoaded = true;
}
}
}

View File

@ -13,8 +13,10 @@ import android.os.Build;
import android.util.Log; import android.util.Log;
import android.view.WindowManager; import android.view.WindowManager;
import androidx.core.app.LocaleManagerCompat;
import androidx.core.app.NotificationManagerCompat; import androidx.core.app.NotificationManagerCompat;
import androidx.core.content.FileProvider; import androidx.core.content.FileProvider;
import androidx.core.os.LocaleListCompat;
import com.facebook.react.bridge.Promise; import com.facebook.react.bridge.Promise;
import com.facebook.react.bridge.ReactApplicationContext; import com.facebook.react.bridge.ReactApplicationContext;
@ -25,6 +27,7 @@ import com.facebook.react.bridge.WritableNativeArray;
import java.io.File; import java.io.File;
import java.util.List; import java.util.List;
import java.util.Locale;
import java.util.Objects; import java.util.Objects;
public class UtilsModule extends ReactContextBaseJavaModule { public class UtilsModule extends ReactContextBaseJavaModule {
@ -252,5 +255,25 @@ public class UtilsModule extends ReactContextBaseJavaModule {
promise.reject("-2", err.getMessage()); promise.reject("-2", err.getMessage());
} }
} }
// https://stackoverflow.com/questions/73463341/in-per-app-language-how-to-get-app-locale-in-api-33-if-system-locale-is-diffe
@ReactMethod
public void getSystemLocales(Promise promise) {
Locale locale = null;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
LocaleListCompat list = LocaleManagerCompat.getSystemLocales(reactContext);
if (list.size() > 0) {
locale = list.get(0);
} else promise.resolve(null);
} else {
locale = Locale.getDefault();
}
if (locale == null) {
promise.resolve("");
} else {
promise.resolve(locale.toString());
}
}
} }

View File

@ -1,7 +0,0 @@
cmake_minimum_required(VERSION 3.13)
# Define the library name here.
project(cn_toside_music_mobile_appmodules)
# This file includes all the necessary to let you build your application with the New Architecture.
include(${REACT_ANDROID_DIR}/cmake-utils/ReactNative-application.cmake)

View File

@ -1,32 +0,0 @@
#include "MainApplicationModuleProvider.h"
#include <rncli.h>
#include <rncore.h>
namespace facebook {
namespace react {
std::shared_ptr<TurboModule> MainApplicationModuleProvider(
const std::string &moduleName,
const JavaTurboModule::InitParams &params) {
// Here you can provide your own module provider for TurboModules coming from
// either your application or from external libraries. The approach to follow
// is similar to the following (for a library called `samplelibrary`:
//
// auto module = samplelibrary_ModuleProvider(moduleName, params);
// if (module != nullptr) {
// return module;
// }
// return rncore_ModuleProvider(moduleName, params);
// Module providers autolinked by RN CLI
auto rncli_module = rncli_ModuleProvider(moduleName, params);
if (rncli_module != nullptr) {
return rncli_module;
}
return rncore_ModuleProvider(moduleName, params);
}
} // namespace react
} // namespace facebook

View File

@ -1,16 +0,0 @@
#pragma once
#include <memory>
#include <string>
#include <ReactCommon/JavaTurboModule.h>
namespace facebook {
namespace react {
std::shared_ptr<TurboModule> MainApplicationModuleProvider(
const std::string &moduleName,
const JavaTurboModule::InitParams &params);
} // namespace react
} // namespace facebook

View File

@ -1,45 +0,0 @@
#include "MainApplicationTurboModuleManagerDelegate.h"
#include "MainApplicationModuleProvider.h"
namespace facebook {
namespace react {
jni::local_ref<MainApplicationTurboModuleManagerDelegate::jhybriddata>
MainApplicationTurboModuleManagerDelegate::initHybrid(
jni::alias_ref<jhybridobject>) {
return makeCxxInstance();
}
void MainApplicationTurboModuleManagerDelegate::registerNatives() {
registerHybrid({
makeNativeMethod(
"initHybrid", MainApplicationTurboModuleManagerDelegate::initHybrid),
makeNativeMethod(
"canCreateTurboModule",
MainApplicationTurboModuleManagerDelegate::canCreateTurboModule),
});
}
std::shared_ptr<TurboModule>
MainApplicationTurboModuleManagerDelegate::getTurboModule(
const std::string &name,
const std::shared_ptr<CallInvoker> &jsInvoker) {
// Not implemented yet: provide pure-C++ NativeModules here.
return nullptr;
}
std::shared_ptr<TurboModule>
MainApplicationTurboModuleManagerDelegate::getTurboModule(
const std::string &name,
const JavaTurboModule::InitParams &params) {
return MainApplicationModuleProvider(name, params);
}
bool MainApplicationTurboModuleManagerDelegate::canCreateTurboModule(
const std::string &name) {
return getTurboModule(name, nullptr) != nullptr ||
getTurboModule(name, {.moduleName = name}) != nullptr;
}
} // namespace react
} // namespace facebook

View File

@ -1,38 +0,0 @@
#include <memory>
#include <string>
#include <ReactCommon/TurboModuleManagerDelegate.h>
#include <fbjni/fbjni.h>
namespace facebook {
namespace react {
class MainApplicationTurboModuleManagerDelegate
: public jni::HybridClass<
MainApplicationTurboModuleManagerDelegate,
TurboModuleManagerDelegate> {
public:
// Adapt it to the package you used for your Java class.
static constexpr auto kJavaDescriptor =
"Lcn/toside/music/mobile/newarchitecture/modules/MainApplicationTurboModuleManagerDelegate;";
static jni::local_ref<jhybriddata> initHybrid(jni::alias_ref<jhybridobject>);
static void registerNatives();
std::shared_ptr<TurboModule> getTurboModule(
const std::string &name,
const std::shared_ptr<CallInvoker> &jsInvoker) override;
std::shared_ptr<TurboModule> getTurboModule(
const std::string &name,
const JavaTurboModule::InitParams &params) override;
/**
* Test-only method. Allows user to verify whether a TurboModule can be
* created by instances of this class.
*/
bool canCreateTurboModule(const std::string &name);
};
} // namespace react
} // namespace facebook

View File

@ -1,65 +0,0 @@
#include "MainComponentsRegistry.h"
#include <CoreComponentsRegistry.h>
#include <fbjni/fbjni.h>
#include <react/renderer/componentregistry/ComponentDescriptorProviderRegistry.h>
#include <react/renderer/components/rncore/ComponentDescriptors.h>
#include <rncli.h>
namespace facebook {
namespace react {
MainComponentsRegistry::MainComponentsRegistry(ComponentFactory *delegate) {}
std::shared_ptr<ComponentDescriptorProviderRegistry const>
MainComponentsRegistry::sharedProviderRegistry() {
auto providerRegistry = CoreComponentsRegistry::sharedProviderRegistry();
// Autolinked providers registered by RN CLI
rncli_registerProviders(providerRegistry);
// Custom Fabric Components go here. You can register custom
// components coming from your App or from 3rd party libraries here.
//
// providerRegistry->add(concreteComponentDescriptorProvider<
// AocViewerComponentDescriptor>());
return providerRegistry;
}
jni::local_ref<MainComponentsRegistry::jhybriddata>
MainComponentsRegistry::initHybrid(
jni::alias_ref<jclass>,
ComponentFactory *delegate) {
auto instance = makeCxxInstance(delegate);
auto buildRegistryFunction =
[](EventDispatcher::Weak const &eventDispatcher,
ContextContainer::Shared const &contextContainer)
-> ComponentDescriptorRegistry::Shared {
auto registry = MainComponentsRegistry::sharedProviderRegistry()
->createComponentDescriptorRegistry(
{eventDispatcher, contextContainer});
auto mutableRegistry =
std::const_pointer_cast<ComponentDescriptorRegistry>(registry);
mutableRegistry->setFallbackComponentDescriptor(
std::make_shared<UnimplementedNativeViewComponentDescriptor>(
ComponentDescriptorParameters{
eventDispatcher, contextContainer, nullptr}));
return registry;
};
delegate->buildRegistryFunction = buildRegistryFunction;
return instance;
}
void MainComponentsRegistry::registerNatives() {
registerHybrid({
makeNativeMethod("initHybrid", MainComponentsRegistry::initHybrid),
});
}
} // namespace react
} // namespace facebook

View File

@ -1,32 +0,0 @@
#pragma once
#include <ComponentFactory.h>
#include <fbjni/fbjni.h>
#include <react/renderer/componentregistry/ComponentDescriptorProviderRegistry.h>
#include <react/renderer/componentregistry/ComponentDescriptorRegistry.h>
namespace facebook {
namespace react {
class MainComponentsRegistry
: public facebook::jni::HybridClass<MainComponentsRegistry> {
public:
// Adapt it to the package you used for your Java class.
constexpr static auto kJavaDescriptor =
"Lcn/toside/music/mobile/newarchitecture/components/MainComponentsRegistry;";
static void registerNatives();
MainComponentsRegistry(ComponentFactory *delegate);
private:
static std::shared_ptr<ComponentDescriptorProviderRegistry const>
sharedProviderRegistry();
static jni::local_ref<jhybriddata> initHybrid(
jni::alias_ref<jclass>,
ComponentFactory *delegate);
};
} // namespace react
} // namespace facebook

View File

@ -1,11 +0,0 @@
#include <fbjni/fbjni.h>
#include "MainApplicationTurboModuleManagerDelegate.h"
#include "MainComponentsRegistry.h"
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *) {
return facebook::jni::initialize(vm, [] {
facebook::react::MainApplicationTurboModuleManagerDelegate::
registerNatives();
facebook::react::MainComponentsRegistry::registerNatives();
});
}

View File

@ -0,0 +1,20 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* <p>This source code is licensed under the MIT license found in the LICENSE file in the root
* directory of this source tree.
*/
package cn.toside.music.mobile;
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.
*/
public class ReactNativeFlipper {
public static void initializeFlipper(Context context, ReactInstanceManager reactInstanceManager) {
// Do nothing as we don't want to initialize Flipper on Release.
}
}

View File

@ -10,46 +10,16 @@ buildscript {
kotlinVersion = "1.6.10" // Or any version above 1.3.x kotlinVersion = "1.6.10" // Or any version above 1.3.x
RNNKotlinVersion = kotlinVersion RNNKotlinVersion = kotlinVersion
if (System.properties['os.arch'] == "aarch64") { // We use NDK 23 which has both M1 support and is the side-by-side NDK version from AGP.
// For M1 Users we need to use the NDK 24 which added support for aarch64 ndkVersion = "23.1.7779620"
ndkVersion = "24.0.8215888"
} else {
// Otherwise we default to the side-by-side NDK version from AGP.
ndkVersion = "21.4.7075529"
}
} }
repositories { repositories {
google() google()
mavenCentral() mavenCentral()
} }
dependencies { dependencies {
classpath("com.android.tools.build:gradle:7.2.2") classpath("com.android.tools.build:gradle:7.3.1")
classpath("com.facebook.react:react-native-gradle-plugin") classpath("com.facebook.react:react-native-gradle-plugin")
classpath("de.undercouch:gradle-download-task:5.0.1")
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion") classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion")
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
allprojects {
repositories {
maven {
// All of React Native (JS, Obj-C sources, Android binaries) is installed from npm
url("$rootDir/../node_modules/react-native/android")
}
maven {
// Android JSC is installed from npm
url("$rootDir/../node_modules/jsc-android/dist")
}
mavenCentral {
// We don't want to fetch react-native from Maven Central as there are
// older versions over there.
content {
excludeGroup "com.facebook.react"
}
}
google()
maven { url 'https://www.jitpack.io' }
} }
} }

View File

@ -37,7 +37,11 @@ reactNativeArchitectures=armeabi-v7a,arm64-v8a,x86,x86_64
# your application. You should enable this flag either if you want # your application. You should enable this flag either if you want
# to write custom TurboModules/Fabric components OR use libraries that # to write custom TurboModules/Fabric components OR use libraries that
# are providing them. # are providing them.
newArchEnabled=false newArchEnabled=true
# Use this property to enable or disable the Hermes JS engine.
# If set to false, you will be using JSC instead.
hermesEnabled=true
# org.gradle.daemon=true # org.gradle.daemon=true

View File

@ -2,10 +2,3 @@ rootProject.name = 'cn.toside.music.mobile'
apply from: file("../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesSettingsGradle(settings) apply from: file("../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesSettingsGradle(settings)
include ':app' include ':app'
includeBuild('../node_modules/react-native-gradle-plugin') includeBuild('../node_modules/react-native-gradle-plugin')
if (settings.hasProperty("newArchEnabled") && settings.newArchEnabled == "true") {
include(":ReactAndroid")
project(":ReactAndroid").projectDir = file('../node_modules/react-native/ReactAndroid')
include(":ReactAndroid:hermes-engine")
project(":ReactAndroid:hermes-engine").projectDir = file('../node_modules/react-native/ReactAndroid/hermes-engine')
}

View File

@ -492,6 +492,7 @@
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",
); );
MARKETING_VERSION = 1.0;
OTHER_LDFLAGS = ( OTHER_LDFLAGS = (
"$(inherited)", "$(inherited)",
"-ObjC", "-ObjC",
@ -517,6 +518,7 @@
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",
); );
MARKETING_VERSION = 1.0;
OTHER_LDFLAGS = ( OTHER_LDFLAGS = (
"$(inherited)", "$(inherited)",
"-ObjC", "-ObjC",

View File

@ -1,8 +1,6 @@
#import <React/RCTBridgeDelegate.h> #import <RCTAppDelegate.h>
#import <UIKit/UIKit.h> #import <UIKit/UIKit.h>
@interface AppDelegate : UIResponder <UIApplicationDelegate, RCTBridgeDelegate> @interface AppDelegate : RCTAppDelegate
@property (nonatomic, strong) UIWindow *window;
@end @end

View File

@ -1,76 +1,18 @@
#import "AppDelegate.h" #import "AppDelegate.h"
#import <ReactNativeNavigation/ReactNativeNavigation.h> #import <ReactNativeNavigation/ReactNativeNavigation.h>
#import <React/RCTBridge.h>
#import <React/RCTBundleURLProvider.h> #import <React/RCTBundleURLProvider.h>
#import <React/RCTAppSetupUtils.h>
#if RCT_NEW_ARCH_ENABLED
#import <React/CoreModulesPlugins.h>
#import <React/RCTCxxBridgeDelegate.h>
#import <React/RCTFabricSurfaceHostingProxyRootView.h>
#import <React/RCTSurfacePresenter.h>
#import <React/RCTSurfacePresenterBridgeAdapter.h>
#import <ReactCommon/RCTTurboModuleManager.h>
#import <react/config/ReactNativeConfig.h>
static NSString *const kRNConcurrentRoot = @"concurrentRoot";
@interface AppDelegate () <RCTCxxBridgeDelegate, RCTTurboModuleManagerDelegate> {
RCTTurboModuleManager *_turboModuleManager;
RCTSurfacePresenterBridgeAdapter *_bridgeAdapter;
std::shared_ptr<const facebook::react::ReactNativeConfig> _reactNativeConfig;
facebook::react::ContextContainer::Shared _contextContainer;
}
@end
#endif
@implementation AppDelegate @implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{ {
RCTAppSetupPrepareApp(application); self.moduleName = @"LxMusicMobile";
// You can add your custom initial props in the dictionary below.
// They will be passed down to the ViewController used by React Native.
self.initialProps = @{};
RCTBridge *bridge = [[RCTBridge alloc] initWithDelegate:self launchOptions:launchOptions]; return [super application:application didFinishLaunchingWithOptions:launchOptions];
[ReactNativeNavigation bootstrapWithBridge:bridge];
#if RCT_NEW_ARCH_ENABLED
_contextContainer = std::make_shared<facebook::react::ContextContainer const>();
_reactNativeConfig = std::make_shared<facebook::react::EmptyReactNativeConfig const>();
_contextContainer->insert("ReactNativeConfig", _reactNativeConfig);
_bridgeAdapter = [[RCTSurfacePresenterBridgeAdapter alloc] initWithBridge:bridge contextContainer:_contextContainer];
bridge.surfacePresenter = _bridgeAdapter.surfacePresenter;
#endif
return YES;
}
/// This method controls whether the `concurrentRoot`feature of React18 is turned on or off.
///
/// @see: https://reactjs.org/blog/2022/03/29/react-v18.html
/// @note: This requires to be rendering on Fabric (i.e. on the New Architecture).
/// @return: `true` if the `concurrentRoot` feture is enabled. Otherwise, it returns `false`.
- (BOOL)concurrentRootEnabled
{
// Switch this bool to turn on and off the concurrent root
return true;
}
- (NSDictionary *)prepareInitialProps
{
NSMutableDictionary *initProps = [NSMutableDictionary new];
#ifdef RCT_NEW_ARCH_ENABLED
initProps[kRNConcurrentRoot] = @([self concurrentRootEnabled]);
#endif
return initProps;
} }
- (NSArray<id<RCTBridgeModule>> *)extraModulesForBridge:(RCTBridge *)bridge { - (NSArray<id<RCTBridgeModule>> *)extraModulesForBridge:(RCTBridge *)bridge {
@ -86,43 +28,14 @@ static NSString *const kRNConcurrentRoot = @"concurrentRoot";
#endif #endif
} }
#if RCT_NEW_ARCH_ENABLED /// This method controls whether the `concurrentRoot`feature of React18 is turned on or off.
///
#pragma mark - RCTCxxBridgeDelegate /// @see: https://reactjs.org/blog/2022/03/29/react-v18.html
/// @note: This requires to be rendering on Fabric (i.e. on the New Architecture).
- (std::unique_ptr<facebook::react::JSExecutorFactory>)jsExecutorFactoryForBridge:(RCTBridge *)bridge /// @return: `true` if the `concurrentRoot` feature is enabled. Otherwise, it returns `false`.
- (BOOL)concurrentRootEnabled
{ {
_turboModuleManager = [[RCTTurboModuleManager alloc] initWithBridge:bridge return true;
delegate:self
jsInvoker:bridge.jsCallInvoker];
return RCTAppSetupDefaultJsExecutorFactory(bridge, _turboModuleManager);
} }
#pragma mark RCTTurboModuleManagerDelegate
- (Class)getModuleClassFromName:(const char *)name
{
return RCTCoreModulesClassProvider(name);
}
- (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:(const std::string &)name
jsInvoker:(std::shared_ptr<facebook::react::CallInvoker>)jsInvoker
{
return nullptr;
}
- (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:(const std::string &)name
initParams:
(const facebook::react::ObjCTurboModule::InitParams &)params
{
return nullptr;
}
- (id<RCTTurboModule>)getModuleInstanceFromClass:(Class)moduleClass
{
return RCTAppSetupDefaultModuleFromClass(moduleClass);
}
#endif
@end @end

View File

@ -17,11 +17,11 @@
<key>CFBundlePackageType</key> <key>CFBundlePackageType</key>
<string>APPL</string> <string>APPL</string>
<key>CFBundleShortVersionString</key> <key>CFBundleShortVersionString</key>
<string>1.0</string> <string>$(MARKETING_VERSION)</string>
<key>CFBundleSignature</key> <key>CFBundleSignature</key>
<string>????</string> <string>????</string>
<key>CFBundleVersion</key> <key>CFBundleVersion</key>
<string>1</string> <string>$(CURRENT_PROJECT_VERSION)</string>
<key>LSRequiresIPhoneOS</key> <key>LSRequiresIPhoneOS</key>
<true/> <true/>
<key>NSAppTransportSecurity</key> <key>NSAppTransportSecurity</key>
@ -50,6 +50,6 @@
<string>UIInterfaceOrientationLandscapeRight</string> <string>UIInterfaceOrientationLandscapeRight</string>
</array> </array>
<key>UIViewControllerBasedStatusBarAppearance</key> <key>UIViewControllerBasedStatusBarAppearance</key>
<true/> <true/>
</dict> </dict>
</plist> </plist>

View File

@ -1,8 +1,25 @@
require_relative '../node_modules/react-native/scripts/react_native_pods' require_relative '../node_modules/react-native/scripts/react_native_pods'
require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules' require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules'
platform :ios, '12.4' platform :ios, min_ios_version_supported
install! 'cocoapods', :deterministic_uuids => false prepare_react_native_project!
# If you are using a `react-native-flipper` your iOS build will fail when `NO_FLIPPER=1` is set.
# because `react-native-flipper` depends on (FlipperKit,...) that will be excluded
#
# To fix this you can also exclude `react-native-flipper` using a `react-native.config.js`
# ```js
# module.exports = {
# dependencies: {
# ...(process.env.NO_FLIPPER ? { 'react-native-flipper': { platforms: { ios: null } } } : {}),
# ```
flipper_config = ENV['NO_FLIPPER'] == "1" ? FlipperConfiguration.disabled : FlipperConfiguration.enabled
linkage = ENV['USE_FRAMEWORKS']
if linkage != nil
Pod::UI.puts "Configuring Pod with #{linkage}ally linked Frameworks".green
use_frameworks! :linkage => linkage.to_sym
end
target 'LxMusicMobile' do target 'LxMusicMobile' do
config = use_native_modules! config = use_native_modules!
@ -15,13 +32,13 @@ target 'LxMusicMobile' do
# Hermes is now enabled by default. Disable by setting this flag to false. # Hermes is now enabled by default. Disable by setting this flag to false.
# Upcoming versions of React Native may rely on get_default_flags(), but # Upcoming versions of React Native may rely on get_default_flags(), but
# we make it explicit here to aid in the React Native upgrade process. # we make it explicit here to aid in the React Native upgrade process.
:hermes_enabled => true, :hermes_enabled => flags[:hermes_enabled],
:fabric_enabled => flags[:fabric_enabled], :fabric_enabled => flags[:fabric_enabled],
# Enables Flipper. # Enables Flipper.
# #
# Note that if you have use_frameworks! enabled, Flipper will not work and # Note that if you have use_frameworks! enabled, Flipper will not work and
# you should disable the next line. # you should disable the next line.
:flipper_configuration => FlipperConfiguration.enabled, :flipper_configuration => flipper_config,
# An absolute path to your application root. # An absolute path to your application root.
:app_path => "#{Pod::Config.instance.installation_root}/.." :app_path => "#{Pod::Config.instance.installation_root}/.."
) )

9342
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -4,7 +4,7 @@
"versionCode": 53, "versionCode": 53,
"private": true, "private": true,
"scripts": { "scripts": {
"dev": "react-native run-android", "dev": "react-native run-android --active-arch-only",
"ios": "react-native run-ios", "ios": "react-native run-ios",
"start": "react-native start", "start": "react-native start",
"sc": "react-native start --reset-cache", "sc": "react-native start --reset-cache",
@ -50,15 +50,15 @@
"iconv-lite": "^0.6.3", "iconv-lite": "^0.6.3",
"lrc-file-parser": "^2.3.0", "lrc-file-parser": "^2.3.0",
"pako": "^2.1.0", "pako": "^2.1.0",
"react": "18.1.0", "react": "18.2.0",
"react-native": "0.70.7", "react-native": "0.71.3",
"react-native-background-timer": "^2.4.1", "react-native-background-timer": "^2.4.1",
"react-native-exception-handler": "^2.10.10", "react-native-exception-handler": "^2.10.10",
"react-native-fs": "^2.20.0", "react-native-fs": "^2.20.0",
"react-native-navigation": "^7.32.1", "react-native-navigation": "^7.32.1",
"react-native-pager-view": "^6.1.4", "react-native-pager-view": "^6.1.4",
"react-native-quick-base64": "^2.0.5", "react-native-quick-base64": "^2.0.5",
"react-native-quick-crypto": "^0.5.0", "react-native-quick-crypto": "github:lyswhut/react-native-quick-crypto#da0173c57db85052c053253945bc0ebfd1af1f56",
"react-native-quick-md5": "^3.0.4", "react-native-quick-md5": "^3.0.4",
"react-native-track-player": "github:lyswhut/react-native-track-player#38027954a5ac6e3d92961745e0a9633fc647f47a", "react-native-track-player": "github:lyswhut/react-native-track-player#38027954a5ac6e3d92961745e0a9633fc647f47a",
"react-native-vector-icons": "^9.2.0" "react-native-vector-icons": "^9.2.0"
@ -66,6 +66,7 @@
"devDependencies": { "devDependencies": {
"@babel/core": "^7.21.0", "@babel/core": "^7.21.0",
"@babel/plugin-proposal-export-namespace-from": "^7.18.9", "@babel/plugin-proposal-export-namespace-from": "^7.18.9",
"@babel/preset-env": "^7.20.2",
"@babel/runtime": "^7.21.0", "@babel/runtime": "^7.21.0",
"@tsconfig/react-native": "^2.0.3", "@tsconfig/react-native": "^2.0.3",
"@types/react": "^18.0.28", "@types/react": "^18.0.28",
@ -76,7 +77,7 @@
"eslint-config-standard-with-typescript": "^34.0.0", "eslint-config-standard-with-typescript": "^34.0.0",
"eslint-plugin-react": "^7.32.2", "eslint-plugin-react": "^7.32.2",
"eslint-plugin-react-hooks": "^4.6.0", "eslint-plugin-react-hooks": "^4.6.0",
"metro-react-native-babel-preset": "0.72.3", "metro-react-native-babel-preset": "0.73.7",
"typescript": "^4.9.5" "typescript": "^4.9.5"
} }
} }

View File

@ -33,4 +33,4 @@
### 其他 ### 其他
- 升级React Native到v0.70.7 - 升级React Native到v0.71.3并启用新架构

View File

@ -1,6 +1,6 @@
import { createI18n } from '@/lang/i18n' import { createI18n } from '@/lang/i18n'
import type { I18n } from '@/lang/i18n' import type { I18n } from '@/lang/i18n'
import { deviceLanguage } from '@/utils/tools' import { getDeviceLanguage } from '@/utils/tools'
import { setLanguage, updateSetting } from '@/core/common' import { setLanguage, updateSetting } from '@/core/common'
@ -10,6 +10,7 @@ export default async(setting: LX.AppSetting) => {
global.i18n = createI18n() global.i18n = createI18n()
if (!lang || !global.i18n.availableLocales.includes(lang)) { if (!lang || !global.i18n.availableLocales.includes(lang)) {
const deviceLanguage = await getDeviceLanguage()
if (typeof deviceLanguage == 'string' && global.i18n.availableLocales.includes(deviceLanguage as I18n['locale'])) { if (typeof deviceLanguage == 'string' && global.i18n.availableLocales.includes(deviceLanguage as I18n['locale'])) {
lang = deviceLanguage as I18n['locale'] lang = deviceLanguage as I18n['locale']
} else { } else {

View File

@ -33,12 +33,13 @@ const handleConnection = (socket: LX.Sync.Socket) => {
const heartbeatTools = { const heartbeatTools = {
failedNum: 0, failedNum: 0,
maxTryNum: 3, maxTryNum: 100000,
stepMs: 3000,
pingTimeout: null as NodeJS.Timeout | null, pingTimeout: null as NodeJS.Timeout | null,
delayRetryTimeout: null as NodeJS.Timeout | null, delayRetryTimeout: null as NodeJS.Timeout | null,
handleOpen() { handleOpen() {
console.log('open') console.log('open')
this.failedNum = 0 // this.failedNum = 0
this.heartbeat() this.heartbeat()
}, },
heartbeat() { heartbeat() {
@ -65,16 +66,23 @@ const heartbeatTools = {
throw new Error('connect error') throw new Error('connect error')
} }
const waitTime = Math.min(2000 + Math.floor(this.failedNum / 2) * this.stepMs, 30000)
// sendSyncStatus({
// status: false,
// message: `Waiting ${waitTime / 1000}s reconnnect...`,
// })
this.delayRetryTimeout = setTimeout(() => { this.delayRetryTimeout = setTimeout(() => {
this.delayRetryTimeout = null this.delayRetryTimeout = null
if (!client) return if (!client) return
console.log(dateFormat(new Date()), 'reconnnect...') console.log(dateFormat(new Date()), 'reconnnect...')
sendSyncStatus({ sendSyncStatus({
status: false, status: false,
message: `Try reconnnect... (${this.failedNum}/${this.maxTryNum})`, message: `Try reconnnect... (${this.failedNum})`,
}) })
connect(client.data.urlInfo, client.data.keyInfo) connect(client.data.urlInfo, client.data.keyInfo)
}, 2000) }, waitTime)
}, },
clearTimeout() { clearTimeout() {
if (this.delayRetryTimeout) { if (this.delayRetryTimeout) {
@ -95,7 +103,7 @@ const heartbeatTools = {
if (data == 'ping') this.heartbeat() if (data == 'ping') this.heartbeat()
}) })
socket.addEventListener('close', (event) => { socket.addEventListener('close', (event) => {
console.log(event.code) // console.log(event.code)
switch (event.code) { switch (event.code) {
case SYNC_CLOSE_CODE.normal: case SYNC_CLOSE_CODE.normal:
case SYNC_CLOSE_CODE.failed: case SYNC_CLOSE_CODE.failed:
@ -176,6 +184,7 @@ export const connect = (urlInfo: LX.Sync.UrlInfo, keyInfo: LX.Sync.KeyInfo) => {
status: true, status: true,
message: '', message: '',
}) })
heartbeatTools.failedNum = 0
}).catch(err => { }).catch(err => {
if (err.message == 'closed') { if (err.message == 'closed') {
sendSyncStatus({ sendSyncStatus({

View File

@ -41,3 +41,6 @@ export const writeFile = async(filePath: string, data: string): Promise<void> =>
export const readFile = async(filePath: string): Promise<string> => { export const readFile = async(filePath: string): Promise<string> => {
return UtilsModule.getStringFromFile(filePath) return UtilsModule.getStringFromFile(filePath)
} }
export const getSystemLocales = () => {
return UtilsModule.getSystemLocales()
}

View File

@ -1,10 +1,10 @@
import { Platform, NativeModules, ToastAndroid, BackHandler, Linking, Dimensions, Alert, Appearance, PermissionsAndroid, AppState, StyleSheet, type ScaledSize } from 'react-native' import { Platform, ToastAndroid, BackHandler, Linking, Dimensions, Alert, Appearance, PermissionsAndroid, AppState, StyleSheet, type ScaledSize } from 'react-native'
// import ExtraDimensions from 'react-native-extra-dimensions-android' // import ExtraDimensions from 'react-native-extra-dimensions-android'
import Clipboard from '@react-native-clipboard/clipboard' import Clipboard from '@react-native-clipboard/clipboard'
import { storageDataPrefix } from '@/config/constant' import { storageDataPrefix } from '@/config/constant'
import { gzip, ungzip } from '@/utils/nativeModules/gzip' import { gzip, ungzip } from '@/utils/nativeModules/gzip'
import { temporaryDirectoryPath, unlink } from '@/utils/fs' import { temporaryDirectoryPath, unlink } from '@/utils/fs'
import { isNotificationsEnabled, openNotificationPermissionActivity, readFile, shareText, writeFile } from '@/utils/nativeModules/utils' import { getSystemLocales, isNotificationsEnabled, openNotificationPermissionActivity, readFile, shareText, writeFile } from '@/utils/nativeModules/utils'
import musicSdk from '@/utils/musicSdk' import musicSdk from '@/utils/musicSdk'
import { getData, removeData, saveData } from '@/plugins/storage' import { getData, removeData, saveData } from '@/plugins/storage'
import BackgroundTimer from 'react-native-background-timer' import BackgroundTimer from 'react-native-background-timer'
@ -12,13 +12,16 @@ import { scaleSizeH, scaleSizeW, setSpText } from './pixelRatio'
import { toOldMusicInfo } from './index' import { toOldMusicInfo } from './index'
import { stringMd5 } from 'react-native-quick-md5' import { stringMd5 } from 'react-native-quick-md5'
// https://stackoverflow.com/a/47349998 // https://stackoverflow.com/a/47349998
let deviceLanguage = Platform.OS === 'ios' export const getDeviceLanguage = async() => {
? NativeModules.SettingsManager.settings.AppleLocale || // let deviceLanguage = Platform.OS === 'ios'
NativeModules.SettingsManager.settings.AppleLanguages[0] // iOS 13 // ? NativeModules.SettingsManager.settings.AppleLocale ||
: NativeModules.I18nManager.localeIdentifier // NativeModules.SettingsManager.settings.AppleLanguages[0] // iOS 13
deviceLanguage = typeof deviceLanguage === 'string' ? deviceLanguage.substring(0, 5).toLocaleLowerCase() : '' // : await getSystemLocales()
// deviceLanguage = typeof deviceLanguage === 'string' ? deviceLanguage.substring(0, 5).toLocaleLowerCase() : ''
return await getSystemLocales()
}
export const isAndroid = Platform.OS === 'android' export const isAndroid = Platform.OS === 'android'
// @ts-expect-error // @ts-expect-error
@ -418,7 +421,3 @@ export const createStyle = <T extends StyleSheet.NamedStyles<T>>(styles: T | Sty
} }
export const toMD5 = stringMd5 export const toMD5 = stringMd5
export {
deviceLanguage,
}

6419
yarn.lock

File diff suppressed because it is too large Load Diff