ha-frp-rn 1.0.1 → 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,51 @@
1
+ plugins {
2
+ id("com.android.library")
3
+ id("org.jetbrains.kotlin.android")
4
+ }
5
+
6
+ android {
7
+ namespace = "io.github.acedroidx.frp"
8
+ compileSdk = 34
9
+
10
+ defaultConfig {
11
+ minSdk = 24
12
+ targetSdk = 34
13
+
14
+ testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
15
+ consumerProguardFiles("consumer-rules.pro")
16
+ }
17
+
18
+ buildTypes {
19
+ release {
20
+ isMinifyEnabled = false
21
+ proguardFiles(
22
+ getDefaultProguardFile("proguard-android-optimize.txt"),
23
+ "proguard-rules.pro"
24
+ )
25
+ }
26
+ }
27
+ compileOptions {
28
+ sourceCompatibility = JavaVersion.VERSION_11
29
+ targetCompatibility = JavaVersion.VERSION_11
30
+ }
31
+ kotlinOptions {
32
+ jvmTarget = "11"
33
+ }
34
+ buildFeatures {
35
+ buildConfig = false
36
+ }
37
+ packaging {
38
+ jniLibs {
39
+ useLegacyPackaging = true
40
+ }
41
+ }
42
+ }
43
+
44
+ dependencies {
45
+ implementation("androidx.core:core-ktx:1.13.1")
46
+ implementation("androidx.appcompat:appcompat:1.7.0")
47
+ testImplementation("junit:junit:4.13.2")
48
+ androidTestImplementation("androidx.test.ext:junit:1.2.1")
49
+ androidTestImplementation("androidx.test.espresso:espresso-core:3.6.1")
50
+ implementation("com.facebook.react:react-native:0.72.0")
51
+ }
@@ -0,0 +1,68 @@
1
+ # ------------------------
2
+ # Consumer ProGuard Rules
3
+ # ------------------------
4
+
5
+ # This is the ProGuard configuration file for the library users.
6
+ # It specifies which classes and members should be kept when the app using this library is minified.
7
+
8
+ # Keep all public classes and methods that are used by the React Native bridge
9
+ -keep class io.github.acedroidx.frp.** { *; }
10
+
11
+ # Keep all React Native bridge methods
12
+ -keepclassmembers class io.github.acedroidx.frp.** {
13
+ @com.facebook.react.bridge.ReactMethod <methods>;
14
+ }
15
+
16
+ # Keep all React Native module classes
17
+ -keep class * extends com.facebook.react.bridge.ReactContextBaseJavaModule { *; }
18
+
19
+ # Keep all React Native package classes
20
+ -keep class * extends com.facebook.react.ReactPackage { *; }
21
+
22
+ # Keep all React Native view manager classes
23
+ -keep class * extends com.facebook.react.uimanager.ViewManager { *; }
24
+
25
+ # Keep all React Native view manager listeners
26
+ -keep class * implements com.facebook.react.uimanager.ReactShadowNode { *; }
27
+
28
+ # Keep all React Native bridge interfaces
29
+ -keep interface com.facebook.react.bridge.** { *; }
30
+
31
+ # Keep all React Native bridge annotations
32
+ -keep @com.facebook.react.bridge.ReactModule class *
33
+
34
+ # Keep all React Native bridge arguments
35
+ -keep class com.facebook.react.bridge.*$* {
36
+ <fields>;
37
+ <methods>;
38
+ }
39
+
40
+ # Keep all React Native bridge lists
41
+ -keep class com.facebook.react.bridge.List * {
42
+ <fields>;
43
+ <methods>;
44
+ }
45
+
46
+ # Keep all React Native bridge maps
47
+ -keep class com.facebook.react.bridge.ReadableMap {
48
+ <fields>;
49
+ <methods>;
50
+ }
51
+
52
+ # Keep all React Native bridge readable arrays
53
+ -keep class com.facebook.react.bridge.ReadableArray {
54
+ <fields>;
55
+ <methods>;
56
+ }
57
+
58
+ # Keep all React Native bridge writable maps
59
+ -keep class com.facebook.react.bridge.WritableMap {
60
+ <fields>;
61
+ <methods>;
62
+ }
63
+
64
+ # Keep all React Native bridge writable arrays
65
+ -keep class com.facebook.react.bridge.WritableArray {
66
+ <fields>;
67
+ <methods>;
68
+ }
@@ -0,0 +1,8 @@
1
+ #Tue Dec 19 17:28:00 CST 2025
2
+ distributionBase=GRADLE_USER_HOME
3
+ distributionPath=wrapper/dists
4
+ distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-bin.zip
5
+ networkTimeout=10000
6
+ validateDistributionUrl=true
7
+ zipStoreBase=GRADLE_USER_HOME
8
+ zipStorePath=wrapper/dists
@@ -0,0 +1,179 @@
1
+ #!/usr/bin/env sh
2
+
3
+ # Copyright 2015 the original author or authors.
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # https://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+
17
+ ##############################################################################
18
+ #
19
+ # Gradle start up script for UN*X
20
+ #
21
+ ##############################################################################
22
+
23
+ # Attempt to set APP_HOME
24
+ # Resolve links: $0 may be a link
25
+ PRG="$0"
26
+ # Need this for relative symlinks.
27
+ while [ -h "$PRG" ]; do
28
+ ls=`ls -ld "$PRG"`
29
+ link=`expr "$ls" : '.*-> \(.*\)$'`
30
+ if expr "$link" : '/.*' > /dev/null; then
31
+ PRG="$link"
32
+ else
33
+ PRG=`dirname "$PRG"`"/$link"
34
+ fi
35
+ done
36
+ SAVED="$PWD"
37
+
38
+ cd "`dirname \"$PRG\"`/"
39
+ APP_HOME="`pwd -P`"
40
+ cd "$SAVED"
41
+
42
+ APP_NAME="Gradle"
43
+ APP_BASE_NAME=`basename "$0"`
44
+
45
+ # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
46
+ DEFAULT_JVM_OPTS="-Xmx64m""-Xms64m"
47
+
48
+ # Use the maximum available, or set MAX_FD != -1 to use that value.
49
+ MAX_FD="maximum"
50
+
51
+ warn () {
52
+ echo "$*"
53
+ }
54
+
55
+ die () {
56
+ echo
57
+ echo "$*"
58
+ echo
59
+ exit 1
60
+ }
61
+
62
+ # OS specific support (must be 'true' or 'false').
63
+ cygwin=false
64
+ darwin=false
65
+ msys=false
66
+ nonstop=false
67
+ case "`uname`" in
68
+ CYGWIN* )
69
+ cygwin=true
70
+ ;;
71
+ Darwin* )
72
+ darwin=true
73
+ ;;
74
+ MINGW* )
75
+ msys=true
76
+ ;;
77
+ NONSTOP* )
78
+ nonstop=true
79
+ ;;
80
+ esac
81
+
82
+ CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
83
+
84
+ # Determine the Java command to use to start the JVM.
85
+ if [ -n "$JAVA_HOME" ] ; then
86
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
87
+ # IBM's JDK on AIX uses strange locations for the executables
88
+ JAVACMD="$JAVA_HOME/jre/sh/java"
89
+ else
90
+ JAVACMD="$JAVA_HOME/bin/java"
91
+ fi
92
+ if [ ! -x "$JAVACMD" ] ; then
93
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
94
+
95
+ Please set the JAVA_HOME variable in your environment to match the
96
+ location of your Java installation."
97
+ fi
98
+ else
99
+ JAVACMD="java"
100
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
101
+
102
+ Please set the JAVA_HOME variable in your environment to match the
103
+ location of your Java installation."
104
+ fi
105
+
106
+ # Increase the maximum file descriptors if we can.
107
+ if [ "$cygwin" = "false" ] && [ "$darwin" = "false" ] && [ "$nonstop" = "false" ] ; then
108
+ MAX_FD_LIMIT=`ulimit -H -n`
109
+ if [ $? -eq 0 ] ; then
110
+ if [ "$MAX_FD" = "maximum" ] || [ "$MAX_FD" = "max" ] ; then
111
+ MAX_FD="$MAX_FD_LIMIT"
112
+ fi
113
+ ulimit -n $MAX_FD
114
+ if [ $? -ne 0 ] ; then
115
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
116
+ fi
117
+ else
118
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
119
+ fi
120
+ fi
121
+
122
+ # For Darwin, add options to specify how the application appears in the dock
123
+ if $darwin; then
124
+ GRADLE_OPTS="$GRADLE_OPTS""-Xdock:name=$APP_NAME""-Xdock:icon=$APP_HOME/media/gradle.icns"
125
+ fi
126
+
127
+ # For Cygwin, switch paths to Windows format before running java
128
+ if $cygwin ; then
129
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
130
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
131
+ JAVACMD=`cygpath --unix "$JAVACMD"`
132
+
133
+ # We build the pattern for arguments to be converted via cygpath
134
+ ROOTDIRSRAW=`find . -type d -maxdepth 1 -name "*" | egrep -v "./" | tr "\\n" ";"`
135
+ ROOTDIRS=`cygpath --path --mixed "$ROOTDIRSRAW"`
136
+ for i in "$@" ; do
137
+ case "$i" in
138
+ ("-D"*)
139
+ args="$args $i"
140
+ ;;
141
+ (*)
142
+ if [ -f "$i" ] ; then
143
+ args="$args `cygpath --path --mixed "$i"`"
144
+ else
145
+ args="$args $i"
146
+ fi
147
+ ;;
148
+ esac
149
+ done
150
+ export args
151
+ fi
152
+
153
+ # Escape application args
154
+ save () {
155
+ for i in "$@" ; do
156
+ case "$i" in
157
+ (*\ * | *\$\'\'' | *\"* | *\`* | *\&* | *\|* | *\;* | *\<* | *\>* | *\(* | *\)* | *\[* | *\]* | *\{* | *\}* | *\!* | *\'\''*)
158
+ save_args="$save_args '\"$i\"'"
159
+ ;;
160
+ (*)
161
+ save_args="$save_args \"$i\""
162
+ ;;
163
+ esac
164
+ done
165
+ export save_args
166
+ }
167
+ save "$@"
168
+
169
+ # Collect all arguments for the java command, following the shell quoting and substitution rules
170
+ eval set -- $save_args
171
+
172
+ # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
173
+ if [ "$darwin" = "true" ] && [ "$HOME" = "$PWD" ]; then
174
+ cd "`dirname $0`"
175
+ fi
176
+
177
+ # Execute Gradle
178
+ # Quoting the arguments is necessary on Windows due to problems with Java not recognizing the quoted arguments
179
+ "$JAVACMD" $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "-Dorg.gradle.appname=$APP_BASE_NAME" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
@@ -0,0 +1,83 @@
1
+ @rem Copyright 2015 the original author or authors.
2
+ @rem
3
+ @rem Licensed under the Apache License, Version 2.0 (the "License");
4
+ @rem you may not use this file except in compliance with the License.
5
+ @rem You may obtain a copy of the License at
6
+ @rem
7
+ @rem https://www.apache.org/licenses/LICENSE-2.0
8
+ @rem
9
+ @rem Unless required by applicable law or agreed to in writing, software
10
+ @rem distributed under the License is distributed on an "AS IS" BASIS,
11
+ @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ @rem See the License for the specific language governing permissions and
13
+ @rem limitations under the License.
14
+
15
+ @if "%DEBUG%" == "" @echo off
16
+ @rem ##########################################################################
17
+ @rem
18
+ @rem Gradle startup script for Windows
19
+ @rem
20
+ @rem ##########################################################################
21
+
22
+ @rem Set local scope for the variables with windows NT shell
23
+ if "%OS%"=="Windows_NT" setlocal
24
+
25
+ set DIRNAME=%~dp0
26
+ if "%DIRNAME%" == "" set DIRNAME=.
27
+ set APP_BASE_NAME=%~n0
28
+ set APP_HOME=%DIRNAME%
29
+
30
+ @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
31
+ set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
32
+
33
+ @rem Find java.exe
34
+ if defined JAVA_HOME goto findJavaFromJavaHome
35
+
36
+ set JAVA_EXE=java.exe
37
+ %JAVA_EXE% -version >NUL 2>&1
38
+ if "%ERRORLEVEL%" == "0" goto execute
39
+
40
+ echo.
41
+ echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
42
+ echo.
43
+ echo Please set the JAVA_HOME variable in your environment to match the
44
+ echo location of your Java installation.
45
+
46
+ goto fail
47
+
48
+ :findJavaFromJavaHome
49
+ set JAVA_HOME=%JAVA_HOME:"=%
50
+ set JAVA_EXE=%JAVA_HOME%/bin/java.exe
51
+
52
+ if exist "%JAVA_EXE%" goto execute
53
+
54
+ echo.
55
+ echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
56
+ echo.
57
+ echo Please set the JAVA_HOME variable in your environment to match the
58
+ echo location of your Java installation.
59
+
60
+ goto fail
61
+
62
+ :execute
63
+ @rem Setup the command line
64
+
65
+ set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
66
+
67
+ @rem Execute Gradle
68
+ "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
69
+
70
+ :end
71
+ @rem End local scope for the variables with windows NT shell
72
+ if "%ERRORLEVEL%"=="0" goto mainEnd
73
+
74
+ :fail
75
+ echo.
76
+ if not "%1"=="" pause
77
+
78
+ exit /b 1
79
+
80
+ :mainEnd
81
+ if "%OS%"=="Windows_NT" endlocal
82
+
83
+ :omega
@@ -0,0 +1,84 @@
1
+ # ------------------------
2
+ # Library ProGuard Rules
3
+ # ------------------------
4
+
5
+ # This is the ProGuard configuration file for the library itself.
6
+ # It specifies which classes and members should be kept when the library is built.
7
+
8
+ # Keep all public classes and their methods for reflection
9
+ -keep class io.github.acedroidx.frp.** { *; }
10
+
11
+ # Keep all enum classes
12
+ -keepclassmembers enum io.github.acedroidx.frp.** {
13
+ public static **[] values();
14
+ public static ** valueOf(java.lang.String);
15
+ }
16
+
17
+ # Keep all native methods
18
+ -keepclasseswithmembernames class * {
19
+ native <methods>;
20
+ }
21
+
22
+ # Keep all class members that are accessed via reflection
23
+ -keepclassmembers class io.github.acedroidx.frp.** {
24
+ <fields>;
25
+ <methods>;
26
+ }
27
+
28
+ # Keep all constructors
29
+ -keepclassmembers class io.github.acedroidx.frp.** {
30
+ public <init>(...);
31
+ }
32
+
33
+ # Keep all React Native related classes and methods
34
+ -keepclassmembers class io.github.acedroidx.frp.FrpModule {
35
+ @com.facebook.react.bridge.ReactMethod <methods>;
36
+ }
37
+
38
+ # Keep all ShellService methods that are called from FrpModule
39
+ -keepclassmembers class io.github.acedroidx.frp.ShellService {
40
+ public <methods>;
41
+ }
42
+
43
+ # Keep all status enum values
44
+ -keepclassmembers class io.github.acedroidx.frp.ShellService$Status {
45
+ public static **[] values();
46
+ public static ** valueOf(java.lang.String);
47
+ public <fields>;
48
+ public <methods>;
49
+ }
50
+
51
+ # Keep all Android Context related methods
52
+ -keepclassmembers class io.github.acedroidx.frp.ShellService {
53
+ public <init>(android.content.Context);
54
+ }
55
+
56
+ # Keep all input/output stream methods
57
+ -keepclassmembers class io.github.acedroidx.frp.ShellService {
58
+ private java.lang.String extractBinary(java.lang.String);
59
+ }
60
+
61
+ # Keep all process management methods
62
+ -keepclassmembers class io.github.acedroidx.frp.ShellService {
63
+ private void startFrp(java.lang.String, java.lang.String);
64
+ private void monitorProcess();
65
+ private void setStatus(io.github.acedroidx.frp.ShellService$Status, java.lang.String);
66
+ }
67
+
68
+ # Keep all listener methods
69
+ -keepclassmembers class io.github.acedroidx.frp.ShellService {
70
+ private void setOnStatusChangeListener(kotlin.jvm.functions.Function2);
71
+ private kotlin.jvm.functions.Function2 onStatusChangeListener;
72
+ }
73
+
74
+ # Keep all executor service methods
75
+ -keepclassmembers class io.github.acedroidx.frp.ShellService {
76
+ private java.util.concurrent.ExecutorService executorService;
77
+ }
78
+
79
+ # Keep all process related fields
80
+ -keepclassmembers class io.github.acedroidx.frp.ShellService {
81
+ private java.lang.Process process;
82
+ private io.github.acedroidx.frp.ShellService$Status status;
83
+ private java.lang.String lastMessage;
84
+ }
@@ -0,0 +1,24 @@
1
+ pluginManagement {
2
+ repositories {
3
+ google()
4
+ mavenCentral()
5
+ gradlePluginPortal()
6
+ }
7
+ }
8
+
9
+ dependencyResolutionManagement {
10
+ repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
11
+ repositories {
12
+ google()
13
+ mavenCentral()
14
+ maven {
15
+ url = uri("https://www.jitpack.io")
16
+ }
17
+ maven {
18
+ url = uri("https://maven.google.com")
19
+ }
20
+ }
21
+ }
22
+
23
+ rootProject.name = "ha-frp-rn"
24
+ include(":ha-frp-rn")
@@ -0,0 +1,63 @@
1
+ package io.github.acedroidx.frp
2
+
3
+ import android.content.Context
4
+ import android.os.Parcelable
5
+ import kotlinx.parcelize.Parcelize
6
+ import java.io.File
7
+
8
+ /**
9
+ * FRP配置数据类
10
+ *
11
+ * 用途:封装FRP配置的基本信息
12
+ * 功能:提供配置文件、日志文件等路径管理
13
+ */
14
+ @Parcelize
15
+ data class FrpConfig(
16
+ val type: FrpType,
17
+ val fileName: String,
18
+ ) : Parcelable {
19
+ override fun toString(): String {
20
+ return "[$type]$fileName"
21
+ }
22
+
23
+ /**
24
+ * 获取配置文件存储目录
25
+ *
26
+ * @param context - Android上下文
27
+ * @return File - 配置文件目录
28
+ */
29
+ fun getDir(context: Context): File {
30
+ return this.type.getDir(context)
31
+ }
32
+
33
+ /**
34
+ * 获取完整的配置文件路径
35
+ *
36
+ * @param context - Android上下文
37
+ * @return File - 配置文件
38
+ */
39
+ fun getFile(context: Context): File {
40
+ return File(this.getDir(context), this.fileName)
41
+ }
42
+
43
+ /**
44
+ * 获取日志文件目录
45
+ *
46
+ * @param context - Android上下文
47
+ * @return File - 日志目录
48
+ */
49
+ fun getLogDir(context: Context): File {
50
+ return File(context.cacheDir, "logs/${this.type.typeName}")
51
+ }
52
+
53
+ /**
54
+ * 获取完整的日志文件路径
55
+ *
56
+ * @param context - Android上下文
57
+ * @return File - 日志文件
58
+ */
59
+ fun getLogFile(context: Context): File {
60
+ val logFileName = this.fileName.replace(".toml", ".log")
61
+ return File(this.getLogDir(context), logFileName)
62
+ }
63
+ }
@@ -0,0 +1,115 @@
1
+ package io.github.acedroidx.frp
2
+
3
+ import android.content.Context
4
+ import com.facebook.react.bridge.*
5
+ import com.facebook.react.module.annotations.ReactModule
6
+ import com.facebook.react.modules.core.DeviceEventManagerModule
7
+ import java.io.File
8
+ import java.io.IOException
9
+
10
+ @ReactModule(name = FrpModule.MODULE_NAME)
11
+ class FrpModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaModule(reactContext) {
12
+
13
+ companion object {
14
+ const val MODULE_NAME = "Frp"
15
+ const val EVENT_FRP_STATUS = "frpStatusChanged"
16
+ }
17
+
18
+ private var shellService: ShellService? = null
19
+ private var context: Context = reactContext
20
+
21
+ override fun getName(): String = MODULE_NAME
22
+
23
+ override fun getConstants(): MutableMap<String, Any> = mutableMapOf(
24
+ "MODULE_NAME" to MODULE_NAME,
25
+ "EVENT_FRP_STATUS" to EVENT_FRP_STATUS
26
+ )
27
+
28
+ @ReactMethod
29
+ fun startVisitor(configPath: String, promise: Promise) {
30
+ try {
31
+ if (shellService == null) {
32
+ shellService = ShellService(context)
33
+ shellService?.setOnStatusChangeListener {\ status, message ->
34
+ sendStatusEvent(status, message)
35
+ }
36
+ }
37
+ shellService?.startVisitor(configPath)
38
+ promise.resolve(true)
39
+ } catch (e: Exception) {
40
+ promise.reject("FRP_ERROR", e.message, e)
41
+ }
42
+ }
43
+
44
+ @ReactMethod
45
+ fun startServer(configPath: String, promise: Promise) {
46
+ try {
47
+ if (shellService == null) {
48
+ shellService = ShellService(context)
49
+ shellService?.setOnStatusChangeListener {\ status, message ->
50
+ sendStatusEvent(status, message)
51
+ }
52
+ }
53
+ shellService?.startServer(configPath)
54
+ promise.resolve(true)
55
+ } catch (e: Exception) {
56
+ promise.reject("FRP_ERROR", e.message, e)
57
+ }
58
+ }
59
+
60
+ @ReactMethod
61
+ fun stop(promise: Promise) {
62
+ try {
63
+ shellService?.stop()
64
+ promise.resolve(true)
65
+ } catch (e: Exception) {
66
+ promise.reject("FRP_ERROR", e.message, e)
67
+ }
68
+ }
69
+
70
+ @ReactMethod
71
+ fun getStatus(promise: Promise) {
72
+ try {
73
+ val status = shellService?.status ?: ShellService.Status.STOPPED
74
+ val message = shellService?.lastMessage ?: ""
75
+ promise.resolve(createStatusMap(status, message))
76
+ } catch (e: Exception) {
77
+ promise.reject("FRP_ERROR", e.message, e)
78
+ }
79
+ }
80
+
81
+ @ReactMethod
82
+ fun copyConfigFromAssets(sourcePath: String, destinationPath: String, promise: Promise) {
83
+ try {
84
+ val assetManager = context.assets
85
+ val inputStream = assetManager.open(sourcePath)
86
+ val outputFile = File(destinationPath)
87
+
88
+ // Create parent directories if they don't exist
89
+ outputFile.parentFile?.mkdirs()
90
+
91
+ val outputStream = outputFile.outputStream()
92
+ inputStream.copyTo(outputStream)
93
+ inputStream.close()
94
+ outputStream.close()
95
+
96
+ promise.resolve(destinationPath)
97
+ } catch (e: IOException) {
98
+ promise.reject("FRP_CONFIG_ERROR", e.message, e)
99
+ }
100
+ }
101
+
102
+ private fun sendStatusEvent(status: ShellService.Status, message: String) {
103
+ val eventData = createStatusMap(status, message)
104
+ reactApplicationContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
105
+ .emit(EVENT_FRP_STATUS, eventData)
106
+ }
107
+
108
+ private fun createStatusMap(status: ShellService.Status, message: String): WritableMap {
109
+ val map = Arguments.createMap()
110
+ map.putString("status", status.name)
111
+ map.putString("message", message)
112
+ map.putBoolean("isRunning", status == ShellService.Status.RUNNING)
113
+ return map
114
+ }
115
+ }
@@ -0,0 +1,16 @@
1
+ package io.github.acedroidx.frp
2
+
3
+ import com.facebook.react.ReactPackage
4
+ import com.facebook.react.bridge.NativeModule
5
+ import com.facebook.react.bridge.ReactApplicationContext
6
+ import com.facebook.react.uimanager.ViewManager
7
+
8
+ class FrpPackage : ReactPackage {
9
+ override fun createNativeModules(reactContext: ReactApplicationContext): List<NativeModule> {
10
+ return listOf(FrpModule(reactContext))
11
+ }
12
+
13
+ override fun createViewManagers(reactContext: ReactApplicationContext): List<ViewManager<*, *>> {
14
+ return emptyList()
15
+ }
16
+ }