manager: mount modules webui resources to manager directly.
This commit is contained in:
@@ -5,6 +5,7 @@ import coil.Coil
|
|||||||
import coil.ImageLoader
|
import coil.ImageLoader
|
||||||
import me.zhanghai.android.appiconloader.coil.AppIconFetcher
|
import me.zhanghai.android.appiconloader.coil.AppIconFetcher
|
||||||
import me.zhanghai.android.appiconloader.coil.AppIconKeyer
|
import me.zhanghai.android.appiconloader.coil.AppIconKeyer
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
lateinit var ksuApp: KernelSUApplication
|
lateinit var ksuApp: KernelSUApplication
|
||||||
|
|
||||||
@@ -24,6 +25,11 @@ class KernelSUApplication : Application() {
|
|||||||
}
|
}
|
||||||
.build()
|
.build()
|
||||||
)
|
)
|
||||||
|
|
||||||
|
val webroot = File(dataDir, "webroot")
|
||||||
|
if (!webroot.exists()) {
|
||||||
|
webroot.mkdir()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -15,34 +15,26 @@ import androidx.compose.runtime.DisposableEffect
|
|||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.platform.LocalLifecycleOwner
|
|
||||||
import androidx.core.view.WindowCompat
|
import androidx.core.view.WindowCompat
|
||||||
import androidx.core.view.WindowInsetsCompat
|
import androidx.core.view.WindowInsetsCompat
|
||||||
import androidx.core.view.WindowInsetsControllerCompat
|
import androidx.core.view.WindowInsetsControllerCompat
|
||||||
import androidx.lifecycle.lifecycleScope
|
|
||||||
import com.google.accompanist.web.WebView
|
import com.google.accompanist.web.WebView
|
||||||
import com.google.accompanist.web.rememberWebViewState
|
import com.google.accompanist.web.rememberWebViewState
|
||||||
import com.ramcosta.composedestinations.annotation.Destination
|
import com.ramcosta.composedestinations.annotation.Destination
|
||||||
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
|
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
|
||||||
import com.topjohnwu.superuser.ShellUtils
|
import com.topjohnwu.superuser.ShellUtils
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import kotlinx.coroutines.withContext
|
|
||||||
import me.weishu.kernelsu.ui.util.createRootShell
|
import me.weishu.kernelsu.ui.util.createRootShell
|
||||||
import me.weishu.kernelsu.ui.util.serveModule
|
import me.weishu.kernelsu.ui.util.serveModule
|
||||||
import java.net.ServerSocket
|
|
||||||
|
|
||||||
@SuppressLint("SetJavaScriptEnabled")
|
@SuppressLint("SetJavaScriptEnabled")
|
||||||
@Destination
|
@Destination
|
||||||
@Composable
|
@Composable
|
||||||
fun WebScreen(navigator: DestinationsNavigator, moduleId: String, moduleName: String) {
|
fun WebScreen(navigator: DestinationsNavigator, moduleId: String, moduleName: String) {
|
||||||
|
|
||||||
val port = 8080
|
|
||||||
LaunchedEffect(Unit) {
|
LaunchedEffect(Unit) {
|
||||||
serveModule(moduleId, port)
|
serveModule(moduleId)
|
||||||
}
|
}
|
||||||
|
|
||||||
val lifecycleOwner = LocalLifecycleOwner.current
|
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
|
|
||||||
DisposableEffect(Unit) {
|
DisposableEffect(Unit) {
|
||||||
@@ -50,15 +42,12 @@ fun WebScreen(navigator: DestinationsNavigator, moduleId: String, moduleName: St
|
|||||||
if (WebViewInterface.isHideSystemUI && context is Activity) {
|
if (WebViewInterface.isHideSystemUI && context is Activity) {
|
||||||
showSystemUI(context.window)
|
showSystemUI(context.window)
|
||||||
}
|
}
|
||||||
lifecycleOwner.lifecycleScope.launch {
|
|
||||||
stopServer(port)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Scaffold { innerPadding ->
|
Scaffold { innerPadding ->
|
||||||
WebView(
|
WebView(
|
||||||
state = rememberWebViewState(url = "http://localhost:$port"),
|
state = rememberWebViewState(url = "file:///data/data/me.weishu.kernelsu/webroot/index.html"),
|
||||||
Modifier
|
Modifier
|
||||||
.fillMaxSize()
|
.fillMaxSize()
|
||||||
.padding(innerPadding),
|
.padding(innerPadding),
|
||||||
@@ -66,6 +55,7 @@ fun WebScreen(navigator: DestinationsNavigator, moduleId: String, moduleName: St
|
|||||||
android.webkit.WebView(context).apply {
|
android.webkit.WebView(context).apply {
|
||||||
settings.javaScriptEnabled = true
|
settings.javaScriptEnabled = true
|
||||||
settings.domStorageEnabled = true
|
settings.domStorageEnabled = true
|
||||||
|
settings.allowFileAccess = true
|
||||||
addJavascriptInterface(WebViewInterface(context), "ksu")
|
addJavascriptInterface(WebViewInterface(context), "ksu")
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -100,22 +90,6 @@ class WebViewInterface(val context: Context) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun getFreePort(): Int {
|
|
||||||
return withContext(Dispatchers.IO) {
|
|
||||||
ServerSocket(0).use { socket -> socket.localPort }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private suspend fun stopServer(port: Int) {
|
|
||||||
withContext(Dispatchers.IO) {
|
|
||||||
runCatching {
|
|
||||||
okhttp3.OkHttpClient()
|
|
||||||
.newCall(okhttp3.Request.Builder().url("http://localhost:$port/stop").build())
|
|
||||||
.execute()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun hideSystemUI(window: Window) {
|
private fun hideSystemUI(window: Window) {
|
||||||
WindowCompat.setDecorFitsSystemWindows(window, false)
|
WindowCompat.setDecorFitsSystemWindows(window, false)
|
||||||
WindowInsetsControllerCompat(window, window.decorView).let { controller ->
|
WindowInsetsControllerCompat(window, window.decorView).let { controller ->
|
||||||
|
|||||||
@@ -131,13 +131,13 @@ fun installModule(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun serveModule(id: String, port: Int): Process {
|
fun serveModule(id: String): Boolean {
|
||||||
// we should use a new root shell to avoid blocking the global shell
|
// we should use a new root shell to avoid blocking the global shell
|
||||||
val process = Runtime.getRuntime().exec("${getKsuDaemonPath()} debug su")
|
val shell = createRootShell()
|
||||||
val builder = Shell.Builder.create()
|
return ShellUtils.fastCmdResult(
|
||||||
val shell = builder.build(process)
|
shell,
|
||||||
shell.newJob().add("${getKsuDaemonPath()} module serve $id $port").submit()
|
"${getKsuDaemonPath()} module link-manager $id ${android.os.Process.myPid()} ${BuildConfig.APPLICATION_ID}"
|
||||||
return process
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun reboot(reason: String = "") {
|
fun reboot(reason: String = "") {
|
||||||
|
|||||||
Reference in New Issue
Block a user