diff --git a/manager/app/src/main/java/me/weishu/kernelsu/ui/webui/WebUIActivity.kt b/manager/app/src/main/java/me/weishu/kernelsu/ui/webui/WebUIActivity.kt index d926d7eada35..188615977178 100644 --- a/manager/app/src/main/java/me/weishu/kernelsu/ui/webui/WebUIActivity.kt +++ b/manager/app/src/main/java/me/weishu/kernelsu/ui/webui/WebUIActivity.kt @@ -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?) { @@ -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")) @@ -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( @@ -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 { @@ -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") } @@ -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() } } } \ No newline at end of file diff --git a/manager/app/src/main/java/me/weishu/kernelsu/ui/webui/WebViewInterface.kt b/manager/app/src/main/java/me/weishu/kernelsu/ui/webui/WebViewInterface.kt index 00fcde653b60..baa2b2aa1578 100644 --- a/manager/app/src/main/java/me/weishu/kernelsu/ui/webui/WebViewInterface.kt +++ b/manager/app/src/main/java/me/weishu/kernelsu/ui/webui/WebViewInterface.kt @@ -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, @@ -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) } } @@ -126,21 +127,21 @@ 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" @@ -148,7 +149,7 @@ class WebViewInterface( ) };${callbackFunc}.emit('error', err); } catch(e) { console.error('emitErr', e); } })();" webView.post { - webView.loadUrl(emitErrCode) + webView.evaluateJavascript(emitErrCode, null) } } }.whenComplete { _, _ -> @@ -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()) { @@ -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)) }