Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,13 @@ import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.updateLayoutParams
import androidx.webkit.WebViewAssetLoader
import com.topjohnwu.superuser.Shell
import me.weishu.kernelsu.ui.util.createRootShell
import java.io.File

@SuppressLint("SetJavaScriptEnabled")
class WebUIActivity : ComponentActivity() {
private lateinit var webviewInterface: WebViewInterface

private var rootShell: Shell? = null
private val rootShell by lazy { createRootShell(true) }
private var webView = null as WebView?

override fun onCreate(savedInstanceState: Bundle?) {

Expand All @@ -35,8 +33,8 @@ class WebUIActivity : ComponentActivity() {

super.onCreate(savedInstanceState)

val moduleId = intent.getStringExtra("id")!!
val name = intent.getStringExtra("name")!!
val moduleId = intent.getStringExtra("id") ?: finishAndRemoveTask().let { return }
val name = intent.getStringExtra("name") ?: finishAndRemoveTask().let { return }
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
@Suppress("DEPRECATION")
setTaskDescription(ActivityManager.TaskDescription("KernelSU - $name"))
Expand All @@ -50,7 +48,6 @@ class WebUIActivity : ComponentActivity() {

val moduleDir = "/data/adb/modules/${moduleId}"
val webRoot = File("${moduleDir}/webroot")
val rootShell = createRootShell(true).also { this.rootShell = it }
val webViewAssetLoader = WebViewAssetLoader.Builder()
.setDomain("mui.kernelsu.org")
.addPathHandler(
Expand All @@ -69,6 +66,8 @@ class WebUIActivity : ComponentActivity() {
}

val webView = WebView(this).apply {
webView = this

ViewCompat.setOnApplyWindowInsetsListener(this) { view, insets ->
val inset = insets.getInsets(WindowInsetsCompat.Type.systemBars())
view.updateLayoutParams<MarginLayoutParams> {
Expand All @@ -82,8 +81,7 @@ class WebUIActivity : ComponentActivity() {
settings.javaScriptEnabled = true
settings.domStorageEnabled = true
settings.allowFileAccess = false
webviewInterface = WebViewInterface(this@WebUIActivity, this, moduleDir)
addJavascriptInterface(webviewInterface, "ksu")
addJavascriptInterface(WebViewInterface(this@WebUIActivity, this, moduleDir), "ksu")
setWebViewClient(webViewClient)
loadUrl("https://mui.kernelsu.org/index.html")
}
Expand All @@ -92,7 +90,13 @@ class WebUIActivity : ComponentActivity() {
}

override fun onDestroy() {
rootShell.runCatching { close() }
webView?.apply {
stopLoading()
removeAllViews()
destroy()
webView = null
}
super.onDestroy()
runCatching { rootShell?.close() }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import org.json.JSONObject
import java.io.File
import java.util.concurrent.CompletableFuture

@Suppress("unused")
class WebViewInterface(
val context: Context,
private val webView: WebView,
Expand Down Expand Up @@ -61,56 +62,56 @@ class WebViewInterface(
options: String?,
callbackFunc: String
) {
val finalCommand = StringBuilder()
processOptions(finalCommand, options)
finalCommand.append(cmd)
val finalCommand = buildString {
processOptions(this, options)
append(cmd)
}

val result = withNewRootShell(true) {
newJob().add(finalCommand.toString()).to(ArrayList(), ArrayList()).exec()
newJob().add(finalCommand).to(ArrayList(), ArrayList()).exec()
}
val stdout = result.out.joinToString(separator = "\n")
val stderr = result.err.joinToString(separator = "\n")

val jsCode =
"javascript: (function() { try { ${callbackFunc}(${result.code}, ${
"(function() { try { ${callbackFunc}(${result.code}, ${
JSONObject.quote(
stdout
)
}, ${JSONObject.quote(stderr)}); } catch(e) { console.error(e); } })();"
webView.post {
webView.loadUrl(jsCode)
webView.evaluateJavascript(jsCode, null)
}
}

@JavascriptInterface
fun spawn(command: String, args: String, options: String?, callbackFunc: String) {
val finalCommand = StringBuilder()

processOptions(finalCommand, options)

if (!TextUtils.isEmpty(args)) {
finalCommand.append(command).append(" ")
JSONArray(args).let { argsArray ->
for (i in 0 until argsArray.length()) {
finalCommand.append(argsArray.getString(i))
finalCommand.append(" ")
val finalCommand = buildString {
processOptions(this, options)

if (!TextUtils.isEmpty(args)) {
append(command).append(" ")
JSONArray(args).let { argsArray ->
for (i in 0 until argsArray.length()) {
append("${argsArray.getString(i)} ")
}
}
} else {
append(command)
}
} else {
finalCommand.append(command)
}

val shell = createRootShell(true)

val emitData = fun(name: String, data: String) {
val jsCode =
"javascript: (function() { try { ${callbackFunc}.${name}.emit('data', ${
"(function() { try { ${callbackFunc}.${name}.emit('data', ${
JSONObject.quote(
data
)
}); } catch(e) { console.error('emitData', e); } })();"
webView.post {
webView.loadUrl(jsCode)
webView.evaluateJavascript(jsCode, null)
}
}

Expand All @@ -126,29 +127,29 @@ class WebViewInterface(
}
}

val future = shell.newJob().add(finalCommand.toString()).to(stdout, stderr).enqueue()
val future = shell.newJob().add(finalCommand).to(stdout, stderr).enqueue()
val completableFuture = CompletableFuture.supplyAsync {
future.get()
}

completableFuture.thenAccept { result ->
val emitExitCode =
"javascript: (function() { try { ${callbackFunc}.emit('exit', ${result.code}); } catch(e) { console.error(`emitExit error: \${e}`); } })();"
$$"(function() { try { $${callbackFunc}.emit('exit', $${result.code}); } catch(e) { console.error(`emitExit error: ${e}`); } })();"
webView.post {
webView.loadUrl(emitExitCode)
webView.evaluateJavascript(emitExitCode, null)
}

if (result.code != 0) {
val emitErrCode =
"javascript: (function() { try { var err = new Error(); err.exitCode = ${result.code}; err.message = ${
"(function() { try { var err = new Error(); err.exitCode = ${result.code}; err.message = ${
JSONObject.quote(
result.err.joinToString(
"\n"
)
)
};${callbackFunc}.emit('error', err); } catch(e) { console.error('emitErr', e); } })();"
webView.post {
webView.loadUrl(emitErrCode)
webView.evaluateJavascript(emitErrCode, null)
}
}
}.whenComplete { _, _ ->
Expand Down Expand Up @@ -179,7 +180,7 @@ class WebViewInterface(
@JavascriptInterface
fun moduleInfo(): String {
val moduleInfos = JSONArray(listModules())
var currentModuleInfo = JSONObject()
val currentModuleInfo = JSONObject()
currentModuleInfo.put("moduleDir", modDir)
val moduleId = File(modDir).getName()
for (i in 0 until moduleInfos.length()) {
Expand All @@ -189,7 +190,7 @@ class WebViewInterface(
continue
}

var keys = currentInfo.keys()
val keys = currentInfo.keys()
for (key in keys) {
currentModuleInfo.put(key, currentInfo.get(key))
}
Expand Down