Skip to content

Commit dfb79ad

Browse files
committed
Add filesystem stream interfaces and implement read/write functions
1 parent bde150c commit dfb79ad

28 files changed

+544
-79
lines changed

gradle/libs.versions.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ squareRetrofit = "2.11.0"
88
squareMoshi = "1.15.1"
99
kotlinVersion = "1.9.0"
1010
kotlin = "2.0.0"
11+
webkit = "1.14.0"
1112

1213
[libraries]
1314
ksp-gradle = { group = "com.google.devtools.ksp", name = "com.google.devtools.ksp.gradle.plugin", version.ref = "ksp" }
@@ -19,6 +20,7 @@ square-retrofit-moshi = { group = "com.squareup.retrofit2", name = "converter-mo
1920
square-retrofit-kotlinxSerialization = { group = "com.squareup.retrofit2", name = "converter-kotlinx-serialization", version.ref = "squareRetrofit" }
2021
square-moshi = { group = "com.squareup.moshi", name = "moshi", version.ref = "squareMoshi" }
2122
square-moshi-kotlin = { group = "com.squareup.moshi", name = "moshi-kotlin-codegen", version.ref = "squareMoshi" }
23+
webkit = { group = "androidx.webkit", name = "webkit", version.ref = "webkit" }
2224

2325
[plugins]
2426
android-application = { id = "com.android.application", version.ref = "agp" }

plugin/build.gradle.kts

Lines changed: 2 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -12,26 +12,6 @@ android {
1212
defaultConfig {
1313
minSdk = 26
1414
multiDexEnabled = false
15-
16-
ndk {
17-
abiFilters += arrayOf("arm64-v8a", "armeabi-v7a", "x86", "x86_64")
18-
}
19-
20-
externalNativeBuild {
21-
cmake {
22-
arguments += listOf(
23-
"-DANDROID_STL=c++_static",
24-
"-DANDROID_SUPPORT_FLEXIBLE_PAGE_SIZES=ON"
25-
)
26-
}
27-
}
28-
}
29-
30-
externalNativeBuild {
31-
cmake {
32-
path = file("src/main/jni/CMakeLists.txt")
33-
version = "3.22.1"
34-
}
3515
}
3616

3717
buildTypes {
@@ -86,6 +66,8 @@ dependencies {
8666
compileOnly(libs.mmrl.platform)
8767
compileOnly(libs.mmrl.jna)
8868

69+
compileOnly(libs.webkit)
70+
8971
compileOnly(libs.square.retrofit.moshi)
9072
compileOnly(libs.square.retrofit.kotlinxSerialization)
9173
compileOnly(libs.square.moshi)

plugin/src/main/jni/CMakeLists.txt

Lines changed: 0 additions & 19 deletions
This file was deleted.

plugin/src/main/jni/native-lib.cpp

Lines changed: 0 additions & 8 deletions
This file was deleted.

plugin/src/main/kotlin/dev/mmrl/Global.kt

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,19 @@
22

33
package dev.mmrl
44

5+
import android.util.Log
56
import android.webkit.JavascriptInterface
67
import androidx.core.content.pm.PackageInfoCompat
8+
import androidx.webkit.WebMessageCompat
9+
import androidx.webkit.WebViewCompat
10+
import androidx.webkit.WebViewFeature
711
import com.dergoogler.mmrl.platform.PlatformManager
12+
import com.dergoogler.mmrl.platform.file.SuFile
813
import com.dergoogler.mmrl.webui.interfaces.WXInterface
914
import com.dergoogler.mmrl.webui.interfaces.WXOptions
1015
import dev.mmrl.internal.WXUInterface
16+
import dev.mmrl.internal.initFsInputStream
17+
import dev.mmrl.internal.initFsOutputStream
1118
import dev.mmrl.module.Dialog
1219
import dev.mmrl.module.FileSystem
1320
import dev.mmrl.module.Module
@@ -24,6 +31,11 @@ class Global(wxOptions: WXOptions) : WXInterface(wxOptions) {
2431

2532
override var name = "global"
2633

34+
init {
35+
initFsInputStream()
36+
initFsOutputStream()
37+
}
38+
2739
private val modules: List<Class<out WXUInterface>> = listOf(
2840
FileSystem::class.java,
2941
Reflection::class.java,
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
package dev.mmrl.internal
2+
3+
import androidx.webkit.WebMessageCompat
4+
import androidx.webkit.WebViewCompat
5+
import androidx.webkit.WebViewFeature
6+
import com.dergoogler.mmrl.platform.file.SuFile
7+
import com.dergoogler.mmrl.webui.interfaces.WXInterface
8+
9+
10+
private val WXInterface.isFsInputStreamAllowed: Boolean get() = "wxu.permission.FS_INPUT_STREAM" in config.permissions
11+
private val WXInterface.isFsOutputStreamAllowed: Boolean get() = "wxu.permission.FS_OUTPUT_STREAM" in config.permissions
12+
13+
fun WXInterface.initFsInputStream() {
14+
if (!isFsInputStreamAllowed || !WebViewFeature.isFeatureSupported(WebViewFeature.WEB_MESSAGE_LISTENER)) {
15+
return
16+
}
17+
18+
WebViewCompat.addWebMessageListener(
19+
webView,
20+
"FsInputStream",
21+
setOf("*")
22+
) { view, message, sourceOrigin, isMainFrame, reply ->
23+
val data: String? = message.data
24+
25+
if (data == null) {
26+
reply.postMessage("Failed! Data was null.")
27+
return@addWebMessageListener
28+
}
29+
30+
val file = SuFile(message.data)
31+
32+
if (!file.exists()) {
33+
reply.postMessage("Failed! File does not exist.")
34+
return@addWebMessageListener
35+
}
36+
37+
when (message.type) {
38+
WebMessageCompat.TYPE_STRING -> {
39+
if (WebViewFeature.isFeatureSupported(WebViewFeature.WEB_MESSAGE_ARRAY_BUFFER)) {
40+
try {
41+
val bytes = file.newInputStream().use { it.readBytes() }
42+
reply.postMessage(bytes)
43+
} catch (e: Exception) {
44+
reply.postMessage("Failed! ${e.message}")
45+
}
46+
} else {
47+
reply.postMessage("Failed! WebMessageCompat.TYPE_ARRAY_BUFFER not supported.")
48+
}
49+
}
50+
51+
else -> {}
52+
}
53+
}
54+
}
55+
56+
fun WXInterface.initFsOutputStream() {
57+
if (!isFsOutputStreamAllowed || !WebViewFeature.isFeatureSupported(WebViewFeature.WEB_MESSAGE_LISTENER)) {
58+
return
59+
}
60+
61+
var currentPath: String? = null
62+
63+
WebViewCompat.addWebMessageListener(
64+
webView,
65+
"FsOutputStream",
66+
setOf("*")
67+
) { view, message, sourceOrigin, isMainFrame, reply ->
68+
69+
when (message.type) {
70+
WebMessageCompat.TYPE_STRING -> {
71+
// Initialize the file path
72+
currentPath = message.data
73+
if (currentPath == null) {
74+
reply.postMessage("Failed! Path was null.")
75+
return@addWebMessageListener
76+
}
77+
78+
val file = SuFile(currentPath)
79+
if (!file.exists()) {
80+
try {
81+
file.createNewFile()
82+
} catch (e: Exception) {
83+
reply.postMessage("Failed to create file: ${e.message}")
84+
return@addWebMessageListener
85+
}
86+
}
87+
88+
reply.postMessage("Path set")
89+
}
90+
91+
WebMessageCompat.TYPE_ARRAY_BUFFER -> {
92+
if (currentPath == null) {
93+
reply.postMessage("Failed! Path not set before sending chunk.")
94+
return@addWebMessageListener
95+
}
96+
97+
try {
98+
val bytes = message.arrayBuffer
99+
val file = SuFile(currentPath)
100+
file.newOutputStream(false).use { it.write(bytes) }
101+
reply.postMessage("Chunk written: ${bytes.size} bytes")
102+
} catch (e: Exception) {
103+
reply.postMessage("Failed to write chunk: ${e.message}")
104+
}
105+
}
106+
107+
else -> {
108+
reply.postMessage("Failed! Unsupported message type: ${message.type}")
109+
}
110+
}
111+
}
112+
}

plugin/src/main/kotlin/dev/mmrl/module/Dialog.kt

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,12 @@ import androidx.compose.runtime.remember
1111
import androidx.compose.runtime.setValue
1212
import androidx.compose.ui.platform.ComposeView
1313
import androidx.compose.ui.window.DialogProperties
14-
import com.dergoogler.mmrl.webui.interfaces.WXInterface
1514
import com.dergoogler.mmrl.webui.interfaces.WXOptions
1615
import dev.mmrl.internal.WXUInterface
1716

1817
class Dialog(wxOptions: WXOptions) : WXUInterface(wxOptions) {
1918
override var name = "dialog"
2019

21-
22-
2320
@JavascriptInterface
2421
fun show() {
2522
val act = activity

plugin/src/main/kotlin/dev/mmrl/util/Extensions.kt

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,17 @@
11
package dev.mmrl.util
22

3+
import android.R.id.input
34
import android.graphics.Bitmap
45
import android.graphics.Canvas
56
import android.graphics.drawable.BitmapDrawable
67
import android.graphics.drawable.Drawable
78
import android.util.Base64
9+
import androidx.core.graphics.createBitmap
810
import com.dergoogler.mmrl.webui.moshi
911
import java.io.ByteArrayOutputStream
10-
import androidx.core.graphics.createBitmap
12+
import java.io.IOException
13+
import java.io.InputStream
14+
1115

1216
fun <T> List<T>?.toJsonString(): String {
1317
if (this == null) return "[]"

ts/build.ts

Lines changed: 11 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,15 @@ import { join, relative } from "path";
77

88
const baseDir = "./src";
99

10-
generateIndexes(baseDir, [{ dir: "types", isTypes: true }, { dir: "classes" }]);
10+
generateIndexes(baseDir, [
11+
{ dir: "types", isTypes: true },
12+
{ dir: "classes" },
13+
{ dir: "functions" },
14+
{ dir: "interfaces", isTypes: true },
15+
]);
1116

1217
// Get Git commit count
13-
const count = parseInt(
14-
execSync("git rev-list --count HEAD").toString().trim(),
15-
10
16-
);
18+
const count = parseInt(execSync("git rev-list --count HEAD").toString().trim(), 10);
1719

1820
// Compute MAJOR.MINOR.PATCH
1921
const MAJOR = Math.floor(count / 10000);
@@ -85,9 +87,7 @@ function generateIndexes(baseDir: string, configs: IndexConfig[]) {
8587
.map((file: string) => {
8688
if (file.endsWith("index.ts")) return;
8789
if (file.endsWith(".ts")) {
88-
let relPath =
89-
"./" +
90-
relative(absDir, file).replace(/\\/g, "/").replace(/\.ts$/, "");
90+
let relPath = "./" + relative(absDir, file).replace(/\\/g, "/").replace(/\.ts$/, "");
9191
return `export ${isTypes ? "type " : ""}* from "${relPath}";`;
9292
}
9393
})
@@ -104,25 +104,15 @@ ${imports}`
104104
);
105105

106106
// Add export from this index to baseDir index
107-
const relPathToBase =
108-
"./" +
109-
relative(baseDir, join(absDir, indexFileName))
110-
.replace(/\\/g, "/")
111-
.replace(/\.ts$/, "");
112-
baseExports.push(
113-
`export ${isTypes ? "type " : ""}* from "${relPathToBase}";`
114-
);
107+
const relPathToBase = "./" + relative(baseDir, join(absDir, indexFileName)).replace(/\\/g, "/").replace(/\.ts$/, "");
108+
baseExports.push(`export ${isTypes ? "type " : ""}* from "${relPathToBase}";`);
115109

116110
if (subDirs.length > 0) {
117111
let exports: string[] = [];
118112
subDirs.forEach((subDir) => {
119113
const indexPath = join(baseDir, subDir, "index.ts");
120114
if (existsSync(indexPath)) {
121-
const relPath =
122-
"./" +
123-
relative(absDir, indexPath)
124-
.replace(/\\/g, "/")
125-
.replace(/\.ts$/, "");
115+
const relPath = "./" + relative(absDir, indexPath).replace(/\\/g, "/").replace(/\.ts$/, "");
126116
exports.push(`export ${isTypes ? "type " : ""}* from "${relPath}";`);
127117
}
128118
});

ts/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "wxu",
3-
"version": "0.0.86",
3+
"version": "0.0.87",
44
"type": "module",
55
"description": "WXU TypeScript Package",
66
"source": "src/index.ts",

0 commit comments

Comments
 (0)