manager: Optimized import, optimized all libsu shell calls, and fixed WebUI memory leaks (#369)

* manager: simply optimize

* manager: optimize webui functions

* manager: detect selinux using libsu:io

* manager: optimize webui functions

* manager: use the default shell

* manager: optimize import

* manager: optimize shell builder

* manager: fix memory leaks

* manager: optimize magisk detection

* manager: use libsu

* manager: optimize webui

---------

Co-authored-by: 白彩恋 <shiro@oom-wg.dev>
This commit is contained in:
梦璃酱
2025-09-01 15:04:58 +08:00
committed by GitHub
parent 662a2b1237
commit c3533861f2
61 changed files with 544 additions and 1182 deletions

View File

@@ -12,10 +12,12 @@ import android.os.Bundle
import coil.Coil
import coil.ImageLoader
import com.dergoogler.mmrl.platform.Platform
import com.sukisu.ultra.ui.util.createRootShellBuilder
import com.topjohnwu.superuser.Shell
import me.zhanghai.android.appiconloader.coil.AppIconFetcher
import me.zhanghai.android.appiconloader.coil.AppIconKeyer
import java.io.File
import java.util.Locale
import java.util.*
@SuppressLint("StaticFieldLeak")
lateinit var ksuApp: KernelSUApplication
@@ -86,6 +88,8 @@ class KernelSUApplication : Application() {
override fun onCreate() {
super.onCreate()
ksuApp = this
Shell.setDefaultBuilder(createRootShellBuilder(true))
Shell.enableVerboseLogging = BuildConfig.DEBUG
// 注册Activity生命周期回调
registerActivityLifecycleCallbacks(activityLifecycleCallbacks)

View File

@@ -2,11 +2,7 @@ package com.sukisu.ultra.ui
import android.content.Intent
import android.content.pm.PackageInfo
import android.os.Binder
import android.os.IBinder
import android.os.IInterface
import android.os.Parcel
import android.os.UserManager
import android.os.*
import android.util.Log
import com.topjohnwu.superuser.ipc.RootService
import rikka.parcelablelist.ParcelableListSlice
@@ -60,7 +56,7 @@ class KsuService : RootService() {
val result = getPackages(flagsArg)
reply?.writeNoException()
reply?.writeInt(1)
result.writeToParcel(reply!!, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE)
result.writeToParcel(reply!!, Parcelable.PARCELABLE_WRITE_RETURN_VALUE)
return true
}
}

View File

@@ -7,18 +7,15 @@ import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.compose.animation.AnimatedContentTransitionScope
import androidx.compose.animation.EnterTransition
import androidx.compose.animation.ExitTransition
import androidx.compose.animation.*
import androidx.compose.animation.core.tween
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.animation.scaleOut
import androidx.compose.animation.slideInHorizontally
import androidx.compose.animation.slideOutHorizontally
import androidx.compose.foundation.layout.*
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Scaffold
import androidx.compose.material3.SnackbarHostState
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.lifecycle.lifecycleScope
import androidx.navigation.NavBackStackEntry
@@ -29,20 +26,19 @@ import com.ramcosta.composedestinations.animations.NavHostAnimatedDestinationSty
import com.ramcosta.composedestinations.generated.NavGraphs
import com.ramcosta.composedestinations.generated.destinations.ExecuteModuleActionScreenDestination
import com.ramcosta.composedestinations.spec.NavHostGraphSpec
import io.sukisu.ultra.UltraToolInstall
import com.sukisu.ultra.ksuApp
import zako.zako.zako.zakoui.activity.util.AppData
import com.sukisu.ultra.Natives
import com.sukisu.ultra.ui.screen.BottomBarDestination
import com.sukisu.ultra.ui.theme.*
import zako.zako.zako.zakoui.activity.util.*
import zako.zako.zako.zakoui.activity.component.BottomBar
import com.sukisu.ultra.ui.theme.KernelSUTheme
import com.sukisu.ultra.ui.util.LocalSnackbarHost
import com.sukisu.ultra.ui.util.install
import com.sukisu.ultra.ui.viewmodel.HomeViewModel
import com.sukisu.ultra.ui.viewmodel.SuperUserViewModel
import com.sukisu.ultra.ui.webui.initPlatform
import io.sukisu.ultra.UltraToolInstall
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.launch
import zako.zako.zako.zakoui.activity.component.BottomBar
import zako.zako.zako.zakoui.activity.util.*
class MainActivity : ComponentActivity() {
private lateinit var superUserViewModel: SuperUserViewModel
@@ -206,7 +202,7 @@ class MainActivity : ComponentActivity() {
// 初始化主题相关设置
ThemeUtils.initializeThemeSettings(this, settingsStateFlow)
val isManager = AppData.isManager(ksuApp.packageName)
val isManager = Natives.becomeManager(packageName)
if (isManager) {
install()
UltraToolInstall.tryToInstall()

View File

@@ -1,14 +1,7 @@
package com.sukisu.ultra.ui.component
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.ElevatedCard
@@ -21,11 +14,7 @@ import androidx.compose.ui.draw.scale
import androidx.compose.ui.res.colorResource
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.text.SpanStyle
import androidx.compose.ui.text.TextLinkStyles
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.fromHtml
import androidx.compose.ui.text.*
import androidx.compose.ui.text.style.TextDecoration
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
@@ -95,7 +84,7 @@ private fun AboutCardContent() {
Spacer(modifier = Modifier.height(8.dp))
val annotatedString = AnnotatedString.Companion.fromHtml(
val annotatedString = AnnotatedString.fromHtml(
htmlString = stringResource(
id = R.string.about_source_code,
"<b><a href=\"https://github.com/ShirkNeko/SukiSU-Ultra\">GitHub</a></b>",

View File

@@ -1,12 +1,14 @@
package com.sukisu.ultra.ui.component
import android.annotation.SuppressLint
import androidx.compose.animation.core.*
import androidx.compose.animation.*
import androidx.compose.runtime.*
import androidx.compose.animation.core.Spring
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.animation.core.spring
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
import androidx.compose.ui.draw.clip

View File

@@ -10,15 +10,20 @@ import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Check
import androidx.compose.material.icons.filled.Close
import androidx.compose.material.icons.filled.Fullscreen
import androidx.compose.material3.*
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.geometry.Size
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.layout.onSizeChanged
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
@@ -30,10 +35,6 @@ import com.sukisu.ultra.R
import com.sukisu.ultra.ui.util.BackgroundTransformation
import com.sukisu.ultra.ui.util.saveTransformedBackground
import kotlinx.coroutines.launch
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.geometry.Size
import androidx.compose.ui.layout.onSizeChanged
import kotlin.math.max
@Composable

View File

@@ -4,35 +4,15 @@ import android.util.Log
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.WindowInsetsSides
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.only
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.safeDrawing
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.outlined.ArrowBack
import androidx.compose.material.icons.filled.Close
import androidx.compose.material.icons.filled.Search
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.material3.TopAppBarScrollBehavior
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
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.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.FocusRequester

View File

@@ -3,12 +3,7 @@ package com.sukisu.ultra.ui.component
import androidx.compose.foundation.LocalIndication
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.selection.toggleable
import androidx.compose.material3.Icon
import androidx.compose.material3.ListItem
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.RadioButton
import androidx.compose.material3.Switch
import androidx.compose.material3.Text
import androidx.compose.material3.*
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier

View File

@@ -5,18 +5,18 @@ import androidx.compose.foundation.clickable
import androidx.compose.foundation.horizontalScroll
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.rememberScrollState
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.SdStorage
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import com.sukisu.ultra.R
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.SdStorage
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.vector.ImageVector
/**
* 槽位选择对话框组件

View File

@@ -5,62 +5,15 @@ import android.content.pm.PackageInfo
import android.content.pm.PackageManager
import android.graphics.drawable.Drawable
import android.util.Log
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.*
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.CheckCircle
import androidx.compose.material.icons.filled.Delete
import androidx.compose.material.icons.filled.Edit
import androidx.compose.material.icons.filled.Folder
import androidx.compose.material.icons.filled.PlayArrow
import androidx.compose.material.icons.filled.RadioButtonUnchecked
import androidx.compose.material.icons.filled.Search
import androidx.compose.material.icons.filled.Settings
import androidx.compose.material.icons.filled.Update
import androidx.compose.material.icons.filled.Visibility
import androidx.compose.material.icons.filled.VisibilityOff
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.Card
import androidx.compose.material3.CardDefaults
import androidx.compose.material3.DropdownMenuItem
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.ExposedDropdownMenuBox
import androidx.compose.material3.ExposedDropdownMenuDefaults
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.MenuAnchorType
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Surface
import androidx.compose.material3.Switch
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.material.icons.filled.*
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip

View File

@@ -1,37 +1,13 @@
package com.sukisu.ultra.ui.component
import android.annotation.SuppressLint
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Add
import androidx.compose.material.icons.filled.Apps
import androidx.compose.material.icons.filled.Folder
import androidx.compose.material.icons.filled.Loop
import androidx.compose.material.icons.filled.PlayArrow
import androidx.compose.material.icons.filled.Security
import androidx.compose.material.icons.filled.Settings
import androidx.compose.material.icons.filled.Storage
import androidx.compose.material3.Button
import androidx.compose.material3.Card
import androidx.compose.material3.CardDefaults
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Switch
import androidx.compose.material3.Text
import androidx.compose.material.icons.filled.*
import androidx.compose.material3.*
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.remember

View File

@@ -1,7 +1,9 @@
package com.sukisu.ultra.ui.component
import androidx.compose.animation.*
import androidx.compose.animation.core.*
import androidx.compose.animation.core.FastOutSlowInEasing
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.animation.core.tween
import androidx.compose.foundation.layout.*
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.*
@@ -15,8 +17,8 @@ import androidx.compose.ui.draw.scale
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import com.sukisu.ultra.R
// 菜单项数据类

View File

@@ -3,11 +3,7 @@ package com.sukisu.ultra.ui.component.profile
import androidx.compose.foundation.layout.Column
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview

View File

@@ -1,28 +1,11 @@
package com.sukisu.ultra.ui.component.profile
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.ExperimentalLayoutApi
import androidx.compose.foundation.layout.FlowRow
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material3.AssistChip
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.ListItem
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedCard
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.OutlinedTextFieldDefaults
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
import androidx.compose.ui.res.stringResource
@@ -34,11 +17,7 @@ import androidx.core.text.isDigitsOnly
import com.maxkeppeker.sheets.core.models.base.Header
import com.maxkeppeker.sheets.core.models.base.rememberUseCaseState
import com.maxkeppeler.sheets.input.InputDialog
import com.maxkeppeler.sheets.input.models.InputHeader
import com.maxkeppeler.sheets.input.models.InputSelection
import com.maxkeppeler.sheets.input.models.InputTextField
import com.maxkeppeler.sheets.input.models.InputTextFieldType
import com.maxkeppeler.sheets.input.models.ValidationResult
import com.maxkeppeler.sheets.input.models.*
import com.maxkeppeler.sheets.list.ListDialog
import com.maxkeppeler.sheets.list.models.ListOption
import com.maxkeppeler.sheets.list.models.ListSelection

View File

@@ -6,21 +6,9 @@ import androidx.compose.material.icons.automirrored.filled.ReadMore
import androidx.compose.material.icons.filled.ArrowDropDown
import androidx.compose.material.icons.filled.ArrowDropUp
import androidx.compose.material.icons.filled.Create
import androidx.compose.material3.DropdownMenuItem
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.ExposedDropdownMenuBox
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.ListItem
import androidx.compose.material3.MenuAnchorType
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import com.sukisu.ultra.Natives

View File

@@ -2,26 +2,9 @@ package com.sukisu.ultra.ui.screen
import android.annotation.SuppressLint
import androidx.annotation.StringRes
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.Crossfade
import androidx.compose.animation.expandVertically
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.animation.shrinkVertically
import androidx.compose.animation.*
import androidx.compose.foundation.gestures.detectTapGestures
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.BoxWithConstraints
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.WindowInsetsSides
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.only
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.safeDrawing
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
@@ -29,31 +12,9 @@ import androidx.compose.material.icons.automirrored.filled.ArrowBack
import androidx.compose.material.icons.filled.AccountCircle
import androidx.compose.material.icons.filled.Android
import androidx.compose.material.icons.filled.Security
import androidx.compose.material3.DropdownMenu
import androidx.compose.material3.DropdownMenuItem
import androidx.compose.material3.ElevatedCard
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.FilterChip
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.ListItem
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.material3.SnackbarHost
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.material3.TopAppBarColors
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.material3.TopAppBarScrollBehavior
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
@@ -86,12 +47,7 @@ import com.sukisu.ultra.ui.component.profile.TemplateConfig
import com.sukisu.ultra.ui.theme.CardConfig
import com.sukisu.ultra.ui.theme.getCardColors
import com.sukisu.ultra.ui.theme.getCardElevation
import com.sukisu.ultra.ui.util.LocalSnackbarHost
import com.sukisu.ultra.ui.util.forceStopApp
import com.sukisu.ultra.ui.util.getSepolicy
import com.sukisu.ultra.ui.util.launchApp
import com.sukisu.ultra.ui.util.restartApp
import com.sukisu.ultra.ui.util.setSepolicy
import com.sukisu.ultra.ui.util.*
import com.sukisu.ultra.ui.viewmodel.SuperUserViewModel
import com.sukisu.ultra.ui.viewmodel.getTemplateInfoById
import kotlinx.coroutines.launch
@@ -386,7 +342,7 @@ private fun AppProfileInner(
}
}
private enum class Mode(@StringRes private val res: Int) {
private enum class Mode(@param:StringRes private val res: Int) {
Default(R.string.profile_default), Template(R.string.profile_template), Custom(R.string.profile_custom);
val text: String

View File

@@ -3,20 +3,15 @@ package com.sukisu.ultra.ui.screen
import androidx.annotation.StringRes
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.*
import androidx.compose.material.icons.filled.Archive
import androidx.compose.material.icons.outlined.*
import androidx.compose.ui.graphics.vector.ImageVector
import com.ramcosta.composedestinations.generated.destinations.HomeScreenDestination
import com.ramcosta.composedestinations.generated.destinations.ModuleScreenDestination
import com.ramcosta.composedestinations.generated.destinations.SuperUserScreenDestination
import com.ramcosta.composedestinations.generated.destinations.SettingScreenDestination
import com.ramcosta.composedestinations.generated.destinations.KpmScreenDestination
import com.ramcosta.composedestinations.generated.destinations.*
import com.ramcosta.composedestinations.spec.DirectionDestinationSpec
import com.sukisu.ultra.R
enum class BottomBarDestination(
val direction: DirectionDestinationSpec,
@StringRes val label: Int,
@param:StringRes val label: Int,
val iconSelected: ImageVector,
val iconNotSelected: ImageVector,
val rootRequired: Boolean,

View File

@@ -2,32 +2,15 @@ package com.sukisu.ultra.ui.screen
import android.os.Environment
import androidx.activity.compose.BackHandler
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.safeDrawing
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Close
import androidx.compose.material.icons.filled.Save
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.ExtendedFloatingActionButton
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.material3.SnackbarHost
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.input.key.Key
import androidx.compose.ui.input.key.key
@@ -37,17 +20,16 @@ import androidx.compose.ui.unit.dp
import com.ramcosta.composedestinations.annotation.Destination
import com.ramcosta.composedestinations.annotation.RootGraph
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import com.sukisu.ultra.R
import com.sukisu.ultra.ui.component.KeyEventBlocker
import com.sukisu.ultra.ui.util.LocalSnackbarHost
import com.sukisu.ultra.ui.util.runModuleAction
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.io.File
import java.text.SimpleDateFormat
import java.util.Date
import java.util.Locale
import java.util.*
@Composable
@Destination<RootGraph>

View File

@@ -4,12 +4,8 @@ import android.net.Uri
import android.os.Environment
import android.os.Parcelable
import androidx.activity.compose.BackHandler
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.*
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.animation.expandVertically
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.animation.shrinkVertically
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.rememberScrollState
@@ -33,22 +29,22 @@ import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel
import com.ramcosta.composedestinations.annotation.Destination
import com.ramcosta.composedestinations.annotation.RootGraph
import com.ramcosta.composedestinations.generated.destinations.FlashScreenDestination
import com.ramcosta.composedestinations.generated.destinations.ModuleScreenDestination
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
import com.ramcosta.composedestinations.navigation.EmptyDestinationsNavigator
import com.sukisu.ultra.R
import com.sukisu.ultra.ui.component.KeyEventBlocker
import com.sukisu.ultra.ui.theme.CardConfig
import com.sukisu.ultra.ui.util.*
import com.sukisu.ultra.ui.viewmodel.ModuleViewModel
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import kotlinx.parcelize.Parcelize
import com.sukisu.ultra.ui.component.KeyEventBlocker
import com.sukisu.ultra.ui.util.*
import com.sukisu.ultra.R
import com.sukisu.ultra.ui.theme.CardConfig
import com.sukisu.ultra.ui.viewmodel.ModuleViewModel
import androidx.lifecycle.viewmodel.compose.viewModel
import java.io.File
import java.text.SimpleDateFormat
import java.util.*

View File

@@ -6,29 +6,11 @@ import android.os.Build
import android.os.PowerManager
import android.system.Os
import androidx.annotation.StringRes
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.*
import androidx.compose.animation.core.Spring
import androidx.compose.animation.core.spring
import androidx.compose.animation.expandVertically
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.animation.shrinkVertically
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.WindowInsetsSides
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.only
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.safeDrawing
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.verticalScroll
@@ -40,29 +22,8 @@ import androidx.compose.material.icons.outlined.TaskAlt
import androidx.compose.material.icons.outlined.Warning
import androidx.compose.material.pullrefresh.pullRefresh
import androidx.compose.material.pullrefresh.rememberPullRefreshState
import androidx.compose.material3.CardDefaults
import androidx.compose.material3.DropdownMenu
import androidx.compose.material3.DropdownMenuItem
import androidx.compose.material3.ElevatedCard
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.material3.TopAppBarScrollBehavior
import androidx.compose.material3.rememberTopAppBarState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.produceState
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
@@ -89,11 +50,11 @@ import com.sukisu.ultra.ui.theme.CardConfig.cardAlpha
import com.sukisu.ultra.ui.theme.CardConfig.cardElevation
import com.sukisu.ultra.ui.theme.getCardColors
import com.sukisu.ultra.ui.theme.getCardElevation
import com.sukisu.ultra.ui.util.SuSFSManager
import com.sukisu.ultra.ui.util.checkNewVersion
import com.sukisu.ultra.ui.util.getSuSFS
import com.sukisu.ultra.ui.util.module.LatestVersionInfo
import com.sukisu.ultra.ui.util.reboot
import com.sukisu.ultra.ui.util.getSuSFS
import com.sukisu.ultra.ui.util.SuSFSManager
import com.sukisu.ultra.ui.viewmodel.HomeViewModel
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
@@ -366,7 +327,7 @@ private fun StatusCard(
systemStatus.ksuVersion != null -> {
val workingModeText = when {
Natives.isSafeMode == true -> stringResource(id = R.string.safe_mode)
Natives.isSafeMode -> stringResource(id = R.string.safe_mode)
else -> stringResource(id = R.string.home_working)
}

View File

@@ -7,22 +7,11 @@ import android.widget.Toast
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts
import androidx.annotation.StringRes
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.expandVertically
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.animation.shrinkVertically
import androidx.compose.animation.*
import androidx.compose.foundation.LocalIndication
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.WindowInsetsSides
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.only
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.safeDrawing
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.selection.toggleable
import androidx.compose.foundation.verticalScroll
@@ -30,31 +19,8 @@ import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.ArrowBack
import androidx.compose.material.icons.filled.AutoFixHigh
import androidx.compose.material.icons.filled.FileUpload
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.ElevatedCard
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.ListItem
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.RadioButton
import androidx.compose.material3.RadioButtonDefaults
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.material3.TopAppBar
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.material3.TopAppBarScrollBehavior
import androidx.compose.material3.rememberTopAppBarState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.produceState
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
@@ -78,22 +44,17 @@ import com.ramcosta.composedestinations.generated.destinations.KernelFlashScreen
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
import com.ramcosta.composedestinations.navigation.EmptyDestinationsNavigator
import com.sukisu.ultra.R
import com.sukisu.ultra.getKernelVersion
import com.sukisu.ultra.ui.component.DialogHandle
import com.sukisu.ultra.ui.component.SlotSelectionDialog
import com.sukisu.ultra.ui.component.rememberConfirmDialog
import com.sukisu.ultra.ui.component.rememberCustomDialog
import com.sukisu.ultra.ui.theme.CardConfig
import com.sukisu.ultra.ui.theme.CardConfig.cardAlpha
import com.sukisu.ultra.ui.theme.CardConfig.cardElevation
import com.sukisu.ultra.ui.theme.getCardColors
import com.sukisu.ultra.ui.util.LkmSelection
import com.sukisu.ultra.ui.util.getCurrentKmi
import com.sukisu.ultra.ui.util.getSupportedKmis
import com.sukisu.ultra.ui.util.isAbDevice
import com.sukisu.ultra.ui.util.isInitBoot
import com.sukisu.ultra.ui.util.rootAvailable
import com.sukisu.ultra.getKernelVersion
import com.sukisu.ultra.ui.theme.CardConfig
import com.sukisu.ultra.ui.theme.getCardElevation
import com.sukisu.ultra.ui.util.*
/**
* @author ShirkNeko
@@ -413,9 +374,9 @@ private fun SelectInstallMethod(
else -> null
}
option?.let { it ->
selectedOption = it
onSelected(it)
option?.let { opt ->
selectedOption = opt
onSelected(opt)
}
}
}

View File

@@ -1,5 +1,6 @@
package com.sukisu.ultra.ui.screen
import android.app.Activity
import android.content.Context
import android.content.Intent
import android.util.Log
@@ -17,25 +18,27 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.core.content.edit
import androidx.lifecycle.viewmodel.compose.viewModel
import com.ramcosta.composedestinations.annotation.Destination
import com.ramcosta.composedestinations.annotation.RootGraph
import com.sukisu.ultra.R
import com.sukisu.ultra.ui.component.*
import com.sukisu.ultra.ui.theme.getCardColors
import com.sukisu.ultra.ui.theme.getCardElevation
import com.sukisu.ultra.ui.util.loadKpmModule
import com.sukisu.ultra.ui.util.unloadKpmModule
import com.sukisu.ultra.ui.viewmodel.KpmViewModel
import com.topjohnwu.superuser.Shell
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import com.sukisu.ultra.ui.component.*
import com.sukisu.ultra.ui.theme.*
import com.sukisu.ultra.ui.viewmodel.KpmViewModel
import com.sukisu.ultra.ui.util.*
import java.io.File
import androidx.core.content.edit
import com.sukisu.ultra.R
import java.io.FileInputStream
import java.net.*
import android.app.Activity
import androidx.compose.ui.res.painterResource
import java.net.URLEncoder
/**
* KPM 管理界面
@@ -83,9 +86,8 @@ fun KpmScreen(
LaunchedEffect(tempFileForInstall) {
tempFileForInstall?.let { tempFile ->
try {
val shell = getRootShell()
val command = "strings ${tempFile.absolutePath} | grep 'name='"
val result = shell.newJob().add(command).to(ArrayList(), null).exec()
val result = Shell.cmd(command).to(ArrayList(), null).exec()
if (result.isSuccess) {
for (line in result.out) {
if (line.startsWith("name=")) {
@@ -424,9 +426,8 @@ private suspend fun handleModuleInstall(
) {
var moduleId: String? = null
try {
val shell = getRootShell()
val command = "strings ${tempFile.absolutePath} | grep 'name='"
val result = shell.newJob().add(command).to(ArrayList(), null).exec()
val result = Shell.cmd(command).to(ArrayList(), null).exec()
if (result.isSuccess) {
for (line in result.out) {
if (line.startsWith("name=")) {
@@ -453,9 +454,8 @@ private suspend fun handleModuleInstall(
try {
if (isEmbed) {
val shell = getRootShell()
shell.newJob().add("mkdir -p /data/adb/kpm").exec()
shell.newJob().add("cp ${tempFile.absolutePath} $targetPath").exec()
Shell.cmd("mkdir -p /data/adb/kpm").exec()
Shell.cmd("cp ${tempFile.absolutePath} $targetPath").exec()
}
val loadResult = loadKpmModule(tempFile.absolutePath)
@@ -499,8 +499,7 @@ private suspend fun handleModuleUninstall(
val moduleFilePath = "/data/adb/kpm/$moduleFileName"
val fileExists = try {
val shell = getRootShell()
val result = shell.newJob().add("ls /data/adb/kpm/$moduleFileName").exec()
val result = Shell.cmd("ls /data/adb/kpm/$moduleFileName").exec()
result.isSuccess
} catch (e: Exception) {
Log.e("KsuCli", "Failed to check module file existence: ${e.message}", e)
@@ -531,8 +530,7 @@ private suspend fun handleModuleUninstall(
}
if (fileExists) {
val shell = getRootShell()
shell.newJob().add("rm $moduleFilePath").exec()
Shell.cmd("rm $moduleFilePath").exec()
}
viewModel.fetchModuleList()
@@ -703,9 +701,8 @@ private fun KpmModuleItem(
}
private fun checkStringsCommand(tempFile: File): Int {
val shell = getRootShell()
val command = "strings ${tempFile.absolutePath} | grep -E 'name=|version=|license=|author='"
val result = shell.newJob().add(command).to(ArrayList(), null).exec()
val result = Shell.cmd(command).to(ArrayList(), null).exec()
if (!result.isSuccess) {
return 0

View File

@@ -11,9 +11,12 @@ import android.util.Log
import android.widget.Toast
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.animation.*
import androidx.compose.animation.core.*
import androidx.compose.foundation.*
import androidx.compose.animation.core.Spring
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.animation.core.spring
import androidx.compose.foundation.LocalIndication
import androidx.compose.foundation.clickable
import androidx.compose.foundation.combinedClickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.interaction.collectIsPressedAsState
import androidx.compose.foundation.layout.*
@@ -28,8 +31,9 @@ import androidx.compose.foundation.selection.toggleable
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.outlined.*
import androidx.compose.material.icons.filled.*
import androidx.compose.material.icons.automirrored.outlined.Wysiwyg
import androidx.compose.material.icons.filled.MoreVert
import androidx.compose.material.icons.filled.Verified
import androidx.compose.material.icons.outlined.*
import androidx.compose.material3.*
import androidx.compose.material3.pulltorefresh.PullToRefreshBox
@@ -41,7 +45,8 @@ import androidx.compose.ui.draw.scale
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.hapticfeedback.HapticFeedbackType
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.*
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalHapticFeedback
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.semantics.Role
@@ -52,45 +57,32 @@ import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import androidx.core.content.edit
import androidx.core.net.toUri
import androidx.lifecycle.viewmodel.compose.viewModel
import com.dergoogler.mmrl.platform.Platform
import com.dergoogler.mmrl.platform.model.ModuleConfig
import com.dergoogler.mmrl.platform.model.ModuleConfig.Companion.asModuleConfig
import com.ramcosta.composedestinations.annotation.Destination
import com.ramcosta.composedestinations.annotation.RootGraph
import com.ramcosta.composedestinations.generated.destinations.ExecuteModuleActionScreenDestination
import com.ramcosta.composedestinations.generated.destinations.FlashScreenDestination
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
import com.ramcosta.composedestinations.navigation.EmptyDestinationsNavigator
import com.sukisu.ultra.Natives
import com.sukisu.ultra.R
import com.sukisu.ultra.ui.component.*
import com.sukisu.ultra.ui.theme.getCardColors
import com.sukisu.ultra.ui.theme.getCardElevation
import com.sukisu.ultra.ui.util.*
import com.sukisu.ultra.ui.viewmodel.ModuleViewModel
import com.sukisu.ultra.ui.webui.WebUIActivity
import com.sukisu.ultra.ui.webui.WebUIXActivity
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import com.sukisu.ultra.Natives
import com.sukisu.ultra.ui.component.ConfirmResult
import com.sukisu.ultra.ui.component.SearchAppBar
import com.sukisu.ultra.ui.component.rememberConfirmDialog
import com.sukisu.ultra.ui.component.rememberLoadingDialog
import com.sukisu.ultra.ui.util.DownloadListener
import com.sukisu.ultra.ui.util.*
import com.sukisu.ultra.ui.util.download
import com.sukisu.ultra.ui.util.hasMagisk
import com.sukisu.ultra.ui.util.reboot
import com.sukisu.ultra.ui.util.restoreModule
import com.sukisu.ultra.ui.util.toggleModule
import com.sukisu.ultra.ui.util.uninstallModule
import com.sukisu.ultra.ui.webui.WebUIActivity
import okhttp3.OkHttpClient
import com.sukisu.ultra.ui.util.ModuleModify
import com.sukisu.ultra.ui.theme.getCardColors
import com.sukisu.ultra.ui.viewmodel.ModuleViewModel
import java.util.concurrent.TimeUnit
import androidx.core.content.edit
import com.sukisu.ultra.R
import com.sukisu.ultra.ui.webui.WebUIXActivity
import com.dergoogler.mmrl.platform.Platform
import androidx.core.net.toUri
import com.dergoogler.mmrl.platform.model.ModuleConfig
import com.dergoogler.mmrl.platform.model.ModuleConfig.Companion.asModuleConfig
import com.sukisu.ultra.ui.component.AnimatedFab
import com.sukisu.ultra.ui.component.rememberFabVisibilityState
import com.sukisu.ultra.ui.theme.getCardElevation
// 菜单项数据类
data class ModuleBottomSheetMenuItem(

View File

@@ -6,11 +6,7 @@ import android.net.Uri
import android.widget.Toast
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.expandVertically
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.animation.shrinkVertically
import androidx.compose.animation.*
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
@@ -21,13 +17,8 @@ import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.Undo
import androidx.compose.material.icons.filled.*
import androidx.compose.material3.*
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.*
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
@@ -47,20 +38,21 @@ import com.ramcosta.composedestinations.generated.destinations.AppProfileTemplat
import com.ramcosta.composedestinations.generated.destinations.FlashScreenDestination
import com.ramcosta.composedestinations.generated.destinations.MoreSettingsScreenDestination
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import com.sukisu.ultra.BuildConfig
import com.sukisu.ultra.Natives
import com.sukisu.ultra.R
import com.sukisu.ultra.ui.component.*
import com.sukisu.ultra.ui.theme.*
import com.sukisu.ultra.ui.theme.CardConfig
import com.sukisu.ultra.ui.theme.CardConfig.cardAlpha
import com.sukisu.ultra.ui.theme.getCardColors
import com.sukisu.ultra.ui.theme.getCardElevation
import com.sukisu.ultra.ui.util.LocalSnackbarHost
import com.sukisu.ultra.ui.util.getBugreportFile
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.time.LocalDateTime
import java.time.format.DateTimeFormatter
import com.sukisu.ultra.ui.component.KsuIsValid
/**
* @author ShirkNeko

View File

@@ -3,58 +3,15 @@ package com.sukisu.ultra.ui.screen
import android.annotation.SuppressLint
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.WindowInsetsSides
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.only
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.safeDrawing
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.ArrowBack
import androidx.compose.material.icons.filled.*
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Button
import androidx.compose.material3.Card
import androidx.compose.material3.CardDefaults
import androidx.compose.material3.CenterAlignedTopAppBar
import androidx.compose.material3.DropdownMenuItem
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.ExposedDropdownMenuBox
import androidx.compose.material3.ExposedDropdownMenuDefaults
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.MenuAnchorType
import androidx.compose.material3.OutlinedButton
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Scaffold
import androidx.compose.material3.ScrollableTabRow
import androidx.compose.material3.Surface
import androidx.compose.material3.Switch
import androidx.compose.material3.Tab
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
@@ -68,18 +25,7 @@ import com.ramcosta.composedestinations.annotation.Destination
import com.ramcosta.composedestinations.annotation.RootGraph
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
import com.sukisu.ultra.R
import com.sukisu.ultra.ui.component.AddAppPathDialog
import com.sukisu.ultra.ui.component.AddKstatStaticallyDialog
import com.sukisu.ultra.ui.component.AddPathDialog
import com.sukisu.ultra.ui.component.AddTryUmountDialog
import com.sukisu.ultra.ui.component.ConfirmDialog
import com.sukisu.ultra.ui.component.EnabledFeaturesContent
import com.sukisu.ultra.ui.component.KstatConfigContent
import com.sukisu.ultra.ui.component.PathSettingsContent
import com.sukisu.ultra.ui.component.SusMountsContent
import com.sukisu.ultra.ui.component.SusPathsContent
import com.sukisu.ultra.ui.component.SusLoopPathsContent
import com.sukisu.ultra.ui.component.TryUmountContent
import com.sukisu.ultra.ui.component.*
import com.sukisu.ultra.ui.theme.CardConfig
import com.sukisu.ultra.ui.util.SuSFSManager
import com.sukisu.ultra.ui.util.SuSFSManager.isSusVersion158
@@ -1171,7 +1117,7 @@ fun SuSFSConfigScreen(
containerColor = MaterialTheme.colorScheme.surface,
contentColor = MaterialTheme.colorScheme.onSurface
) {
allTabs.forEachIndexed { index, tab ->
allTabs.forEach { tab ->
Tab(
selected = selectedTab == tab,
onClick = { selectedTab = tab },

View File

@@ -4,6 +4,7 @@ import android.annotation.SuppressLint
import androidx.compose.animation.*
import androidx.compose.animation.core.*
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.gestures.detectTapGestures
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.interaction.collectIsPressedAsState
@@ -17,7 +18,6 @@ import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.clickable
import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.*
@@ -34,31 +34,30 @@ import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.lifecycle.viewmodel.compose.viewModel
import com.sukisu.ultra.R
import coil.compose.AsyncImage
import coil.request.ImageRequest
import com.dergoogler.mmrl.ui.component.LabelItem
import com.dergoogler.mmrl.ui.component.LabelItemDefaults
import com.ramcosta.composedestinations.annotation.Destination
import com.ramcosta.composedestinations.annotation.RootGraph
import com.ramcosta.composedestinations.generated.destinations.AppProfileScreenDestination
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
import kotlinx.coroutines.launch
import com.sukisu.ultra.Natives
import com.sukisu.ultra.R
import com.sukisu.ultra.ui.component.FabMenuPresets
import com.sukisu.ultra.ui.component.SearchAppBar
import com.sukisu.ultra.ui.component.VerticalExpandableFab
import com.sukisu.ultra.ui.component.FabMenuPresets
import com.sukisu.ultra.ui.util.ModuleModify
import com.sukisu.ultra.ui.viewmodel.SuperUserViewModel
import com.sukisu.ultra.ui.viewmodel.AppCategory
import com.sukisu.ultra.ui.viewmodel.SortType
import com.dergoogler.mmrl.ui.component.LabelItem
import com.dergoogler.mmrl.ui.component.LabelItemDefaults
import kotlin.math.*
import com.sukisu.ultra.ui.viewmodel.SuperUserViewModel
import kotlinx.coroutines.launch
import java.io.File
// 应用优先级枚举

View File

@@ -4,16 +4,7 @@ import android.content.ClipData
import android.content.ClipboardManager
import android.widget.Toast
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.ExperimentalLayoutApi
import androidx.compose.foundation.layout.FlowRow
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.WindowInsetsSides
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.only
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.safeDrawing
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material.ExperimentalMaterialApi
@@ -22,28 +13,9 @@ import androidx.compose.material.icons.automirrored.filled.ArrowBack
import androidx.compose.material.icons.filled.Add
import androidx.compose.material.icons.filled.ImportExport
import androidx.compose.material.icons.filled.Sync
import androidx.compose.material3.DropdownMenu
import androidx.compose.material3.DropdownMenuItem
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.ExtendedFloatingActionButton
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.ListItem
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.material3.TopAppBarScrollBehavior
import androidx.compose.material3.*
import androidx.compose.material3.pulltorefresh.PullToRefreshBox
import androidx.compose.material3.rememberTopAppBarState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.LocalContext
@@ -58,11 +30,11 @@ import com.ramcosta.composedestinations.generated.destinations.TemplateEditorScr
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
import com.ramcosta.composedestinations.result.ResultRecipient
import com.ramcosta.composedestinations.result.getOr
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import com.sukisu.ultra.R
import com.sukisu.ultra.ui.theme.CardConfig
import com.sukisu.ultra.ui.viewmodel.TemplateViewModel
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
/**
* @author weishu

View File

@@ -2,13 +2,7 @@ package com.sukisu.ultra.ui.screen
import android.widget.Toast
import androidx.activity.compose.BackHandler
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.WindowInsetsSides
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.only
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.safeDrawing
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions
@@ -17,24 +11,9 @@ import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.ArrowBack
import androidx.compose.material.icons.filled.DeleteForever
import androidx.compose.material.icons.filled.Save
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.ListItem
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.material3.TopAppBarScrollBehavior
import androidx.compose.material3.rememberTopAppBarState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
import androidx.compose.ui.input.nestedscroll.nestedScroll
@@ -44,6 +23,7 @@ import androidx.compose.ui.platform.LocalSoftwareKeyboardController
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.text.input.KeyboardType
import androidx.lifecycle.compose.dropUnlessResumed
import com.ramcosta.composedestinations.annotation.Destination
import com.ramcosta.composedestinations.annotation.RootGraph
import com.ramcosta.composedestinations.result.ResultBackNavigator
@@ -55,7 +35,6 @@ import com.sukisu.ultra.ui.util.getAppProfileTemplate
import com.sukisu.ultra.ui.util.setAppProfileTemplate
import com.sukisu.ultra.ui.viewmodel.TemplateViewModel
import com.sukisu.ultra.ui.viewmodel.toJSON
import androidx.lifecycle.compose.dropUnlessResumed
/**
* @author weishu

View File

@@ -3,11 +3,7 @@ package com.sukisu.ultra.ui.theme
import android.content.Context
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.material3.CardDefaults
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableFloatStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.compose.runtime.*
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.luminance
import androidx.compose.ui.unit.dp

View File

@@ -5,51 +5,40 @@ import android.content.Context
import android.net.Uri
import android.os.Build
import android.util.Log
import androidx.activity.ComponentActivity
import androidx.activity.SystemBarStyle
import androidx.activity.enableEdgeToEdge
import androidx.annotation.RequiresApi
import androidx.compose.animation.core.animateFloat
import androidx.compose.animation.core.spring
import androidx.compose.animation.core.updateTransition
import androidx.compose.foundation.background
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.darkColorScheme
import androidx.compose.material3.dynamicDarkColorScheme
import androidx.compose.material3.dynamicLightColorScheme
import androidx.compose.material3.lightColorScheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
import androidx.compose.ui.draw.paint
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.graphics.toArgb
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.dp
import androidx.compose.ui.zIndex
import androidx.core.content.edit
import androidx.core.net.toUri
import coil.compose.AsyncImagePainter
import coil.compose.rememberAsyncImagePainter
import androidx.compose.foundation.background
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.unit.dp
import com.sukisu.ultra.ui.util.BackgroundTransformation
import com.sukisu.ultra.ui.util.saveTransformedBackground
import java.io.File
import java.io.FileOutputStream
import java.io.InputStream
import androidx.core.content.edit
import androidx.core.net.toUri
import com.sukisu.ultra.ui.util.BackgroundTransformation
import com.sukisu.ultra.ui.util.saveTransformedBackground
import androidx.activity.SystemBarStyle
import androidx.activity.ComponentActivity
import androidx.activity.enableEdgeToEdge
import androidx.compose.material3.ColorScheme
import androidx.compose.runtime.SideEffect
import androidx.compose.ui.graphics.toArgb
/**
* 主题配置对象,管理应用的主题相关状态
@@ -166,8 +155,8 @@ fun KernelSUTheme(
val bgImagePainter = backgroundUri.value?.let {
rememberAsyncImagePainter(
model = it,
onError = {
Log.e("ThemeSystem", "背景图加载失败: ${it.result.throwable.message}")
onError = { err ->
Log.e("ThemeSystem", "背景图加载失败: ${err.result.throwable.message}")
ThemeConfig.customBackgroundUri = null
context.saveCustomBackground(null)
},
@@ -222,7 +211,7 @@ fun KernelSUTheme(
)
// 自定义背景层
backgroundUri.value?.let { uri ->
backgroundUri.value?.let {
Box(
modifier = Modifier
.fillMaxSize()

View File

@@ -8,10 +8,10 @@ import android.graphics.Canvas
import android.graphics.Matrix
import android.net.Uri
import android.util.Log
import androidx.core.graphics.createBitmap
import java.io.File
import java.io.FileOutputStream
import java.io.InputStream
import androidx.core.graphics.createBitmap
data class BackgroundTransformation(
val scale: Float = 1f,

View File

@@ -15,8 +15,8 @@ import android.util.Log
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.core.content.ContextCompat
import com.sukisu.ultra.ui.util.module.LatestVersionInfo
import androidx.core.net.toUri
import com.sukisu.ultra.ui.util.module.LatestVersionInfo
import java.io.File
import java.util.concurrent.TimeUnit

View File

@@ -32,7 +32,8 @@ import java.util.Locale;
* <p>
* Currently this file is aligned to zh.txt in ICU 4.6
*/
public class HanziToPinyin {
@SuppressWarnings("SizeReplaceableByIsEmpty")
public record HanziToPinyin(boolean mHasChinaCollator) {
private static final String TAG = "HanziToPinyin";
// Turn on this flag when we want to check internal data structure.
@@ -44,77 +45,77 @@ public class HanziToPinyin {
* Each unihans is the first one within same pinyin when collator is zh_CN.
*/
public static final char[] UNIHANS = {
'\u963f', '\u54ce', '\u5b89', '\u80ae', '\u51f9', '\u516b',
'\u6300', '\u6273', '\u90a6', '\u52f9', '\u9642', '\u5954',
'\u4f3b', '\u5c44', '\u8fb9', '\u706c', '\u618b', '\u6c43',
'\u51ab', '\u7676', '\u5cec', '\u5693', '\u5072', '\u53c2',
'\u4ed3', '\u64a1', '\u518a', '\u5d7e', '\u66fd', '\u66fe',
'\u5c64', '\u53c9', '\u8286', '\u8fbf', '\u4f25', '\u6284',
'\u8f66', '\u62bb', '\u6c88', '\u6c89', '\u9637', '\u5403',
'\u5145', '\u62bd', '\u51fa', '\u6b3b', '\u63e3', '\u5ddb',
'\u5205', '\u5439', '\u65fe', '\u9034', '\u5472', '\u5306',
'\u51d1', '\u7c97', '\u6c46', '\u5d14', '\u90a8', '\u6413',
'\u5491', '\u5446', '\u4e39', '\u5f53', '\u5200', '\u561a',
'\u6265', '\u706f', '\u6c10', '\u55f2', '\u7538', '\u5201',
'\u7239', '\u4e01', '\u4e1f', '\u4e1c', '\u543a', '\u53be',
'\u8011', '\u8968', '\u5428', '\u591a', '\u59b8', '\u8bf6',
'\u5940', '\u97a5', '\u513f', '\u53d1', '\u5e06', '\u531a',
'\u98de', '\u5206', '\u4e30', '\u8985', '\u4ecf', '\u7d11',
'\u4f15', '\u65ee', '\u4f85', '\u7518', '\u5188', '\u768b',
'\u6208', '\u7ed9', '\u6839', '\u522f', '\u5de5', '\u52fe',
'\u4f30', '\u74dc', '\u4e56', '\u5173', '\u5149', '\u5f52',
'\u4e28', '\u5459', '\u54c8', '\u548d', '\u4f44', '\u592f',
'\u8320', '\u8bc3', '\u9ed2', '\u62eb', '\u4ea8', '\u5677',
'\u53ff', '\u9f41', '\u4e6f', '\u82b1', '\u6000', '\u72bf',
'\u5ddf', '\u7070', '\u660f', '\u5419', '\u4e0c', '\u52a0',
'\u620b', '\u6c5f', '\u827d', '\u9636', '\u5dfe', '\u5755',
'\u5182', '\u4e29', '\u51e5', '\u59e2', '\u5658', '\u519b',
'\u5494', '\u5f00', '\u520a', '\u5ffc', '\u5c3b', '\u533c',
'\u808e', '\u52a5', '\u7a7a', '\u62a0', '\u625d', '\u5938',
'\u84af', '\u5bbd', '\u5321', '\u4e8f', '\u5764', '\u6269',
'\u5783', '\u6765', '\u5170', '\u5577', '\u635e', '\u808b',
'\u52d2', '\u5d1a', '\u5215', '\u4fe9', '\u5941', '\u826f',
'\u64a9', '\u5217', '\u62ce', '\u5222', '\u6e9c', '\u56d6',
'\u9f99', '\u779c', '\u565c', '\u5a08', '\u7567', '\u62a1',
'\u7f57', '\u5463', '\u5988', '\u57cb', '\u5ada', '\u7264',
'\u732b', '\u4e48', '\u5445', '\u95e8', '\u753f', '\u54aa',
'\u5b80', '\u55b5', '\u4e5c', '\u6c11', '\u540d', '\u8c2c',
'\u6478', '\u54de', '\u6bea', '\u55ef', '\u62cf', '\u8149',
'\u56e1', '\u56d4', '\u5b6c', '\u7592', '\u5a1e', '\u6041',
'\u80fd', '\u59ae', '\u62c8', '\u5b22', '\u9e1f', '\u634f',
'\u56dc', '\u5b81', '\u599e', '\u519c', '\u7fba', '\u5974',
'\u597b', '\u759f', '\u9ec1', '\u90cd', '\u5594', '\u8bb4',
'\u5991', '\u62cd', '\u7705', '\u4e53', '\u629b', '\u5478',
'\u55b7', '\u5309', '\u4e15', '\u56e8', '\u527d', '\u6c15',
'\u59d8', '\u4e52', '\u948b', '\u5256', '\u4ec6', '\u4e03',
'\u6390', '\u5343', '\u545b', '\u6084', '\u767f', '\u4eb2',
'\u72c5', '\u828e', '\u4e18', '\u533a', '\u5cd1', '\u7f3a',
'\u590b', '\u5465', '\u7a63', '\u5a06', '\u60f9', '\u4eba',
'\u6254', '\u65e5', '\u8338', '\u53b9', '\u909a', '\u633c',
'\u5827', '\u5a51', '\u77a4', '\u637c', '\u4ee8', '\u6be2',
'\u4e09', '\u6852', '\u63bb', '\u95aa', '\u68ee', '\u50e7',
'\u6740', '\u7b5b', '\u5c71', '\u4f24', '\u5f30', '\u5962',
'\u7533', '\u8398', '\u6552', '\u5347', '\u5c38', '\u53ce',
'\u4e66', '\u5237', '\u8870', '\u95e9', '\u53cc', '\u8c01',
'\u542e', '\u8bf4', '\u53b6', '\u5fea', '\u635c', '\u82cf',
'\u72fb', '\u590a', '\u5b59', '\u5506', '\u4ed6', '\u56fc',
'\u574d', '\u6c64', '\u5932', '\u5fd1', '\u71a5', '\u5254',
'\u5929', '\u65eb', '\u5e16', '\u5385', '\u56f2', '\u5077',
'\u51f8', '\u6e4d', '\u63a8', '\u541e', '\u4e47', '\u7a75',
'\u6b6a', '\u5f2f', '\u5c23', '\u5371', '\u6637', '\u7fc1',
'\u631d', '\u4e4c', '\u5915', '\u8672', '\u4eda', '\u4e61',
'\u7071', '\u4e9b', '\u5fc3', '\u661f', '\u51f6', '\u4f11',
'\u5401', '\u5405', '\u524a', '\u5743', '\u4e2b', '\u6079',
'\u592e', '\u5e7a', '\u503b', '\u4e00', '\u56d9', '\u5e94',
'\u54df', '\u4f63', '\u4f18', '\u625c', '\u56e6', '\u66f0',
'\u6655', '\u7b60', '\u7b7c', '\u5e00', '\u707d', '\u5142',
'\u5328', '\u50ae', '\u5219', '\u8d3c', '\u600e', '\u5897',
'\u624e', '\u635a', '\u6cbe', '\u5f20', '\u957f', '\u9577',
'\u4f4b', '\u8707', '\u8d1e', '\u4e89', '\u4e4b', '\u5cd9',
'\u5ea2', '\u4e2d', '\u5dde', '\u6731', '\u6293', '\u62fd',
'\u4e13', '\u5986', '\u96b9', '\u5b92', '\u5353', '\u4e72',
'\u5b97', '\u90b9', '\u79df', '\u94bb', '\u539c', '\u5c0a',
'\u6628', '\u5159', '\u9fc3', '\u9fc4',};
'', '', '', '', '', '',
'', '', '', '', '', '',
'', '', '', '', '', '',
'', '', '', '', '', '',
'', '', '', '', '', '',
'', '', '', '辿', '', '',
'', '', '', '', '', '',
'', '', '', '', '', '',
'', '', '', '', '', '',
'', '', '', '', '', '',
'', '', '', '', '', '',
'', '', '', '', '', '',
'', '', '', '', '', '',
'', '', '', '', '', '',
'', '', '', '', '', '',
'', '', '', '', '', '',
'', '', '', '', '', '',
'', '', '', '', '', '',
'', '', '', '', '', '',
'', '', '', '', '', '',
'', '', '', '', '', '',
'', '', '', '', '怀', '',
'', '', '', '', '', '',
'', '', '', '', '', '',
'', '', '', '', '', '',
'', '', '', '', '', '',
'', '', '', '', '', '',
'', '', '', '', '', '',
'', '', '', '', '', '',
'', '', '', '', '', '',
'', '', '', '', '', '',
'', '', '', '', '', '',
'', '', '', '', '', '',
'', '', '', '', '', '',
'', '', '', '', '', '',
'', '', '', '', '', '',
'', '', '', '', '', '',
'', '', '', '', '', '',
'', '', '', '', '', '',
'', '', '', '', '', '',
'', '', '', '', '', '',
'', '', '', '', '', '',
'', '', '', '', '', '',
'', '', '', '', '', '',
'', '', '', '', '', '',
'', '', '', '', '', '',
'', '', '', '', '', '',
'', '', '', '', '', '',
'', '', '', '', '', '',
'', '', '', '', '', '',
'', '', '', '', '', '',
'', '', '', '', '', '',
'', '', '', '', '', '',
'', '', '', '', '', '',
'', '', '', '', '', '',
'', '', '', '', '', '',
'', '', '', '', '', '',
'', '', '', '', '', '',
'', '', '', '', '', '',
'', '', '', '', '', '',
'', '', '', '', '', '',
'', '', '', '', '', '',
'', '', '', '', '', '',
'', '', '', '', '', '',
'', '', '', '', '', '',
'', '', '', '', '', '',
'', '', '', '', '', '',
'', '', '', '', '', '',
'', '', '', '', '', '',
'', '', '', '', '', '',
'', '', '', '鿄'};
/**
* Pinyin array.
@@ -334,18 +335,17 @@ public class HanziToPinyin {
{90, 85, 0, 0, 0, 0}, {90, 85, 65, 78, 0, 0},
{90, 85, 73, 0, 0, 0}, {90, 85, 78, 0, 0, 0},
{90, 85, 79, 0, 0, 0}, {0, 0, 0, 0, 0, 0},
{83, 72, 65, 78, 0, 0}, {0, 0, 0, 0, 0, 0},};
{83, 72, 65, 78, 0, 0}, {0, 0, 0, 0, 0, 0}};
/**
* First and last Chinese character with known Pinyin according to zh collation
*/
private static final String FIRST_PINYIN_UNIHAN = "\u963F";
private static final String LAST_PINYIN_UNIHAN = "\u9FFF";
private static final String FIRST_PINYIN_UNIHAN = "";
private static final String LAST_PINYIN_UNIHAN = "鿿";
private static final Collator COLLATOR = Collator.getInstance(Locale.CHINA);
private static HanziToPinyin sInstance;
private final boolean mHasChinaCollator;
public static class Token {
/**
@@ -381,10 +381,6 @@ public class HanziToPinyin {
public String target;
}
protected HanziToPinyin(boolean hasChinaCollator) {
mHasChinaCollator = hasChinaCollator;
}
public static HanziToPinyin getInstance() {
synchronized (HanziToPinyin.class) {
if (sInstance != null) {

View File

@@ -76,6 +76,7 @@ private data class LinkInfo(
val end: Int
)
@Suppress("HttpUrlsUsage")
private fun extractUrls(text: String): List<LinkInfo> = buildList {
val matcher = urlPattern.matcher(text)
while (matcher.find()) {

View File

@@ -10,15 +10,15 @@ import android.os.SystemClock
import android.provider.OpenableColumns
import android.system.Os
import android.util.Log
import com.sukisu.ultra.Natives
import com.sukisu.ultra.ksuApp
import com.topjohnwu.superuser.CallbackList
import com.topjohnwu.superuser.Shell
import com.topjohnwu.superuser.ShellUtils
import com.topjohnwu.superuser.io.SuFile
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import kotlinx.parcelize.Parcelize
import com.sukisu.ultra.BuildConfig
import com.sukisu.ultra.Natives
import com.sukisu.ultra.ksuApp
import org.json.JSONArray
import java.io.File
@@ -29,19 +29,8 @@ import java.io.File
*/
private const val TAG = "KsuCli"
private fun getKsuDaemonPath(): String {
return ksuApp.applicationInfo.nativeLibraryDir + File.separator + "libzakozako.so"
}
object KsuCli {
val SHELL: Shell = createRootShell()
val GLOBAL_MNT_SHELL: Shell = createRootShell(true)
}
fun getRootShell(globalMnt: Boolean = false): Shell {
return if (globalMnt) KsuCli.GLOBAL_MNT_SHELL else {
KsuCli.SHELL
}
private val ksuDaemonPath by lazy {
"${ksuApp.applicationInfo.nativeLibraryDir}${File.separator}libzakozako.so"
}
inline fun <T> withNewRootShell(
@@ -63,37 +52,39 @@ fun Uri.getFileName(context: Context): String? {
return fileName
}
fun createRootShellBuilder(globalMnt: Boolean = false): Shell.Builder {
return Shell.Builder.create().run {
val cmd = buildString {
append("$ksuDaemonPath debug su")
if (globalMnt) append(" -g")
append(" || ")
append("su")
if (globalMnt) append(" --mount-master")
append(" || ")
append("sh")
}
setCommands("sh", "-c", cmd)
}
}
fun createRootShell(globalMnt: Boolean = false): Shell {
Shell.enableVerboseLogging = BuildConfig.DEBUG
val builder = Shell.Builder.create()
return try {
if (globalMnt) {
builder.build(getKsuDaemonPath(), "debug", "su", "-g")
} else {
builder.build(getKsuDaemonPath(), "debug", "su")
}
} catch (e: Throwable) {
Log.w(TAG, "ksu failed: ", e)
try {
if (globalMnt) {
builder.build("su", "-mm")
} else {
builder.build("su")
}
} catch (e: Throwable) {
Log.e(TAG, "su failed: ", e)
builder.build("sh")
}
return runCatching {
createRootShellBuilder(globalMnt).build()
}.getOrElse { e ->
Log.w(TAG, "su failed: ", e)
Shell.Builder.create().apply {
if (globalMnt) setFlags(Shell.FLAG_MOUNT_MASTER)
}.build()
}
}
fun execKsud(args: String, newShell: Boolean = false): Boolean {
return if (newShell) {
withNewRootShell {
ShellUtils.fastCmdResult(this, "${getKsuDaemonPath()} $args")
ShellUtils.fastCmdResult(this, "$ksuDaemonPath $args")
}
} else {
ShellUtils.fastCmdResult(getRootShell(), "${getKsuDaemonPath()} $args")
ShellUtils.fastCmdResult("$ksuDaemonPath $args")
}
}
@@ -105,19 +96,15 @@ fun install() {
}
fun listModules(): String {
val shell = getRootShell()
val out =
shell.newJob().add("${getKsuDaemonPath()} module list").to(ArrayList(), null).exec().out
Shell.cmd("$ksuDaemonPath module list").to(ArrayList(), null).exec().out
return out.joinToString("\n").ifBlank { "[]" }
}
fun getModuleCount(): Int {
val result = listModules()
runCatching {
val array = JSONArray(result)
return array.length()
}.getOrElse { return 0 }
return runCatching {
JSONArray(listModules()).length()
}.getOrDefault(0)
}
fun getSuperuserCount(): Int {
@@ -185,7 +172,7 @@ fun flashModule(
this?.copyTo(output)
}
val cmd = "module install ${file.absolutePath}"
val result = flashWithIO("${getKsuDaemonPath()} $cmd", onStdout, onStderr)
val result = flashWithIO("$ksuDaemonPath $cmd", onStdout, onStderr)
Log.i("KernelSU", "install module $uri result: $result")
file.delete()
@@ -212,7 +199,7 @@ fun runModuleAction(
}
}
val result = shell.newJob().add("${getKsuDaemonPath()} module action $moduleId")
val result = shell.newJob().add("$ksuDaemonPath module action $moduleId")
.to(stdoutCallback, stderrCallback).exec()
Log.i("KernelSU", "Module runAction result: $result")
@@ -224,7 +211,7 @@ fun restoreBoot(
): Boolean {
val magiskboot = File(ksuApp.applicationInfo.nativeLibraryDir, "libzakoboot.so")
val result = flashWithIO(
"${getKsuDaemonPath()} boot-restore -f --magiskboot $magiskboot",
"$ksuDaemonPath boot-restore -f --magiskboot $magiskboot",
onStdout,
onStderr
)
@@ -237,7 +224,7 @@ fun uninstallPermanently(
): Boolean {
val magiskboot = File(ksuApp.applicationInfo.nativeLibraryDir, "libzakoboot.so")
val result =
flashWithIO("${getKsuDaemonPath()} uninstall --magiskboot $magiskboot", onStdout, onStderr)
flashWithIO("$ksuDaemonPath uninstall --magiskboot $magiskboot", onStdout, onStderr)
onFinish(result.isSuccess, result.code)
return result.isSuccess
}
@@ -312,7 +299,7 @@ fun installBoot(
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)
cmd += " -o $downloadsDir"
val result = flashWithIO("${getKsuDaemonPath()} $cmd", onStdout, onStderr)
val result = flashWithIO("$ksuDaemonPath $cmd", onStdout, onStderr)
Log.i("KernelSU", "install boot result: ${result.isSuccess}")
bootFile?.delete()
@@ -324,22 +311,17 @@ fun installBoot(
}
fun reboot(reason: String = "") {
val shell = getRootShell()
if (reason == "recovery") {
// KEYCODE_POWER = 26, hide incorrect "Factory data reset" message
ShellUtils.fastCmd(shell, "/system/bin/input keyevent 26")
ShellUtils.fastCmdResult("/system/bin/input keyevent 26")
}
ShellUtils.fastCmd(shell, "/system/bin/svc power reboot $reason || /system/bin/reboot $reason")
ShellUtils.fastCmdResult("/system/bin/svc power reboot $reason || /system/bin/reboot $reason")
}
fun rootAvailable(): Boolean {
val shell = getRootShell()
return shell.isRoot
}
fun rootAvailable() = Shell.isAppGrantedRoot() == true
fun isAbDevice(): Boolean {
val shell = getRootShell()
return ShellUtils.fastCmd(shell, "getprop ro.build.ab_update").trim().toBoolean()
return ShellUtils.fastCmd("getprop ro.build.ab_update").trim().toBoolean()
}
fun isInitBoot(): Boolean {
@@ -347,91 +329,77 @@ fun isInitBoot(): Boolean {
}
suspend fun getCurrentKmi(): String = withContext(Dispatchers.IO) {
val shell = getRootShell()
val cmd = "boot-info current-kmi"
ShellUtils.fastCmd(shell, "${getKsuDaemonPath()} $cmd")
ShellUtils.fastCmd("$ksuDaemonPath $cmd")
}
suspend fun getSupportedKmis(): List<String> = withContext(Dispatchers.IO) {
val shell = getRootShell()
val cmd = "boot-info supported-kmi"
val out = shell.newJob().add("${getKsuDaemonPath()} $cmd").to(ArrayList(), null).exec().out
val out = Shell.cmd("$ksuDaemonPath $cmd").to(ArrayList(), null).exec().out
out.filter { it.isNotBlank() }.map { it.trim() }
}
fun hasMagisk(): Boolean {
val shell = getRootShell(true)
val result = shell.newJob().add("which magisk").exec()
Log.i(TAG, "has magisk: ${result.isSuccess}")
return result.isSuccess
val result = ShellUtils.fastCmdResult("which magisk")
Log.i(TAG, "has magisk: $result")
return result
}
fun isSepolicyValid(rules: String?): Boolean {
if (rules == null) {
return true
}
val shell = getRootShell()
val result =
shell.newJob().add("${getKsuDaemonPath()} sepolicy check '$rules'").to(ArrayList(), null)
Shell.cmd("$ksuDaemonPath sepolicy check '$rules'").to(ArrayList(), null)
.exec()
return result.isSuccess
}
fun getSepolicy(pkg: String): String {
val shell = getRootShell()
val result =
shell.newJob().add("${getKsuDaemonPath()} profile get-sepolicy $pkg").to(ArrayList(), null)
Shell.cmd("$ksuDaemonPath profile get-sepolicy $pkg").to(ArrayList(), null)
.exec()
Log.i(TAG, "code: ${result.code}, out: ${result.out}, err: ${result.err}")
return result.out.joinToString("\n")
}
fun setSepolicy(pkg: String, rules: String): Boolean {
val shell = getRootShell()
val result = shell.newJob().add("${getKsuDaemonPath()} profile set-sepolicy $pkg '$rules'")
val result = Shell.cmd("$ksuDaemonPath profile set-sepolicy $pkg '$rules'")
.to(ArrayList(), null).exec()
Log.i(TAG, "set sepolicy result: ${result.code}")
return result.isSuccess
}
fun listAppProfileTemplates(): List<String> {
val shell = getRootShell()
return shell.newJob().add("${getKsuDaemonPath()} profile list-templates").to(ArrayList(), null)
return Shell.cmd("$ksuDaemonPath profile list-templates").to(ArrayList(), null)
.exec().out
}
fun getAppProfileTemplate(id: String): String {
val shell = getRootShell()
return shell.newJob().add("${getKsuDaemonPath()} profile get-template '${id}'")
return Shell.cmd("$ksuDaemonPath profile get-template '${id}'")
.to(ArrayList(), null).exec().out.joinToString("\n")
}
fun setAppProfileTemplate(id: String, template: String): Boolean {
val shell = getRootShell()
val escapedTemplate = template.replace("\"", "\\\"")
val cmd = """${getKsuDaemonPath()} profile set-template "$id" "$escapedTemplate'""""
return shell.newJob().add(cmd)
val cmd = """$ksuDaemonPath profile set-template "$id" "$escapedTemplate'""""
return Shell.cmd(cmd)
.to(ArrayList(), null).exec().isSuccess
}
fun deleteAppProfileTemplate(id: String): Boolean {
val shell = getRootShell()
return shell.newJob().add("${getKsuDaemonPath()} profile delete-template '${id}'")
return Shell.cmd("$ksuDaemonPath profile delete-template '${id}'")
.to(ArrayList(), null).exec().isSuccess
}
fun forceStopApp(packageName: String) {
val shell = getRootShell()
val result = shell.newJob().add("am force-stop $packageName").exec()
val result = Shell.cmd("am force-stop $packageName").exec()
Log.i(TAG, "force stop $packageName result: $result")
}
fun launchApp(packageName: String) {
val shell = getRootShell()
val result =
shell.newJob()
.add("cmd package resolve-activity --brief $packageName | tail -n 1 | xargs cmd activity start-activity -n")
Shell.cmd("cmd package resolve-activity --brief $packageName | tail -n 1 | xargs cmd activity start-activity -n")
.exec()
Log.i(TAG, "launch $packageName result: $result")
}
@@ -441,131 +409,90 @@ fun restartApp(packageName: String) {
launchApp(packageName)
}
fun getSuSFSDaemonPath(): String {
return ksuApp.applicationInfo.nativeLibraryDir + File.separator + "libzakozakozako.so"
val suSFSDaemonPath by lazy {
"${ksuApp.applicationInfo.nativeLibraryDir}${File.separator}libzakozakozako.so"
}
fun getSuSFS(): String {
val shell = getRootShell()
val result = ShellUtils.fastCmd(shell, "${getSuSFSDaemonPath()} support")
return result
return ShellUtils.fastCmd("$suSFSDaemonPath support")
}
fun getSuSFSVersion(): String {
val shell = getRootShell()
val result = ShellUtils.fastCmd(shell, "${getSuSFSDaemonPath()} version")
return result
return ShellUtils.fastCmd("$suSFSDaemonPath version")
}
fun getSuSFSVariant(): String {
val shell = getRootShell()
val result = ShellUtils.fastCmd(shell, "${getSuSFSDaemonPath()} variant")
return result
return ShellUtils.fastCmd("$suSFSDaemonPath variant")
}
fun getSuSFSFeatures(): String {
val shell = getRootShell()
val result = ShellUtils.fastCmd(shell, "${getSuSFSDaemonPath()} features")
return result
return ShellUtils.fastCmd("$suSFSDaemonPath features")
}
fun susfsSUS_SU_0(): String {
val shell = getRootShell()
val result = ShellUtils.fastCmd(shell, "${getSuSFSDaemonPath()} sus_su 0")
return result
return ShellUtils.fastCmd("$suSFSDaemonPath sus_su 0")
}
fun susfsSUS_SU_2(): String {
val shell = getRootShell()
val result = ShellUtils.fastCmd(shell, "${getSuSFSDaemonPath()} sus_su 2")
return result
return ShellUtils.fastCmd("$suSFSDaemonPath sus_su 2")
}
fun susfsSUS_SU_Mode(): String {
val shell = getRootShell()
val result = ShellUtils.fastCmd(shell, "${getSuSFSDaemonPath()} sus_su mode")
return result
return ShellUtils.fastCmd("$suSFSDaemonPath sus_su mode")
}
fun getKpmmgrPath(): String {
return ksuApp.applicationInfo.nativeLibraryDir + File.separator + "libkpmmgr.so"
val kpmmgrPath by lazy {
"${ksuApp.applicationInfo.nativeLibraryDir}${File.separator}libkpmmgr.so"
}
fun loadKpmModule(path: String, args: String? = null): String {
val shell = getRootShell()
val cmd = "${getKpmmgrPath()} load $path ${args ?: ""}"
return ShellUtils.fastCmd(shell, cmd)
return ShellUtils.fastCmd("$kpmmgrPath load $path ${args ?: ""}")
}
fun unloadKpmModule(name: String): String {
val shell = getRootShell()
val cmd = "${getKpmmgrPath()} unload $name"
return ShellUtils.fastCmd(shell, cmd)
return ShellUtils.fastCmd("$kpmmgrPath unload $name")
}
fun getKpmModuleCount(): Int {
val shell = getRootShell()
val cmd = "${getKpmmgrPath()} num"
val result = ShellUtils.fastCmd(shell, cmd)
val result = ShellUtils.fastCmd("$kpmmgrPath num")
return result.trim().toIntOrNull() ?: 0
}
fun runCmd(shell: Shell, cmd: String): String {
return shell.newJob()
.add(cmd)
fun runCmd(cmd: String): String {
return Shell.cmd(cmd)
.to(mutableListOf<String>(), null)
.exec().out
.joinToString("\n")
}
fun listKpmModules(): String {
val shell = getRootShell()
val cmd = "${getKpmmgrPath()} list"
return try {
runCmd(shell, cmd).trim()
} catch (e: Exception) {
Log.e(TAG, "Failed to list KPM modules", e)
""
}
return runCmd("$kpmmgrPath list").trim()
}
fun getKpmModuleInfo(name: String): String {
val shell = getRootShell()
val cmd = "${getKpmmgrPath()} info $name"
return try {
runCmd(shell, cmd).trim()
} catch (e: Exception) {
Log.e(TAG, "Failed to get KPM module info: $name", e)
""
}
return runCmd("$kpmmgrPath info $name").trim()
}
fun controlKpmModule(name: String, args: String? = null): Int {
val shell = getRootShell()
val cmd = """${getKpmmgrPath()} control $name "${args ?: ""}""""
val result = runCmd(shell, cmd)
val result = runCmd("""$kpmmgrPath control $name "${args ?: ""}"""")
return result.trim().toIntOrNull() ?: -1
}
fun getKpmVersion(): String {
val shell = getRootShell()
val cmd = "${getKpmmgrPath()} version"
val result = ShellUtils.fastCmd(shell, cmd)
return result.trim()
return ShellUtils.fastCmd("$kpmmgrPath version").trim()
}
fun getZygiskImplement(): String {
val shell = getRootShell()
val zygiskPath = "/data/adb/modules/zygisksu"
val rezygiskPath = "/data/adb/modules/rezygisk"
val result = if (ShellUtils.fastCmdResult(shell, "test -f $zygiskPath/module.prop && test ! -f $zygiskPath/disable")) {
ShellUtils.fastCmd(shell, "grep '^name=' $zygiskPath/module.prop | cut -d'=' -f2")
} else if (ShellUtils.fastCmdResult(shell, "test -f $rezygiskPath/module.prop && test ! -f $rezygiskPath/disable")) {
ShellUtils.fastCmd(shell, "grep '^name=' $rezygiskPath/module.prop | cut -d'=' -f2")
} else {
"None"
}
val result = when {
SuFile(zygiskPath, "module.prop").exists() && !SuFile(zygiskPath, "disable").exists() ->
ShellUtils.fastCmd("grep '^name=' $zygiskPath/module.prop | cut -d'=' -f2")
SuFile(rezygiskPath, "module.prop").exists() && !SuFile(rezygiskPath, "disable").exists() ->
ShellUtils.fastCmd("grep '^name=' $rezygiskPath/module.prop | cut -d'=' -f2")
else -> "None"
}.trim()
Log.i(TAG, "Zygisk implement: $result")
return result
}

View File

@@ -3,9 +3,10 @@ package com.sukisu.ultra.ui.util
import android.content.Context
import android.os.Build
import android.system.Os
import com.topjohnwu.superuser.ShellUtils
import com.sukisu.ultra.Natives
import com.sukisu.ultra.ui.screen.getManagerVersion
import com.topjohnwu.superuser.Shell
import com.topjohnwu.superuser.ShellUtils
import java.io.File
import java.io.FileWriter
import java.io.PrintWriter
@@ -38,30 +39,28 @@ fun getBugreportFile(context: Context): File {
val bootConfig = File(bugreportDir, "boot_config.txt")
val kernelConfig = File(bugreportDir, "defconfig.gz")
val shell = getRootShell(true)
Shell.cmd("dmesg > ${dmesgFile.absolutePath}").exec()
Shell.cmd("logcat -d > ${logcatFile.absolutePath}").exec()
Shell.cmd("tar -czf ${tombstonesFile.absolutePath} -C /data/tombstones .").exec()
Shell.cmd("tar -czf ${dropboxFile.absolutePath} -C /data/system/dropbox .").exec()
Shell.cmd("tar -czf ${pstoreFile.absolutePath} -C /sys/fs/pstore .").exec()
Shell.cmd("tar -czf ${diagFile.absolutePath} -C /data/vendor/diag . --exclude=./minidump.gz").exec()
Shell.cmd("tar -czf ${oplusFile.absolutePath} -C /mnt/oplus/op2/media/log/boot_log/ .").exec()
Shell.cmd("tar -czf ${bootlogFile.absolutePath} -C /data/adb/ksu/log .").exec()
shell.newJob().add("dmesg > ${dmesgFile.absolutePath}").exec()
shell.newJob().add("logcat -d > ${logcatFile.absolutePath}").exec()
shell.newJob().add("tar -czf ${tombstonesFile.absolutePath} -C /data/tombstones .").exec()
shell.newJob().add("tar -czf ${dropboxFile.absolutePath} -C /data/system/dropbox .").exec()
shell.newJob().add("tar -czf ${pstoreFile.absolutePath} -C /sys/fs/pstore .").exec()
shell.newJob().add("tar -czf ${diagFile.absolutePath} -C /data/vendor/diag . --exclude=./minidump.gz").exec()
shell.newJob().add("tar -czf ${oplusFile.absolutePath} -C /mnt/oplus/op2/media/log/boot_log/ .").exec()
shell.newJob().add("tar -czf ${bootlogFile.absolutePath} -C /data/adb/ksu/log .").exec()
Shell.cmd("cat /proc/1/mountinfo > ${mountsFile.absolutePath}").exec()
Shell.cmd("cat /proc/filesystems > ${fileSystemsFile.absolutePath}").exec()
Shell.cmd("busybox tree /data/adb > ${adbFileTree.absolutePath}").exec()
Shell.cmd("ls -alRZ /data/adb > ${adbFileDetails.absolutePath}").exec()
Shell.cmd("du -sh /data/adb/ksu/* > ${ksuFileSize.absolutePath}").exec()
Shell.cmd("cp /data/system/packages.list ${appListFile.absolutePath}").exec()
Shell.cmd("getprop > ${propFile.absolutePath}").exec()
Shell.cmd("cp /data/adb/ksu/.allowlist ${allowListFile.absolutePath}").exec()
Shell.cmd("cp /proc/modules ${procModules.absolutePath}").exec()
Shell.cmd("cp /proc/bootconfig ${bootConfig.absolutePath}").exec()
Shell.cmd("cp /proc/config.gz ${kernelConfig.absolutePath}").exec()
shell.newJob().add("cat /proc/1/mountinfo > ${mountsFile.absolutePath}").exec()
shell.newJob().add("cat /proc/filesystems > ${fileSystemsFile.absolutePath}").exec()
shell.newJob().add("busybox tree /data/adb > ${adbFileTree.absolutePath}").exec()
shell.newJob().add("ls -alRZ /data/adb > ${adbFileDetails.absolutePath}").exec()
shell.newJob().add("du -sh /data/adb/ksu/* > ${ksuFileSize.absolutePath}").exec()
shell.newJob().add("cp /data/system/packages.list ${appListFile.absolutePath}").exec()
shell.newJob().add("getprop > ${propFile.absolutePath}").exec()
shell.newJob().add("cp /data/adb/ksu/.allowlist ${allowListFile.absolutePath}").exec()
shell.newJob().add("cp /proc/modules ${procModules.absolutePath}").exec()
shell.newJob().add("cp /proc/bootconfig ${bootConfig.absolutePath}").exec()
shell.newJob().add("cp /proc/config.gz ${kernelConfig.absolutePath}").exec()
val selinux = ShellUtils.fastCmd(shell, "getenforce")
val selinux = ShellUtils.fastCmd("getenforce")
// basic information
val buildInfo = File(bugreportDir, "basic.txt")
@@ -102,9 +101,9 @@ fun getBugreportFile(context: Context): File {
val targetFile = File(context.cacheDir, "KernelSU_bugreport_${current}.tar.gz")
shell.newJob().add("tar czf ${targetFile.absolutePath} -C ${bugreportDir.absolutePath} .").exec()
shell.newJob().add("rm -rf ${bugreportDir.absolutePath}").exec()
shell.newJob().add("chmod 0644 ${targetFile.absolutePath}").exec()
Shell.cmd("tar czf ${targetFile.absolutePath} -C ${bugreportDir.absolutePath} .").exec()
Shell.cmd("rm -rf ${bugreportDir.absolutePath}").exec()
Shell.cmd("chmod 0644 ${targetFile.absolutePath}").exec()
return targetFile
}

View File

@@ -9,17 +9,16 @@ import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.platform.LocalContext
import com.sukisu.ultra.R
import kotlinx.coroutines.CompletableDeferred
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import com.sukisu.ultra.R
import java.io.BufferedReader
import java.io.IOException
import java.io.InputStreamReader
import java.text.SimpleDateFormat
import java.util.Date
import java.util.Locale
import java.util.*
object ModuleModify {
@Composable
@@ -442,8 +441,4 @@ object ModuleModify {
type = "application/octet-stream"
}
}
private fun reboot() {
Runtime.getRuntime().exec(arrayOf("su", "-c", "reboot"))
}
}

View File

@@ -3,13 +3,13 @@ package com.sukisu.ultra.ui.util
import android.content.Context
import android.content.Intent
import android.net.Uri
import android.util.Log
import com.sukisu.ultra.R
import java.io.BufferedReader
import java.io.IOException
import java.io.InputStreamReader
import java.nio.charset.StandardCharsets
import java.util.zip.ZipInputStream
import com.sukisu.ultra.R
import android.util.Log
import java.io.IOException
object ModuleUtils {
private const val TAG = "ModuleUtils"
@@ -108,10 +108,7 @@ object ModuleUtils {
return try {
val inputStream = context.contentResolver.openInputStream(uri)
if (inputStream == null) {
return null
}
val inputStream = context.contentResolver.openInputStream(uri) ?: return null
val zipInputStream = ZipInputStream(inputStream)
var entry = zipInputStream.nextEntry

View File

@@ -4,6 +4,7 @@ import android.content.Context
import android.net.Uri
import android.util.Log
import com.sukisu.ultra.Natives
import com.topjohnwu.superuser.Shell
import java.io.File
import java.io.FileOutputStream
@@ -124,18 +125,17 @@ object ModuleVerificationManager {
// 为指定模块创建验证标志文件
fun createVerificationFlag(moduleId: String): Boolean {
return try {
val shell = getRootShell()
val flagFilePath = "$VERIFICATION_FLAGS_DIR/$moduleId"
// 确保目录存在
val createDirCommand = "mkdir -p '$VERIFICATION_FLAGS_DIR'"
shell.newJob().add(createDirCommand).exec()
Shell.cmd(createDirCommand).exec()
// 创建验证标志文件,写入验证时间戳
val timestamp = System.currentTimeMillis()
val command = "echo '$timestamp' > '$flagFilePath'"
val result = shell.newJob().add(command).exec()
val result = Shell.cmd(command).exec()
if (result.isSuccess) {
Log.d(TAG, "验证标志文件创建成功: $flagFilePath")
@@ -152,11 +152,10 @@ object ModuleVerificationManager {
fun removeVerificationFlag(moduleId: String): Boolean {
return try {
val shell = getRootShell()
val flagFilePath = "$VERIFICATION_FLAGS_DIR/$moduleId"
val command = "rm -f '$flagFilePath'"
val result = shell.newJob().add(command).exec()
val result = Shell.cmd(command).exec()
if (result.isSuccess) {
Log.d(TAG, "验证标志文件移除成功: $flagFilePath")
@@ -173,11 +172,10 @@ object ModuleVerificationManager {
fun getVerificationTimestamp(moduleId: String): Long {
return try {
val shell = getRootShell()
val flagFilePath = "$VERIFICATION_FLAGS_DIR/$moduleId"
val command = "cat '$flagFilePath' 2>/dev/null || echo '0'"
val result = shell.newJob().add(command).to(ArrayList(), null).exec()
val result = Shell.cmd(command).to(ArrayList(), null).exec()
if (result.isSuccess && result.out.isNotEmpty()) {
val timestampStr = result.out.firstOrNull()?.trim() ?: "0"
@@ -195,12 +193,11 @@ object ModuleVerificationManager {
if (moduleIds.isEmpty()) return emptyMap()
return try {
val shell = getRootShell()
val result = mutableMapOf<String, Boolean>()
// 确保目录存在
val createDirCommand = "mkdir -p '$VERIFICATION_FLAGS_DIR'"
shell.newJob().add(createDirCommand).exec()
Shell.cmd(createDirCommand).exec()
// 批量检查所有模块的验证标志文件
val commands = moduleIds.map { moduleId ->
@@ -208,7 +205,7 @@ object ModuleVerificationManager {
}
val command = commands.joinToString(" && ")
val shellResult = shell.newJob().add(command).to(ArrayList(), null).exec()
val shellResult = Shell.cmd(command).to(ArrayList(), null).exec()
if (shellResult.isSuccess) {
shellResult.out.forEach { line ->

View File

@@ -1,31 +1,19 @@
package com.sukisu.ultra.ui.util
import android.content.Context
import com.topjohnwu.superuser.Shell
import com.sukisu.ultra.R
import com.topjohnwu.superuser.io.SuFile
fun getSELinuxStatus(context: Context): String {
val shell = Shell.Builder.create().build("sh")
val list = ArrayList<String>()
val result = shell.use {
it.newJob().add("getenforce").to(list, list).exec()
}
val output = list.joinToString("\n").trim()
return if (result.isSuccess) {
when (output) {
"Enforcing" -> context.getString(R.string.selinux_status_enforcing)
"Permissive" -> context.getString(R.string.selinux_status_permissive)
"Disabled" -> context.getString(R.string.selinux_status_disabled)
fun getSELinuxStatus(context: Context) = SuFile("/sys/fs/selinux/enforce").run {
when {
!exists() -> context.getString(R.string.selinux_status_disabled)
!isFile -> context.getString(R.string.selinux_status_unknown)
!canRead() -> context.getString(R.string.selinux_status_enforcing)
else -> when (runCatching { newInputStream() }.getOrNull()?.bufferedReader()
?.use { it.runCatching { readLine() }.getOrNull()?.trim()?.toIntOrNull() }) {
1 -> context.getString(R.string.selinux_status_enforcing)
0 -> context.getString(R.string.selinux_status_permissive)
else -> context.getString(R.string.selinux_status_unknown)
}
} else {
if (output.contains("Permission denied")) {
context.getString(R.string.selinux_status_enforcing)
} else {
context.getString(R.string.selinux_status_unknown)
}
}
}

View File

@@ -7,22 +7,17 @@ import android.content.pm.ApplicationInfo
import android.content.pm.PackageInfo
import android.util.Log
import android.widget.Toast
import androidx.core.content.edit
import com.dergoogler.mmrl.platform.Platform.Companion.context
import com.sukisu.ultra.Natives
import com.sukisu.ultra.R
import com.sukisu.ultra.ui.viewmodel.SuperUserViewModel
import com.topjohnwu.superuser.Shell
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withContext
import kotlinx.coroutines.*
import org.json.JSONObject
import java.io.File
import java.io.FileOutputStream
import java.io.IOException
import androidx.core.content.edit
import com.sukisu.ultra.ui.viewmodel.SuperUserViewModel
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
import org.json.JSONObject
import java.text.SimpleDateFormat
import java.util.*
@@ -430,12 +425,10 @@ object SuSFSManager {
async(Dispatchers.IO) {
val dataPath = "$MEDIA_DATA_PATH/${appInfo.packageName}"
val exists = try {
val shell = getRootShell()
val outputList = mutableListOf<String>()
val errorList = mutableListOf<String>()
val result = shell.newJob()
.add("[ -d \"$dataPath\" ] && echo 'exists' || echo 'not_exists'")
val result = Shell.cmd("[ -d \"$dataPath\" ] && echo 'exists' || echo 'not_exists'")
.to(outputList, errorList)
.exec()

View File

@@ -8,6 +8,7 @@ import android.util.Log
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.core.content.edit
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.dergoogler.mmrl.platform.Platform.Companion.context
@@ -22,7 +23,6 @@ import com.sukisu.ultra.ui.util.module.LatestVersionInfo
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import androidx.core.content.edit
class HomeViewModel : ViewModel() {
companion object {

View File

@@ -6,10 +6,10 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.sukisu.ultra.ui.util.*
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import com.sukisu.ultra.ui.util.*
/**
* @author ShirkNeko

View File

@@ -7,26 +7,26 @@ import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.core.content.edit
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.dergoogler.mmrl.platform.model.ModuleConfig
import com.dergoogler.mmrl.platform.model.ModuleConfig.Companion.asModuleConfig
import com.sukisu.ultra.ui.util.HanziToPinyin
import com.sukisu.ultra.ui.util.ModuleVerificationManager
import com.sukisu.ultra.ui.util.listModules
import com.topjohnwu.superuser.Shell
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import com.sukisu.ultra.ui.util.HanziToPinyin
import com.sukisu.ultra.ui.util.listModules
import com.sukisu.ultra.ui.util.getRootShell
import com.sukisu.ultra.ui.util.ModuleVerificationManager
import kotlinx.coroutines.withContext
import org.json.JSONArray
import org.json.JSONObject
import java.text.Collator
import java.text.DecimalFormat
import java.util.Locale
import java.util.*
import java.util.concurrent.TimeUnit
import kotlin.math.log10
import kotlin.math.pow
import androidx.core.content.edit
/**
* @author ShirkNeko
@@ -452,9 +452,8 @@ class ModuleSizeCache(context: Context) {
*/
private fun calculateModuleFolderSize(dirId: String): Long {
return try {
val shell = getRootShell()
val command = "du -sb /data/adb/modules/$dirId"
val result = shell.newJob().add(command).to(ArrayList(), null).exec()
val result = Shell.cmd(command).to(ArrayList(), null).exec()
if (result.isSuccess && result.out.isNotEmpty()) {
val sizeStr = result.out.firstOrNull()?.split("\t")?.firstOrNull()

View File

@@ -1,43 +1,29 @@
package com.sukisu.ultra.ui.viewmodel
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.content.ServiceConnection
import android.content.SharedPreferences
import android.content.*
import android.content.pm.ApplicationInfo
import android.content.pm.PackageInfo
import android.os.IBinder
import android.os.Parcelable
import android.os.SystemClock
import android.util.Log
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableFloatStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.compose.runtime.*
import androidx.core.content.edit
import androidx.lifecycle.ViewModel
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.supervisorScope
import com.sukisu.ultra.Natives
import com.sukisu.ultra.ksuApp
import com.sukisu.ultra.ui.KsuService
import com.sukisu.ultra.ui.util.HanziToPinyin
import com.topjohnwu.superuser.Shell
import kotlinx.coroutines.*
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import kotlinx.parcelize.Parcelize
import com.sukisu.ultra.Natives
import com.sukisu.ultra.ksuApp
import com.sukisu.ultra.ui.util.HanziToPinyin
import java.text.Collator
import java.util.*
import java.util.concurrent.LinkedBlockingQueue
import java.util.concurrent.ThreadPoolExecutor
import java.util.concurrent.TimeUnit
import java.util.concurrent.LinkedBlockingQueue
import androidx.core.content.edit
import com.sukisu.ultra.ui.KsuService
import com.sukisu.ultra.ui.util.KsuCli
import com.topjohnwu.superuser.Shell
import kotlinx.coroutines.asCoroutineDispatcher
import kotlin.coroutines.resume
import kotlin.coroutines.suspendCoroutine
@@ -428,8 +414,7 @@ class SuperUserViewModel : ViewModel() {
Shell.EXECUTOR,
connection
)
val shell = KsuCli.SHELL
task?.let { shell.execTask(it) }
task?.let { Shell.getShell().execTask(it) }
} catch (e: Exception) {
Log.e(TAG, "Failed to bind KsuService", e)
continuation.resume(null)

View File

@@ -7,21 +7,21 @@ import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.lifecycle.ViewModel
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import kotlinx.parcelize.Parcelize
import com.sukisu.ultra.Natives
import com.sukisu.ultra.profile.Capabilities
import com.sukisu.ultra.profile.Groups
import com.sukisu.ultra.ui.util.getAppProfileTemplate
import com.sukisu.ultra.ui.util.listAppProfileTemplates
import com.sukisu.ultra.ui.util.setAppProfileTemplate
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import kotlinx.parcelize.Parcelize
import okhttp3.OkHttpClient
import okhttp3.Request
import org.json.JSONArray
import org.json.JSONObject
import java.text.Collator
import java.util.Locale
import java.util.*
import java.util.concurrent.TimeUnit

View File

@@ -1,13 +1,13 @@
package com.sukisu.ultra.ui.webui
import android.content.ServiceConnection
import android.util.Log
import android.content.pm.PackageInfo
import android.util.Log
import com.dergoogler.mmrl.platform.Platform
import com.dergoogler.mmrl.platform.model.IProvider
import com.dergoogler.mmrl.platform.model.PlatformIntent
import com.sukisu.ultra.ksuApp
import com.sukisu.ultra.Natives
import com.sukisu.ultra.ksuApp
import com.topjohnwu.superuser.ipc.RootService
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay

View File

@@ -3,11 +3,9 @@ package com.sukisu.ultra.ui.webui;
import android.content.Context;
import android.util.Log;
import android.webkit.WebResourceResponse;
import androidx.annotation.NonNull;
import androidx.annotation.WorkerThread;
import androidx.webkit.WebViewAssetLoader;
import com.topjohnwu.superuser.Shell;
import com.topjohnwu.superuser.io.SuFile;
import com.topjohnwu.superuser.io.SuFileInputStream;

View File

@@ -16,16 +16,14 @@ import androidx.core.view.WindowInsetsCompat
import androidx.core.view.updateLayoutParams
import androidx.webkit.WebViewAssetLoader
import com.dergoogler.mmrl.platform.model.ModId
import com.topjohnwu.superuser.Shell
import com.dergoogler.mmrl.webui.interfaces.WXOptions
import com.sukisu.ultra.ui.util.createRootShell
import java.io.File
import com.dergoogler.mmrl.webui.interfaces.WXOptions
@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?) {
@@ -37,8 +35,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("SukiSU-Ultra - $name"))
@@ -53,7 +51,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(
@@ -72,6 +69,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> {
@@ -85,8 +84,7 @@ class WebUIActivity : ComponentActivity() {
settings.javaScriptEnabled = true
settings.domStorageEnabled = true
settings.allowFileAccess = false
webviewInterface = WebViewInterface(WXOptions(this@WebUIActivity, this, ModId(moduleId)))
addJavascriptInterface(webviewInterface, "ksu")
addJavascriptInterface(WebViewInterface(WXOptions(this@WebUIActivity, this, ModId(moduleId))), "ksu")
setWebViewClient(webViewClient)
loadUrl("https://mui.kernelsu.org/index.html")
}
@@ -95,7 +93,13 @@ class WebUIActivity : ComponentActivity() {
}
override fun onDestroy() {
rootShell.runCatching { close() }
webView?.apply {
stopLoading()
removeAllViews()
destroy()
webView = null
}
super.onDestroy()
runCatching { rootShell?.close() }
}
}

View File

@@ -8,11 +8,7 @@ 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.compose.runtime.*
import androidx.lifecycle.lifecycleScope
import com.dergoogler.mmrl.platform.Platform
import com.dergoogler.mmrl.platform.model.ModId

View File

@@ -12,18 +12,16 @@ import androidx.core.view.WindowInsetsControllerCompat
import com.dergoogler.mmrl.webui.interfaces.WXInterface
import com.dergoogler.mmrl.webui.interfaces.WXOptions
import com.dergoogler.mmrl.webui.model.JavaScriptInterface
import com.sukisu.ultra.ui.util.*
import com.topjohnwu.superuser.CallbackList
import com.topjohnwu.superuser.ShellUtils
import com.topjohnwu.superuser.internal.UiThreadHandler
import com.sukisu.ultra.ui.util.createRootShell
import com.sukisu.ultra.ui.util.listModules
import com.sukisu.ultra.ui.util.withNewRootShell
import org.json.JSONArray
import org.json.JSONObject
import com.sukisu.ultra.ui.util.*
import java.io.File
import java.util.concurrent.CompletableFuture
@Suppress("unused")
class WebViewInterface(
wxOptions: WXOptions,
) : WXInterface(wxOptions) {
@@ -68,56 +66,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)
val finalCommand = buildString {
processOptions(this, options)
if (!TextUtils.isEmpty(args)) {
finalCommand.append(command).append(" ")
append(command).append(" ")
JSONArray(args).let { argsArray ->
for (i in 0 until argsArray.length()) {
finalCommand.append(argsArray.getString(i))
finalCommand.append(" ")
append("${argsArray.getString(i)} ")
}
}
} else {
finalCommand.append(command)
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)
}
}
@@ -133,21 +131,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"
@@ -155,7 +153,7 @@ class WebViewInterface(
)
};${callbackFunc}.emit('error', err); } catch(e) { console.error('emitErr', e); } })();"
webView.post {
webView.loadUrl(emitErrCode)
webView.evaluateJavascript(emitErrCode, null)
}
}
}.whenComplete { _, _ ->

View File

@@ -1,15 +1,13 @@
package io.sukisu.ultra;
import java.util.ArrayList;
import com.topjohnwu.superuser.Shell;
import com.sukisu.ultra.ui.util.KsuCli;
import java.util.ArrayList;
public class UltraShellHelper {
public static String runCmd(String cmds) {
StringBuilder sb = new StringBuilder();
for(String str : KsuCli.INSTANCE.getGLOBAL_MNT_SHELL()
.newJob()
.add(cmds)
for(String str : Shell.cmd(cmds)
.to(new ArrayList<>(), null)
.exec()
.getOut()) {
@@ -18,11 +16,6 @@ public class UltraShellHelper {
return sb.toString();
}
public static boolean isPathExists(String path) {
String result = runCmd("test -f '" + path + "' && echo 'exists'");
return result.contains("exists");
}
public static void CopyFileTo(String path, String target) {
runCmd("cp -f '" + path + "' '" + target + "' 2>&1");
}

View File

@@ -1,5 +1,7 @@
package io.sukisu.ultra;
import com.topjohnwu.superuser.io.SuFile;
import static com.sukisu.ultra.ui.util.KsuCliKt.getKpmmgrPath;
import static com.sukisu.ultra.ui.util.KsuCliKt.getSuSFSDaemonPath;
@@ -7,15 +9,17 @@ public class UltraToolInstall {
private static final String OUTSIDE_KPMMGR_PATH = "/data/adb/ksu/bin/kpmmgr";
private static final String OUTSIDE_SUSFSD_PATH = "/data/adb/ksu/bin/susfsd";
public static void tryToInstall() {
String kpmmgrPath = getKpmmgrPath();
if (UltraShellHelper.isPathExists(OUTSIDE_KPMMGR_PATH)) {
UltraShellHelper.CopyFileTo(kpmmgrPath, OUTSIDE_KPMMGR_PATH);
UltraShellHelper.runCmd("chmod a+rx " + OUTSIDE_KPMMGR_PATH);
SuFile KpmmgrFile = new SuFile(OUTSIDE_KPMMGR_PATH);
if (KpmmgrFile.exists()) {
UltraShellHelper.CopyFileTo(getKpmmgrPath(), OUTSIDE_KPMMGR_PATH);
boolean _ = KpmmgrFile.setReadable(true, false);
boolean _ = KpmmgrFile.setExecutable(true, false);
}
String SuSFSDaemonPath = getSuSFSDaemonPath();
if (UltraShellHelper.isPathExists(OUTSIDE_SUSFSD_PATH)) {
UltraShellHelper.CopyFileTo(SuSFSDaemonPath, OUTSIDE_SUSFSD_PATH);
UltraShellHelper.runCmd("chmod a+rx " + OUTSIDE_SUSFSD_PATH);
SuFile SuSFSDaemonFile = new SuFile(OUTSIDE_SUSFSD_PATH);
if (SuSFSDaemonFile.exists()) {
UltraShellHelper.CopyFileTo(getSuSFSDaemonPath(), OUTSIDE_SUSFSD_PATH);
boolean _ = SuSFSDaemonFile.setReadable(true, false);
boolean _ = SuSFSDaemonFile.setExecutable(true, false);
}
}
}

View File

@@ -1,30 +1,28 @@
package zako.zako.zako.zakoui.activity.component
import android.annotation.SuppressLint
import androidx.compose.foundation.layout.*
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.navigation.NavHostController
import com.ramcosta.composedestinations.generated.NavGraphs
import com.ramcosta.composedestinations.spec.RouteOrDirection
import com.ramcosta.composedestinations.utils.isRouteOnBackStackAsState
import com.ramcosta.composedestinations.utils.rememberDestinationsNavigator
import com.ramcosta.composedestinations.spec.RouteOrDirection
import com.ramcosta.composedestinations.generated.NavGraphs
import com.sukisu.ultra.Natives
import com.sukisu.ultra.ksuApp
import com.sukisu.ultra.ui.MainActivity
import zako.zako.zako.zakoui.activity.util.AppData
import zako.zako.zako.zakoui.activity.util.AppData.getKpmVersionUse
import com.sukisu.ultra.ui.screen.BottomBarDestination
import com.sukisu.ultra.ui.theme.CardConfig.cardAlpha
import com.sukisu.ultra.ui.theme.CardConfig.cardElevation
import androidx.compose.foundation.layout.windowInsetsPadding
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.WindowInsetsSides
import androidx.compose.foundation.layout.only
import androidx.compose.foundation.layout.navigationBars
import zako.zako.zako.zakoui.activity.util.AppData
import zako.zako.zako.zakoui.activity.util.AppData.DataRefreshManager
import zako.zako.zako.zakoui.activity.util.AppData.getKpmVersionUse
@SuppressLint("ContextCastToActivity")
@OptIn(ExperimentalMaterial3Api::class)

View File

@@ -1,10 +1,6 @@
package zako.zako.zako.zakoui.activity.util
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.animation.slideInVertically
import androidx.compose.animation.slideOutVertically
import androidx.compose.animation.*
import androidx.compose.runtime.Composable
object AnimatedBottomBar {

View File

@@ -1,11 +1,7 @@
package zako.zako.zako.zakoui.activity.util
import com.sukisu.ultra.Natives
import com.sukisu.ultra.ui.util.getKpmModuleCount
import com.sukisu.ultra.ui.util.getKpmVersion
import com.sukisu.ultra.ui.util.getModuleCount
import com.sukisu.ultra.ui.util.getSuperuserCount
import com.sukisu.ultra.ui.util.rootAvailable
import com.sukisu.ultra.ui.util.*
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
@@ -84,13 +80,6 @@ object AppData {
}
}
/**
* 检查是否具有管理员权限
*/
fun isManager(packageName: String): Boolean {
return Natives.becomeManager(packageName)
}
/**
* 检查是否是完整功能模式
*/

View File

@@ -3,12 +3,12 @@ package zako.zako.zako.zakoui.activity.util
import android.content.Context
import androidx.lifecycle.LifecycleCoroutineScope
import com.sukisu.ultra.ui.MainActivity
import zako.zako.zako.zakoui.activity.util.AppData.DataRefreshManager
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch
import zako.zako.zako.zakoui.activity.util.AppData.DataRefreshManager
object DataRefreshUtils {

View File

@@ -4,7 +4,7 @@ import android.annotation.SuppressLint
import android.content.Context
import android.content.res.Configuration
import android.os.Build
import java.util.Locale
import java.util.*
object LocaleUtils {

View File

@@ -6,7 +6,9 @@ import android.content.Context
import android.net.Uri
import androidx.documentfile.provider.DocumentFile
import com.sukisu.ultra.R
import com.sukisu.ultra.ui.util.rootAvailable
import com.sukisu.ultra.utils.AssetsUtil
import com.topjohnwu.superuser.Shell
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
@@ -131,7 +133,7 @@ class HorizonKernelWorker(
if (isAbDevice && slot != null) {
state.updateStep(context.getString(R.string.horizon_getting_original_slot))
state.updateProgress(0.72f)
originalSlot = runCommandGetOutput(true, "getprop ro.boot.slot_suffix")
originalSlot = runCommandGetOutput("getprop ro.boot.slot_suffix")
state.updateStep(context.getString(R.string.horizon_setting_target_slot))
state.updateProgress(0.74f)
@@ -165,13 +167,11 @@ class HorizonKernelWorker(
// 检查设备是否为AB分区设备
private fun isAbDevice(): Boolean {
val abUpdate = runCommandGetOutput(true, "getprop ro.build.ab_update")?.trim() ?: ""
if (abUpdate.equals("false", ignoreCase = true) || abUpdate.isEmpty()) {
return false
}
val abUpdate = runCommandGetOutput("getprop ro.build.ab_update")
if (!abUpdate.toBoolean()) return false
val slotSuffix = runCommandGetOutput(true, "getprop ro.boot.slot_suffix")
return !slotSuffix.isNullOrEmpty()
val slotSuffix = runCommandGetOutput("getprop ro.boot.slot_suffix")
return slotSuffix.isNotEmpty()
}
private fun cleanup() {
@@ -195,11 +195,10 @@ class HorizonKernelWorker(
}
}
@SuppressLint("StringFormatInvalid")
private fun patch() {
val kernelVersion = runCommandGetOutput(true, "cat /proc/version")
val kernelVersion = runCommandGetOutput("cat /proc/version")
val versionRegex = """\d+\.\d+\.\d+""".toRegex()
val version = kernelVersion?.let { versionRegex.find(it) }?.value ?: ""
val version = kernelVersion.let { versionRegex.find(it) }?.value ?: ""
val toolName = if (version.isNotEmpty()) {
val parts = version.split('.')
if (parts.size >= 2) {
@@ -286,28 +285,7 @@ class HorizonKernelWorker(
}
}
private fun runCommandGetOutput(su: Boolean, cmd: String): String? {
val shell = if (su) "su" else "sh"
val process = Runtime.getRuntime().exec(arrayOf(shell, "-c", cmd))
return try {
process.inputStream.bufferedReader().use { reader ->
reader.readText().trim()
}
} catch (_: Exception) {
""
} finally {
process.destroy()
}
}
private fun rootAvailable(): Boolean {
return try {
val process = Runtime.getRuntime().exec("su -c true")
val exitValue = process.waitFor()
exitValue == 0
} catch (_: Exception) {
false
}
private fun runCommandGetOutput(cmd: String): String {
return Shell.cmd(cmd).exec().out.joinToString("\n").trim()
}
}

View File

@@ -19,6 +19,8 @@ import androidx.compose.runtime.*
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.input.key.Key
import androidx.compose.ui.input.key.key
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
@@ -29,21 +31,20 @@ import com.ramcosta.composedestinations.annotation.Destination
import com.ramcosta.composedestinations.annotation.RootGraph
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
import com.sukisu.ultra.R
import zako.zako.zako.zakoui.flash.HorizonKernelState
import zako.zako.zako.zakoui.flash.HorizonKernelWorker
import com.sukisu.ultra.ui.component.KeyEventBlocker
import com.sukisu.ultra.ui.util.*
import com.sukisu.ultra.ui.theme.CardConfig
import com.sukisu.ultra.ui.util.LocalSnackbarHost
import com.sukisu.ultra.ui.util.reboot
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import zako.zako.zako.zakoui.flash.FlashState
import zako.zako.zako.zakoui.flash.HorizonKernelState
import zako.zako.zako.zakoui.flash.HorizonKernelWorker
import java.io.File
import java.text.SimpleDateFormat
import java.util.*
import androidx.compose.ui.input.key.Key
import androidx.compose.ui.input.key.key
import com.sukisu.ultra.ui.theme.CardConfig
import zako.zako.zako.zakoui.flash.FlashState
import kotlinx.coroutines.delay
/**
* @author ShirkNeko

View File

@@ -9,89 +9,49 @@ import android.os.Build
import android.widget.Toast
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.*
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.animation.expandVertically
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.animation.shrinkVertically
import androidx.compose.animation.slideInVertically
import androidx.compose.animation.slideOutVertically
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.foundation.*
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.verticalScroll
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.ArrowBack
import androidx.compose.material.icons.automirrored.filled.NavigateNext
import androidx.compose.material.icons.filled.*
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Button
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Slider
import androidx.compose.material3.SliderDefaults
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.material3.rememberTopAppBarState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableFloatStateOf
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.core.content.edit
import com.ramcosta.composedestinations.annotation.Destination
import com.ramcosta.composedestinations.annotation.RootGraph
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
import com.sukisu.ultra.Natives
import com.sukisu.ultra.R
import com.sukisu.ultra.ksuApp
import com.sukisu.ultra.ui.component.ImageEditorDialog
import com.sukisu.ultra.ui.component.KsuIsValid
import com.sukisu.ultra.ui.theme.CardConfig.cardElevation
import com.sukisu.ultra.ui.theme.*
import com.sukisu.ultra.ui.theme.CardConfig.cardElevation
import com.sukisu.ultra.ui.util.*
import com.topjohnwu.superuser.Shell
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import java.util.Locale
import java.util.*
import kotlin.math.roundToInt
import androidx.compose.foundation.border
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.icons.automirrored.filled.ArrowBack
import androidx.compose.material.icons.automirrored.filled.NavigateNext
import androidx.compose.material3.Card
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.IconButton
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Switch
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.vector.ImageVector
import com.sukisu.ultra.ui.theme.getCardColors
import com.sukisu.ultra.ui.theme.getCardElevation
import androidx.compose.material3.RadioButton
import androidx.compose.material3.TextButton
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.unit.sp
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
import com.sukisu.ultra.ksuApp
/**
* @author ShirkNeko
@@ -110,7 +70,7 @@ fun saveCardConfig(context: Context) {
/**
* 更多设置屏幕
*/
@SuppressLint("LocalContextConfigurationRead", "ObsoleteSdkInt")
@SuppressLint("LocalContextConfigurationRead", "LocalContextResourcesRead", "ObsoleteSdkInt")
@OptIn(ExperimentalMaterial3Api::class)
@Destination<RootGraph>
@Composable