Skip to content

Commit cd703ed

Browse files
committed
Fix the document picker version mismatch
1 parent a32907c commit cd703ed

15 files changed

Lines changed: 196 additions & 134 deletions

File tree

examples/react-native/RunAnywhereAI/android/app/build.gradle

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@ android {
140140
"-DCMAKE_MODULE_PATH=${projectDir}"
141141
}
142142
}
143-
ndkVersion "26.3.11579264"
143+
ndkVersion "27.0.12077973"
144144
}
145145
signingConfigs {
146146
debug {
@@ -192,6 +192,9 @@ dependencies {
192192
// The version of react-native is set by the React Native Gradle Plugin
193193
implementation("com.facebook.react:react-android")
194194

195+
// PDF text extraction for DocumentService (RAG)
196+
implementation("com.tom-roush:pdfbox-android:2.0.27.0")
197+
195198
// Manually link Nitro modules (excluded from autolinking)
196199
implementation project(':react-native-nitro-modules')
197200

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
package com.runanywhereaI
2+
3+
import android.net.Uri
4+
import com.facebook.react.bridge.Promise
5+
import com.facebook.react.bridge.ReactApplicationContext
6+
import com.facebook.react.bridge.ReactContextBaseJavaModule
7+
import com.facebook.react.bridge.ReactMethod
8+
import com.tom_roush.pdfbox.android.PDFBoxResourceLoader
9+
import com.tom_roush.pdfbox.pdmodel.PDDocument
10+
import com.tom_roush.pdfbox.text.PDFTextStripper
11+
import org.json.JSONArray
12+
import org.json.JSONObject
13+
import java.io.File
14+
15+
class DocumentServiceModule(private val reactContext: ReactApplicationContext) :
16+
ReactContextBaseJavaModule(reactContext) {
17+
18+
override fun getName(): String = "DocumentService"
19+
20+
override fun initialize() {
21+
super.initialize()
22+
PDFBoxResourceLoader.init(reactContext)
23+
}
24+
25+
@ReactMethod
26+
fun extractText(filePath: String, promise: Promise) {
27+
Thread {
28+
try {
29+
val fileType = resolveFileType(filePath)
30+
val text = when (fileType) {
31+
FileType.PDF -> extractPDFText(filePath)
32+
FileType.JSON -> extractJSONText(filePath)
33+
FileType.PLAIN_TEXT -> readPlainText(filePath)
34+
}
35+
promise.resolve(text)
36+
} catch (e: Exception) {
37+
promise.reject("EXTRACT_ERROR", e.localizedMessage, e)
38+
}
39+
}.start()
40+
}
41+
42+
private enum class FileType { PDF, JSON, PLAIN_TEXT }
43+
44+
private fun resolveFileType(filePath: String): FileType {
45+
// For content:// URIs, use the content resolver to get the MIME type
46+
if (filePath.startsWith("content://")) {
47+
val mimeType = reactContext.contentResolver.getType(Uri.parse(filePath))
48+
if (mimeType != null) {
49+
return when {
50+
mimeType == "application/pdf" -> FileType.PDF
51+
mimeType == "application/json" -> FileType.JSON
52+
else -> FileType.PLAIN_TEXT
53+
}
54+
}
55+
}
56+
57+
// Fall back to extension-based detection for file:// and raw paths
58+
val ext = filePath.substringAfterLast('.', "").lowercase()
59+
return when (ext) {
60+
"pdf" -> FileType.PDF
61+
"json" -> FileType.JSON
62+
else -> FileType.PLAIN_TEXT
63+
}
64+
}
65+
66+
private fun extractPDFText(filePath: String): String {
67+
val inputStream = openInputStream(filePath)
68+
?: throw Exception("Failed to open PDF. File may be corrupted or image-only.")
69+
70+
val document = inputStream.use { PDDocument.load(it) }
71+
document.use { doc ->
72+
if (doc.numberOfPages == 0) {
73+
throw Exception("PDF has no pages.")
74+
}
75+
val stripper = PDFTextStripper()
76+
val result = stripper.getText(doc).trim()
77+
if (result.isEmpty()) {
78+
throw Exception("PDF contains no extractable text (may be image-only).")
79+
}
80+
return result
81+
}
82+
}
83+
84+
private fun extractJSONText(filePath: String): String {
85+
val raw = readPlainText(filePath)
86+
val parsed = when {
87+
raw.trimStart().startsWith("[") -> JSONArray(raw) as Any
88+
else -> JSONObject(raw) as Any
89+
}
90+
val strings = mutableListOf<String>()
91+
extractStrings(parsed, strings)
92+
return strings.joinToString("\n")
93+
}
94+
95+
private fun extractStrings(value: Any, result: MutableList<String>) {
96+
when (value) {
97+
is String -> result.add(value)
98+
is JSONObject -> {
99+
for (key in value.keys()) {
100+
extractStrings(value.get(key), result)
101+
}
102+
}
103+
is JSONArray -> {
104+
for (i in 0 until value.length()) {
105+
extractStrings(value.get(i), result)
106+
}
107+
}
108+
}
109+
}
110+
111+
private fun readPlainText(filePath: String): String {
112+
val inputStream = openInputStream(filePath)
113+
?: throw Exception("Failed to open file at: $filePath")
114+
return inputStream.use { it.bufferedReader().readText() }
115+
}
116+
117+
private fun openInputStream(filePath: String): java.io.InputStream? {
118+
return if (filePath.startsWith("content://")) {
119+
reactContext.contentResolver.openInputStream(Uri.parse(filePath))
120+
} else {
121+
val path = if (filePath.startsWith("file://")) {
122+
Uri.parse(filePath).path ?: filePath.removePrefix("file://")
123+
} else {
124+
filePath
125+
}
126+
File(path).inputStream()
127+
}
128+
}
129+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package com.runanywhereaI
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 DocumentServicePackage : ReactPackage {
9+
override fun createNativeModules(reactContext: ReactApplicationContext): List<NativeModule> =
10+
listOf(DocumentServiceModule(reactContext))
11+
12+
override fun createViewManagers(reactContext: ReactApplicationContext): List<ViewManager<*, *>> =
13+
emptyList()
14+
}

examples/react-native/RunAnywhereAI/android/app/src/main/java/com/runanywhereaI/MainApplication.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ class MainApplication : Application(), ReactApplication {
2323
add(RunAnywhereCorePackage())
2424
add(RunAnywhereLlamaPackage())
2525
add(RunAnywhereONNXPackage())
26+
add(DocumentServicePackage())
2627
},
2728
)
2829
}

examples/react-native/RunAnywhereAI/android/build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ buildscript {
44
minSdkVersion = 24
55
compileSdkVersion = 36
66
targetSdkVersion = 36
7-
ndkVersion = "26.3.11579264"
7+
ndkVersion = "27.0.12077973"
88
kotlinVersion = "2.1.20"
99
}
1010
repositories {

examples/react-native/RunAnywhereAI/android/gradle/wrapper/gradle-wrapper.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
distributionBase=GRADLE_USER_HOME
22
distributionPath=wrapper/dists
3-
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.3-bin.zip
3+
distributionUrl=https\://services.gradle.org/distributions/gradle-9.0.0-bin.zip
44
networkTimeout=10000
55
validateDistributionUrl=true
66
zipStoreBase=GRADLE_USER_HOME

examples/react-native/RunAnywhereAI/knip.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@
2525
"@babel/runtime",
2626
"react-native-nitro-modules",
2727
"runanywhere-react-native",
28-
"rn-fetch-blob",
2928
"@react-navigation/native-stack",
3029
"react-native-audio-recorder-player"
3130
],

examples/react-native/RunAnywhereAI/package-lock.json

Lines changed: 11 additions & 73 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

examples/react-native/RunAnywhereAI/package.json

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
"dependencies": {
2121
"@react-native-async-storage/async-storage": "^2.2.0",
2222
"@react-native-clipboard/clipboard": "^1.16.3",
23+
"@react-native-documents/picker": "^10.1.7",
2324
"@react-navigation/bottom-tabs": "^7.12.0",
2425
"@react-navigation/native": "^7.1.28",
2526
"@react-navigation/native-stack": "^7.12.0",
@@ -28,7 +29,6 @@
2829
"@runanywhere/onnx": "file:../../../sdk/runanywhere-react-native/packages/onnx",
2930
"react": "19.2.0",
3031
"react-native": "0.83.1",
31-
"react-native-document-picker": "^9.3.1",
3232
"react-native-fs": "^2.20.0",
3333
"react-native-image-picker": "^8.2.1",
3434
"react-native-live-audio-stream": "^1.1.1",
@@ -38,7 +38,6 @@
3838
"react-native-screens": "^4.23.0",
3939
"react-native-vector-icons": "^10.3.0",
4040
"react-native-vision-camera": "^4.7.3",
41-
"rn-fetch-blob": "^0.12.0",
4241
"zustand": "^5.0.0"
4342
},
4443
"devDependencies": {

0 commit comments

Comments
 (0)