manager: update webuix

This commit is contained in:
Der_Googler
2025-05-30 01:19:39 +02:00
parent 1eb6eceb2d
commit 18219a40b0
7 changed files with 117 additions and 144 deletions
+1
View File
@@ -108,6 +108,7 @@ dependencies {
implementation(libs.androidx.lifecycle.runtime.compose) implementation(libs.androidx.lifecycle.runtime.compose)
implementation(libs.androidx.lifecycle.runtime.ktx) implementation(libs.androidx.lifecycle.runtime.ktx)
implementation(libs.androidx.lifecycle.viewmodel.compose) implementation(libs.androidx.lifecycle.viewmodel.compose)
implementation(libs.androidx.swiperefreshlayout)
implementation(libs.compose.destinations.core) implementation(libs.compose.destinations.core)
ksp(libs.compose.destinations.ksp) ksp(libs.compose.destinations.ksp)
@@ -71,12 +71,7 @@ class MainActivity : ComponentActivity() {
if (isManager) install() if (isManager) install()
setContent { setContent {
// Read AMOLED mode preference
val prefs = getSharedPreferences("settings", Context.MODE_PRIVATE)
val amoledMode = prefs.getBoolean("enable_amoled", false)
KernelSUTheme ( KernelSUTheme (
amoledMode = amoledMode
) { ) {
val navController = rememberNavController() val navController = rememberNavController()
val snackBarHostState = remember { SnackbarHostState() } val snackBarHostState = remember { SnackbarHostState() }
@@ -1,20 +1,19 @@
package com.rifsxd.ksunext.ui.theme package com.rifsxd.ksunext.ui.theme
import android.content.Context
import android.content.res.Configuration
import android.content.res.Resources
import android.os.Build import android.os.Build
import androidx.activity.SystemBarStyle import androidx.compose.material3.ColorScheme
import androidx.activity.ComponentActivity
import androidx.activity.enableEdgeToEdge
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.darkColorScheme import androidx.compose.material3.darkColorScheme
import androidx.compose.material3.dynamicDarkColorScheme import androidx.compose.material3.dynamicDarkColorScheme
import androidx.compose.material3.dynamicLightColorScheme import androidx.compose.material3.dynamicLightColorScheme
import androidx.compose.material3.lightColorScheme import androidx.compose.material3.lightColorScheme
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.SideEffect
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.toArgb
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import com.dergoogler.mmrl.ui.component.StatusBarStyle
private val DarkColorScheme = darkColorScheme( private val DarkColorScheme = darkColorScheme(
primary = PRIMARY, primary = PRIMARY,
@@ -38,38 +37,58 @@ fun Color.blend(other: Color, ratio: Float): Color {
) )
} }
@Composable /**
fun KernelSUTheme( * AMOLED colors are handled through the context
*/
fun Context.getColorScheme(
darkTheme: Boolean = isSystemInDarkTheme(), darkTheme: Boolean = isSystemInDarkTheme(),
): ColorScheme {
// Read AMOLED mode preference
val prefs = getSharedPreferences("settings", Context.MODE_PRIVATE)
val amoledMode = prefs.getBoolean("enable_amoled", false)
// Dynamic color is available on Android 12+ // Dynamic color is available on Android 12+
dynamicColor: Boolean = true, val dynamicColor = true
amoledMode: Boolean = false,
content: @Composable () -> Unit return when {
) {
val colorScheme = when {
amoledMode && darkTheme && dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> { amoledMode && darkTheme && dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
val context = LocalContext.current val dynamicScheme = dynamicDarkColorScheme(this)
val dynamicScheme = dynamicDarkColorScheme(context)
dynamicScheme.copy( dynamicScheme.copy(
background = AMOLED_BLACK, background = AMOLED_BLACK,
surface = AMOLED_BLACK, surface = AMOLED_BLACK,
surfaceVariant = dynamicScheme.surfaceVariant.blend(AMOLED_BLACK, 0.6f), surfaceVariant = dynamicScheme.surfaceVariant.blend(AMOLED_BLACK, 0.6f),
surfaceContainer = dynamicScheme.surfaceContainer.blend(AMOLED_BLACK, 0.6f), surfaceContainer = dynamicScheme.surfaceContainer.blend(AMOLED_BLACK, 0.6f),
surfaceContainerLow = dynamicScheme.surfaceContainerLow.blend(AMOLED_BLACK, 0.6f), surfaceContainerLow = dynamicScheme.surfaceContainerLow.blend(AMOLED_BLACK, 0.6f),
surfaceContainerLowest = dynamicScheme.surfaceContainerLowest.blend(AMOLED_BLACK, 0.6f), surfaceContainerLowest = dynamicScheme.surfaceContainerLowest.blend(
AMOLED_BLACK,
0.6f
),
surfaceContainerHigh = dynamicScheme.surfaceContainerHigh.blend(AMOLED_BLACK, 0.6f), surfaceContainerHigh = dynamicScheme.surfaceContainerHigh.blend(AMOLED_BLACK, 0.6f),
surfaceContainerHighest = dynamicScheme.surfaceContainerHighest.blend(AMOLED_BLACK, 0.6f), surfaceContainerHighest = dynamicScheme.surfaceContainerHighest.blend(
AMOLED_BLACK,
0.6f
),
) )
} }
dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> { dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
val context = LocalContext.current if (darkTheme) dynamicDarkColorScheme(this) else dynamicLightColorScheme(this)
if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context)
} }
darkTheme -> DarkColorScheme darkTheme -> DarkColorScheme
else -> LightColorScheme else -> LightColorScheme
} }
}
SystemBarStyle( @Composable
fun KernelSUTheme(
darkTheme: Boolean = isSystemInDarkTheme(),
content: @Composable () -> Unit,
) {
val context = LocalContext.current
val colorScheme = context.getColorScheme(darkTheme)
StatusBarStyle(
darkMode = darkTheme darkMode = darkTheme
) )
@@ -80,31 +99,7 @@ fun KernelSUTheme(
) )
} }
@Composable internal fun isSystemInDarkTheme(): Boolean {
private fun SystemBarStyle( val uiMode = Resources.getSystem().configuration.uiMode
darkMode: Boolean, return (uiMode and Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES
statusBarScrim: Color = Color.Transparent,
navigationBarScrim: Color = Color.Transparent,
) {
val context = LocalContext.current
val activity = context as ComponentActivity
SideEffect {
activity.enableEdgeToEdge(
statusBarStyle = SystemBarStyle.auto(
statusBarScrim.toArgb(),
statusBarScrim.toArgb(),
) { darkMode },
navigationBarStyle = when {
darkMode -> SystemBarStyle.dark(
navigationBarScrim.toArgb()
)
else -> SystemBarStyle.light(
navigationBarScrim.toArgb(),
navigationBarScrim.toArgb(),
)
}
)
}
} }
@@ -17,14 +17,15 @@ import androidx.core.view.updateLayoutParams
import androidx.webkit.WebViewAssetLoader import androidx.webkit.WebViewAssetLoader
import com.dergoogler.mmrl.platform.model.ModId import com.dergoogler.mmrl.platform.model.ModId
import com.dergoogler.mmrl.webui.interfaces.WXOptions import com.dergoogler.mmrl.webui.interfaces.WXOptions
import com.dergoogler.mmrl.webui.util.WebUIOptions
import com.dergoogler.mmrl.webui.view.WXView
import com.dergoogler.mmrl.webui.view.WebUIView
import com.topjohnwu.superuser.Shell import com.topjohnwu.superuser.Shell
import com.rifsxd.ksunext.ui.util.createRootShell import com.rifsxd.ksunext.ui.util.createRootShell
import java.io.File import java.io.File
@SuppressLint("SetJavaScriptEnabled") @SuppressLint("SetJavaScriptEnabled")
class WebUIActivity : ComponentActivity() { class WebUIActivity : ComponentActivity() {
private lateinit var webviewInterface: WebViewInterface
private var rootShell: Shell? = null private var rootShell: Shell? = null
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
@@ -71,7 +72,12 @@ class WebUIActivity : ComponentActivity() {
} }
} }
val webView = WebView(this).apply { val options = WebUIOptions(
modId = ModId(moduleId),
context = this,
)
val webView = WebUIView(options).apply {
ViewCompat.setOnApplyWindowInsetsListener(this) { view, insets -> ViewCompat.setOnApplyWindowInsetsListener(this) { view, insets ->
val inset = insets.getInsets(WindowInsetsCompat.Type.systemBars()) val inset = insets.getInsets(WindowInsetsCompat.Type.systemBars())
view.updateLayoutParams<MarginLayoutParams> { view.updateLayoutParams<MarginLayoutParams> {
@@ -82,15 +88,15 @@ class WebUIActivity : ComponentActivity() {
} }
return@setOnApplyWindowInsetsListener insets return@setOnApplyWindowInsetsListener insets
} }
val factory = WebViewInterface.factory()
settings.javaScriptEnabled = true settings.javaScriptEnabled = true
settings.domStorageEnabled = true settings.domStorageEnabled = true
settings.allowFileAccess = false settings.allowFileAccess = false
webviewInterface = WebViewInterface( addJavascriptInterface(factory)
WXOptions(this@WebUIActivity, this, ModId(moduleId))
)
addJavascriptInterface(webviewInterface, "ksu")
setWebViewClient(webViewClient) setWebViewClient(webViewClient)
loadUrl("https://mui.kernelsu.org/index.html") loadDomain()
} }
setContentView(webView) setContentView(webView)
@@ -1,32 +1,19 @@
package com.rifsxd.ksunext.ui.webui package com.rifsxd.ksunext.ui.webui
import android.app.ActivityManager
import android.os.Build import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.webkit.WebView import android.widget.Toast
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import com.dergoogler.mmrl.platform.Platform import com.dergoogler.mmrl.platform.Platform
import com.dergoogler.mmrl.platform.model.ModId import com.dergoogler.mmrl.webui.activity.WXActivity
import com.dergoogler.mmrl.ui.component.Loading import com.dergoogler.mmrl.webui.util.WebUIOptions
import com.dergoogler.mmrl.webui.screen.WebUIScreen import com.dergoogler.mmrl.webui.view.WebUIXView
import com.dergoogler.mmrl.webui.util.rememberWebUIOptions
import com.rifsxd.ksunext.BuildConfig import com.rifsxd.ksunext.BuildConfig
import com.rifsxd.ksunext.ui.theme.KernelSUTheme import com.rifsxd.ksunext.ui.theme.getColorScheme
import kotlinx.coroutines.delay import com.rifsxd.ksunext.ui.theme.isSystemInDarkTheme
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
class WebUIXActivity : ComponentActivity() { class WebUIXActivity : WXActivity() {
private lateinit var webView: WebView
private val userAgent private val userAgent
get(): String { get(): String {
val ksuVersion = BuildConfig.VERSION_CODE val ksuVersion = BuildConfig.VERSION_CODE
@@ -45,69 +32,59 @@ class WebUIXActivity : ComponentActivity() {
return "KernelSU Next/$ksuVersion (Linux; Android $osVersion; $deviceModel; $platform/$platformVersion)" return "KernelSU Next/$ksuVersion (Linux; Android $osVersion; $deviceModel; $platform/$platformVersion)"
} }
override fun onCreate(savedInstanceState: Bundle?) { override fun onRender(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onRender(savedInstanceState)
enableEdgeToEdge()
webView = WebView(this) if (this.modId == null) {
val msg = "ModId cannot be null"
lifecycleScope.launch { Toast.makeText(this, msg, Toast.LENGTH_SHORT).show()
initPlatform() throw IllegalArgumentException(msg)
} }
val moduleId = intent.getStringExtra("id")!! // Cast since we check it
val name = intent.getStringExtra("name")!! val modId = this.modId!!
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
@Suppress("DEPRECATION")
setTaskDescription(ActivityManager.TaskDescription("KernelSU Next - $name"))
} else {
val taskDescription =
ActivityManager.TaskDescription.Builder().setLabel("KernelSU Next - $name").build()
setTaskDescription(taskDescription)
}
val prefs = getSharedPreferences("settings", MODE_PRIVATE) val prefs = getSharedPreferences("settings", MODE_PRIVATE)
val webDebugging = prefs.getBoolean("enable_web_debugging", false)
val erudaInject = prefs.getBoolean("use_webuix_eruda", false)
setContent {
KernelSUTheme {
var isLoading by remember { mutableStateOf(true) }
LaunchedEffect(Platform.isAlive) { val options = WebUIOptions(
while (!Platform.isAlive) { modId = modId,
delay(1000) context = this,
} debug = webDebugging,
appVersionCode = BuildConfig.VERSION_CODE,
isDarkMode = isSystemInDarkTheme(),
enableEruda = erudaInject,
cls = WebUIXActivity::class.java,
userAgentString = userAgent,
colorScheme = getColorScheme()
)
isLoading = false val view = WebUIXView(options).apply {
} wx.addJavascriptInterface(WebViewInterface.factory())
wx.loadDomain()
}
if (isLoading) { this.options = options
Loading() this.view = view
return@KernelSUTheme
}
val webDebugging = prefs.getBoolean("enable_web_debugging", false) // Ensure type safety
val erudaInject = prefs.getBoolean("use_webuix_eruda", false) val name = intent.getStringExtra("name")
val dark = isSystemInDarkTheme() if (name != null) {
setActivityTitle("KernelSU Next - $name")
}
val options = rememberWebUIOptions( val loading = createLoadingRenderer()
modId = ModId(moduleId), setContentView(loading)
debug = webDebugging,
appVersionCode = BuildConfig.VERSION_CODE,
isDarkMode = dark,
enableEruda = erudaInject,
cls = WebUIXActivity::class.java,
userAgentString = userAgent
)
WebUIScreen( lifecycleScope.launch {
webView = webView, val deferred = Platform.getAsyncDeferred(this, null) {
options = options, view
interfaces = listOf(
WebViewInterface.factory()
)
)
} }
setContentView(deferred.await())
} }
} }
} }
@@ -1,14 +1,12 @@
package com.rifsxd.ksunext.ui.webui package com.rifsxd.ksunext.ui.webui
import android.app.Activity
import android.os.Handler
import android.os.Looper
import android.text.TextUtils import android.text.TextUtils
import android.view.Window import android.view.Window
import android.webkit.JavascriptInterface import android.webkit.JavascriptInterface
import android.widget.Toast import android.widget.Toast
import androidx.core.view.WindowInsetsCompat import androidx.core.view.WindowInsetsCompat
import androidx.core.view.WindowInsetsControllerCompat import androidx.core.view.WindowInsetsControllerCompat
import com.dergoogler.mmrl.platform.model.ModId.Companion.moduleDir
import com.dergoogler.mmrl.webui.interfaces.WXInterface import com.dergoogler.mmrl.webui.interfaces.WXInterface
import com.dergoogler.mmrl.webui.interfaces.WXOptions import com.dergoogler.mmrl.webui.interfaces.WXOptions
import com.dergoogler.mmrl.webui.model.JavaScriptInterface import com.dergoogler.mmrl.webui.model.JavaScriptInterface
@@ -20,20 +18,19 @@ import com.rifsxd.ksunext.ui.util.listModules
import com.rifsxd.ksunext.ui.util.withNewRootShell import com.rifsxd.ksunext.ui.util.withNewRootShell
import org.json.JSONArray import org.json.JSONArray
import org.json.JSONObject import org.json.JSONObject
import java.io.File
import java.util.concurrent.CompletableFuture import java.util.concurrent.CompletableFuture
class WebViewInterface( class WebViewInterface(
wxOptions: WXOptions, wxOptions: WXOptions,
) : WXInterface(wxOptions) { ) : WXInterface(wxOptions) {
override var name: String = "ksu" override var name: String = "ksu"
// Add logging tag for console.log
override var tag: String = "KernelSUInterface"
companion object { companion object {
fun factory() = JavaScriptInterface(WebViewInterface::class.java) fun factory() = JavaScriptInterface(WebViewInterface::class.java)
} }
private val modDir get() = "/data/adb/modules/${modId.id}"
@JavascriptInterface @JavascriptInterface
fun exec(cmd: String): String { fun exec(cmd: String): String {
return withNewRootShell(true) { ShellUtils.fastCmd(this, cmd) } return withNewRootShell(true) { ShellUtils.fastCmd(this, cmd) }
@@ -171,23 +168,22 @@ class WebViewInterface(
@JavascriptInterface @JavascriptInterface
fun fullScreen(enable: Boolean) { fun fullScreen(enable: Boolean) {
if (context is Activity) { runMainLooperPost {
Handler(Looper.getMainLooper()).post { if (enable) {
if (enable) { hideSystemUI(window)
hideSystemUI(activity.window) } else {
} else { showSystemUI(window)
showSystemUI(activity.window)
}
} }
} }
} }
@JavascriptInterface @JavascriptInterface
fun moduleInfo(): String { fun moduleInfo(): String {
val modDir = modId.moduleDir
val moduleInfos = JSONArray(listModules()) val moduleInfos = JSONArray(listModules())
val currentModuleInfo = JSONObject() val currentModuleInfo = JSONObject()
currentModuleInfo.put("moduleDir", modDir) currentModuleInfo.put("moduleDir", modDir)
val moduleId = File(modDir).getName() val moduleId = modDir.getName()
for (i in 0 until moduleInfos.length()) { for (i in 0 until moduleInfos.length()) {
val currentInfo = moduleInfos.getJSONObject(i) val currentInfo = moduleInfos.getJSONObject(i)
+4 -1
View File
@@ -17,7 +17,8 @@ parcelablelist = "2.0.1"
libsu = "6.0.0" libsu = "6.0.0"
apksign = "1.4" apksign = "1.4"
cmaker = "1.2" cmaker = "1.2"
mmrl = "817d76c8b5" mmrl = "fcb3a1fb76"
swiperefreshlayout = "1.1.0"
[plugins] [plugins]
agp-app = { id = "com.android.application", version.ref = "agp" } agp-app = { id = "com.android.application", version.ref = "agp" }
@@ -36,6 +37,8 @@ androidx-activity-compose = { group = "androidx.activity", name = "activity-comp
androidx-navigation-compose = { group = "androidx.navigation", name = "navigation-compose", version.ref = "navigation" } androidx-navigation-compose = { group = "androidx.navigation", name = "navigation-compose", version.ref = "navigation" }
androidx-swiperefreshlayout = { group = "androidx.swiperefreshlayout", name = "swiperefreshlayout", version.ref = "swiperefreshlayout" }
androidx-compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "compose-bom" } androidx-compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "compose-bom" }
androidx-compose-material-icons-extended = { group = "androidx.compose.material", name = "material-icons-extended" } androidx-compose-material-icons-extended = { group = "androidx.compose.material", name = "material-icons-extended" }
androidx-compose-material = { group = "androidx.compose.material", name = "material" } androidx-compose-material = { group = "androidx.compose.material", name = "material" }