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

View File

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

View File

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

View File

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

View File

@@ -1,12 +1,14 @@
package com.sukisu.ultra.ui.component package com.sukisu.ultra.ui.component
import android.annotation.SuppressLint import android.annotation.SuppressLint
import androidx.compose.animation.core.*
import androidx.compose.animation.* 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.layout.Box
import androidx.compose.foundation.lazy.LazyListState import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha import androidx.compose.ui.draw.alpha
import androidx.compose.ui.draw.clip 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.Check
import androidx.compose.material.icons.filled.Close import androidx.compose.material.icons.filled.Close
import androidx.compose.material.icons.filled.Fullscreen 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.runtime.*
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.clip
import androidx.compose.ui.geometry.Size
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.graphicsLayer import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.input.pointer.pointerInput import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.layout.onSizeChanged
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp 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.BackgroundTransformation
import com.sukisu.ultra.ui.util.saveTransformedBackground import com.sukisu.ultra.ui.util.saveTransformedBackground
import kotlinx.coroutines.launch 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 import kotlin.math.max
@Composable @Composable

View File

@@ -4,35 +4,15 @@ import android.util.Log
import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.fadeIn import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut import androidx.compose.animation.fadeOut
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.*
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.text.KeyboardActions import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.outlined.ArrowBack import androidx.compose.material.icons.automirrored.outlined.ArrowBack
import androidx.compose.material.icons.filled.Close import androidx.compose.material.icons.filled.Close
import androidx.compose.material.icons.filled.Search import androidx.compose.material.icons.filled.Search
import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.*
import androidx.compose.material3.Icon import androidx.compose.runtime.*
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.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.FocusRequester 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.LocalIndication
import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.selection.toggleable import androidx.compose.foundation.selection.toggleable
import androidx.compose.material3.Icon import androidx.compose.material3.*
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.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier 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.horizontalScroll
import androidx.compose.foundation.layout.* import androidx.compose.foundation.layout.*
import androidx.compose.foundation.rememberScrollState 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.material3.*
import androidx.compose.runtime.* import androidx.compose.runtime.*
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier 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.res.stringResource
import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import com.sukisu.ultra.R 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.content.pm.PackageManager
import android.graphics.drawable.Drawable import android.graphics.drawable.Drawable
import android.util.Log import android.util.Log
import androidx.compose.foundation.Image import androidx.compose.foundation.*
import androidx.compose.foundation.background import androidx.compose.foundation.layout.*
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.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.CheckCircle import androidx.compose.material.icons.filled.*
import androidx.compose.material.icons.filled.Delete import androidx.compose.material3.*
import androidx.compose.material.icons.filled.Edit import androidx.compose.runtime.*
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.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.clip

View File

@@ -1,37 +1,13 @@
package com.sukisu.ultra.ui.component package com.sukisu.ultra.ui.component
import android.annotation.SuppressLint import android.annotation.SuppressLint
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.*
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.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Add import androidx.compose.material.icons.filled.*
import androidx.compose.material.icons.filled.Apps import androidx.compose.material3.*
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.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.remember import androidx.compose.runtime.remember

View File

@@ -1,7 +1,9 @@
package com.sukisu.ultra.ui.component package com.sukisu.ultra.ui.component
import androidx.compose.animation.* 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.foundation.layout.*
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.* 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.Color
import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.res.stringResource 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 androidx.compose.ui.unit.dp
import com.sukisu.ultra.R 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.foundation.layout.Column
import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.*
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview

View File

@@ -1,28 +1,11 @@
package com.sukisu.ultra.ui.component.profile package com.sukisu.ultra.ui.component.profile
import androidx.compose.foundation.clickable import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.*
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.text.KeyboardActions import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material3.AssistChip import androidx.compose.material3.*
import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.runtime.*
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.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalSoftwareKeyboardController import androidx.compose.ui.platform.LocalSoftwareKeyboardController
import androidx.compose.ui.res.stringResource 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.Header
import com.maxkeppeker.sheets.core.models.base.rememberUseCaseState import com.maxkeppeker.sheets.core.models.base.rememberUseCaseState
import com.maxkeppeler.sheets.input.InputDialog import com.maxkeppeler.sheets.input.InputDialog
import com.maxkeppeler.sheets.input.models.InputHeader import com.maxkeppeler.sheets.input.models.*
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.list.ListDialog import com.maxkeppeler.sheets.list.ListDialog
import com.maxkeppeler.sheets.list.models.ListOption import com.maxkeppeler.sheets.list.models.ListOption
import com.maxkeppeler.sheets.list.models.ListSelection 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.ArrowDropDown
import androidx.compose.material.icons.filled.ArrowDropUp import androidx.compose.material.icons.filled.ArrowDropUp
import androidx.compose.material.icons.filled.Create import androidx.compose.material.icons.filled.Create
import androidx.compose.material3.DropdownMenuItem import androidx.compose.material3.*
import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.runtime.*
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.runtime.saveable.rememberSaveable import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import com.sukisu.ultra.Natives import com.sukisu.ultra.Natives

View File

@@ -2,26 +2,9 @@ package com.sukisu.ultra.ui.screen
import android.annotation.SuppressLint import android.annotation.SuppressLint
import androidx.annotation.StringRes import androidx.annotation.StringRes
import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.*
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.foundation.gestures.detectTapGestures import androidx.compose.foundation.gestures.detectTapGestures
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.*
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.rememberScrollState import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons 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.AccountCircle
import androidx.compose.material.icons.filled.Android import androidx.compose.material.icons.filled.Android
import androidx.compose.material.icons.filled.Security import androidx.compose.material.icons.filled.Security
import androidx.compose.material3.DropdownMenu import androidx.compose.material3.*
import androidx.compose.material3.DropdownMenuItem import androidx.compose.runtime.*
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.runtime.saveable.rememberSaveable import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha 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.CardConfig
import com.sukisu.ultra.ui.theme.getCardColors import com.sukisu.ultra.ui.theme.getCardColors
import com.sukisu.ultra.ui.theme.getCardElevation import com.sukisu.ultra.ui.theme.getCardElevation
import com.sukisu.ultra.ui.util.LocalSnackbarHost import com.sukisu.ultra.ui.util.*
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.viewmodel.SuperUserViewModel import com.sukisu.ultra.ui.viewmodel.SuperUserViewModel
import com.sukisu.ultra.ui.viewmodel.getTemplateInfoById import com.sukisu.ultra.ui.viewmodel.getTemplateInfoById
import kotlinx.coroutines.launch 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); Default(R.string.profile_default), Template(R.string.profile_template), Custom(R.string.profile_custom);
val text: String val text: String

View File

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

View File

@@ -2,32 +2,15 @@ package com.sukisu.ultra.ui.screen
import android.os.Environment import android.os.Environment
import androidx.activity.compose.BackHandler import androidx.activity.compose.BackHandler
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.*
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll 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.Icons
import androidx.compose.material.icons.filled.Close import androidx.compose.material.icons.filled.Close
import androidx.compose.material.icons.filled.Save import androidx.compose.material.icons.filled.Save
import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.*
import androidx.compose.material3.ExtendedFloatingActionButton import androidx.compose.runtime.*
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.runtime.saveable.rememberSaveable import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.input.key.Key import androidx.compose.ui.input.key.Key
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.Destination
import com.ramcosta.composedestinations.annotation.RootGraph import com.ramcosta.composedestinations.annotation.RootGraph
import com.ramcosta.composedestinations.navigation.DestinationsNavigator 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.R
import com.sukisu.ultra.ui.component.KeyEventBlocker import com.sukisu.ultra.ui.component.KeyEventBlocker
import com.sukisu.ultra.ui.util.LocalSnackbarHost import com.sukisu.ultra.ui.util.LocalSnackbarHost
import com.sukisu.ultra.ui.util.runModuleAction 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.io.File
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.Date import java.util.*
import java.util.Locale
@Composable @Composable
@Destination<RootGraph> @Destination<RootGraph>

View File

@@ -4,12 +4,8 @@ import android.net.Uri
import android.os.Environment import android.os.Environment
import android.os.Parcelable import android.os.Parcelable
import androidx.activity.compose.BackHandler import androidx.activity.compose.BackHandler
import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.*
import androidx.compose.animation.core.animateFloatAsState 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.background
import androidx.compose.foundation.layout.* import androidx.compose.foundation.layout.*
import androidx.compose.foundation.rememberScrollState 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.text.font.FontWeight
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel
import com.ramcosta.composedestinations.annotation.Destination import com.ramcosta.composedestinations.annotation.Destination
import com.ramcosta.composedestinations.annotation.RootGraph import com.ramcosta.composedestinations.annotation.RootGraph
import com.ramcosta.composedestinations.generated.destinations.FlashScreenDestination import com.ramcosta.composedestinations.generated.destinations.FlashScreenDestination
import com.ramcosta.composedestinations.generated.destinations.ModuleScreenDestination import com.ramcosta.composedestinations.generated.destinations.ModuleScreenDestination
import com.ramcosta.composedestinations.navigation.DestinationsNavigator import com.ramcosta.composedestinations.navigation.DestinationsNavigator
import com.ramcosta.composedestinations.navigation.EmptyDestinationsNavigator 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.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import kotlinx.parcelize.Parcelize 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.io.File
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.* import java.util.*

View File

@@ -6,29 +6,11 @@ import android.os.Build
import android.os.PowerManager import android.os.PowerManager
import android.system.Os import android.system.Os
import androidx.annotation.StringRes 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.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.clickable
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.*
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.rememberScrollState import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.verticalScroll 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.icons.outlined.Warning
import androidx.compose.material.pullrefresh.pullRefresh import androidx.compose.material.pullrefresh.pullRefresh
import androidx.compose.material.pullrefresh.rememberPullRefreshState import androidx.compose.material.pullrefresh.rememberPullRefreshState
import androidx.compose.material3.CardDefaults import androidx.compose.material3.*
import androidx.compose.material3.DropdownMenu import androidx.compose.runtime.*
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.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color 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.CardConfig.cardElevation
import com.sukisu.ultra.ui.theme.getCardColors import com.sukisu.ultra.ui.theme.getCardColors
import com.sukisu.ultra.ui.theme.getCardElevation 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.checkNewVersion
import com.sukisu.ultra.ui.util.getSuSFS
import com.sukisu.ultra.ui.util.module.LatestVersionInfo import com.sukisu.ultra.ui.util.module.LatestVersionInfo
import com.sukisu.ultra.ui.util.reboot 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 com.sukisu.ultra.ui.viewmodel.HomeViewModel
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@@ -366,7 +327,7 @@ private fun StatusCard(
systemStatus.ksuVersion != null -> { systemStatus.ksuVersion != null -> {
val workingModeText = when { 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) 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.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.result.contract.ActivityResultContracts
import androidx.annotation.StringRes import androidx.annotation.StringRes
import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.*
import androidx.compose.animation.expandVertically
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.animation.shrinkVertically
import androidx.compose.foundation.LocalIndication import androidx.compose.foundation.LocalIndication
import androidx.compose.foundation.clickable import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.*
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.rememberScrollState import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.selection.toggleable import androidx.compose.foundation.selection.toggleable
import androidx.compose.foundation.verticalScroll 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.automirrored.filled.ArrowBack
import androidx.compose.material.icons.filled.AutoFixHigh import androidx.compose.material.icons.filled.AutoFixHigh
import androidx.compose.material.icons.filled.FileUpload import androidx.compose.material.icons.filled.FileUpload
import androidx.compose.material3.AlertDialog import androidx.compose.material3.*
import androidx.compose.material3.Button import androidx.compose.runtime.*
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.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip 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.DestinationsNavigator
import com.ramcosta.composedestinations.navigation.EmptyDestinationsNavigator import com.ramcosta.composedestinations.navigation.EmptyDestinationsNavigator
import com.sukisu.ultra.R import com.sukisu.ultra.R
import com.sukisu.ultra.getKernelVersion
import com.sukisu.ultra.ui.component.DialogHandle import com.sukisu.ultra.ui.component.DialogHandle
import com.sukisu.ultra.ui.component.SlotSelectionDialog import com.sukisu.ultra.ui.component.SlotSelectionDialog
import com.sukisu.ultra.ui.component.rememberConfirmDialog import com.sukisu.ultra.ui.component.rememberConfirmDialog
import com.sukisu.ultra.ui.component.rememberCustomDialog 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.cardAlpha
import com.sukisu.ultra.ui.theme.CardConfig.cardElevation import com.sukisu.ultra.ui.theme.CardConfig.cardElevation
import com.sukisu.ultra.ui.theme.getCardColors 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.theme.getCardElevation
import com.sukisu.ultra.ui.util.*
/** /**
* @author ShirkNeko * @author ShirkNeko
@@ -413,9 +374,9 @@ private fun SelectInstallMethod(
else -> null else -> null
} }
option?.let { it -> option?.let { opt ->
selectedOption = it selectedOption = opt
onSelected(it) onSelected(opt)
} }
} }
} }

View File

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

View File

@@ -11,9 +11,12 @@ import android.util.Log
import android.widget.Toast import android.widget.Toast
import androidx.activity.compose.rememberLauncherForActivityResult import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.animation.* import androidx.compose.animation.core.Spring
import androidx.compose.animation.core.* import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.foundation.* 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.MutableInteractionSource
import androidx.compose.foundation.interaction.collectIsPressedAsState import androidx.compose.foundation.interaction.collectIsPressedAsState
import androidx.compose.foundation.layout.* 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.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.outlined.* import androidx.compose.material.icons.automirrored.outlined.Wysiwyg
import androidx.compose.material.icons.filled.* import androidx.compose.material.icons.filled.MoreVert
import androidx.compose.material.icons.filled.Verified
import androidx.compose.material.icons.outlined.* import androidx.compose.material.icons.outlined.*
import androidx.compose.material3.* import androidx.compose.material3.*
import androidx.compose.material3.pulltorefresh.PullToRefreshBox 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.graphics.vector.ImageVector
import androidx.compose.ui.hapticfeedback.HapticFeedbackType import androidx.compose.ui.hapticfeedback.HapticFeedbackType
import androidx.compose.ui.input.nestedscroll.nestedScroll 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.painterResource
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.semantics.Role 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.tooling.preview.Preview
import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.Dp
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 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.Destination
import com.ramcosta.composedestinations.annotation.RootGraph import com.ramcosta.composedestinations.annotation.RootGraph
import com.ramcosta.composedestinations.generated.destinations.ExecuteModuleActionScreenDestination import com.ramcosta.composedestinations.generated.destinations.ExecuteModuleActionScreenDestination
import com.ramcosta.composedestinations.generated.destinations.FlashScreenDestination import com.ramcosta.composedestinations.generated.destinations.FlashScreenDestination
import com.ramcosta.composedestinations.navigation.DestinationsNavigator import com.ramcosta.composedestinations.navigation.DestinationsNavigator
import com.ramcosta.composedestinations.navigation.EmptyDestinationsNavigator 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.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext 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 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 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( data class ModuleBottomSheetMenuItem(

View File

@@ -6,11 +6,7 @@ import android.net.Uri
import android.widget.Toast import android.widget.Toast
import androidx.activity.compose.rememberLauncherForActivityResult import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.*
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.background
import androidx.compose.foundation.clickable import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.* 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.automirrored.filled.Undo
import androidx.compose.material.icons.filled.* import androidx.compose.material.icons.filled.*
import androidx.compose.material3.* import androidx.compose.material3.*
import androidx.compose.runtime.Composable import androidx.compose.runtime.*
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip 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.FlashScreenDestination
import com.ramcosta.composedestinations.generated.destinations.MoreSettingsScreenDestination import com.ramcosta.composedestinations.generated.destinations.MoreSettingsScreenDestination
import com.ramcosta.composedestinations.navigation.DestinationsNavigator 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.BuildConfig
import com.sukisu.ultra.Natives import com.sukisu.ultra.Natives
import com.sukisu.ultra.R import com.sukisu.ultra.R
import com.sukisu.ultra.ui.component.* 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.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.LocalSnackbarHost
import com.sukisu.ultra.ui.util.getBugreportFile 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.LocalDateTime
import java.time.format.DateTimeFormatter import java.time.format.DateTimeFormatter
import com.sukisu.ultra.ui.component.KsuIsValid
/** /**
* @author ShirkNeko * @author ShirkNeko

View File

@@ -3,58 +3,15 @@ package com.sukisu.ultra.ui.screen
import android.annotation.SuppressLint import android.annotation.SuppressLint
import androidx.activity.compose.rememberLauncherForActivityResult import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.*
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.rememberScrollState import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.verticalScroll import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.ArrowBack import androidx.compose.material.icons.automirrored.filled.ArrowBack
import androidx.compose.material.icons.filled.* import androidx.compose.material.icons.filled.*
import androidx.compose.material3.AlertDialog import androidx.compose.material3.*
import androidx.compose.material3.Button import androidx.compose.runtime.*
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.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color 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.annotation.RootGraph
import com.ramcosta.composedestinations.navigation.DestinationsNavigator import com.ramcosta.composedestinations.navigation.DestinationsNavigator
import com.sukisu.ultra.R import com.sukisu.ultra.R
import com.sukisu.ultra.ui.component.AddAppPathDialog import com.sukisu.ultra.ui.component.*
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.theme.CardConfig import com.sukisu.ultra.ui.theme.CardConfig
import com.sukisu.ultra.ui.util.SuSFSManager import com.sukisu.ultra.ui.util.SuSFSManager
import com.sukisu.ultra.ui.util.SuSFSManager.isSusVersion158 import com.sukisu.ultra.ui.util.SuSFSManager.isSusVersion158
@@ -1171,7 +1117,7 @@ fun SuSFSConfigScreen(
containerColor = MaterialTheme.colorScheme.surface, containerColor = MaterialTheme.colorScheme.surface,
contentColor = MaterialTheme.colorScheme.onSurface contentColor = MaterialTheme.colorScheme.onSurface
) { ) {
allTabs.forEachIndexed { index, tab -> allTabs.forEach { tab ->
Tab( Tab(
selected = selectedTab == tab, selected = selectedTab == tab,
onClick = { selectedTab = tab }, onClick = { selectedTab = tab },

View File

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

View File

@@ -4,16 +4,7 @@ import android.content.ClipData
import android.content.ClipboardManager import android.content.ClipboardManager
import android.widget.Toast import android.widget.Toast
import androidx.compose.foundation.clickable import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.*
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.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.items
import androidx.compose.material.ExperimentalMaterialApi 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.Add
import androidx.compose.material.icons.filled.ImportExport import androidx.compose.material.icons.filled.ImportExport
import androidx.compose.material.icons.filled.Sync import androidx.compose.material.icons.filled.Sync
import androidx.compose.material3.DropdownMenu import androidx.compose.material3.*
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.pulltorefresh.PullToRefreshBox import androidx.compose.material3.pulltorefresh.PullToRefreshBox
import androidx.compose.material3.rememberTopAppBarState import androidx.compose.runtime.*
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.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.LocalContext 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.navigation.DestinationsNavigator
import com.ramcosta.composedestinations.result.ResultRecipient import com.ramcosta.composedestinations.result.ResultRecipient
import com.ramcosta.composedestinations.result.getOr import com.ramcosta.composedestinations.result.getOr
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import com.sukisu.ultra.R import com.sukisu.ultra.R
import com.sukisu.ultra.ui.theme.CardConfig import com.sukisu.ultra.ui.theme.CardConfig
import com.sukisu.ultra.ui.viewmodel.TemplateViewModel import com.sukisu.ultra.ui.viewmodel.TemplateViewModel
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
/** /**
* @author weishu * @author weishu

View File

@@ -2,13 +2,7 @@ package com.sukisu.ultra.ui.screen
import android.widget.Toast import android.widget.Toast
import androidx.activity.compose.BackHandler import androidx.activity.compose.BackHandler
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.*
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.rememberScrollState import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.text.KeyboardActions import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions 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.automirrored.filled.ArrowBack
import androidx.compose.material.icons.filled.DeleteForever import androidx.compose.material.icons.filled.DeleteForever
import androidx.compose.material.icons.filled.Save import androidx.compose.material.icons.filled.Save
import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.*
import androidx.compose.material3.Icon import androidx.compose.runtime.*
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.runtime.saveable.rememberSaveable import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.input.nestedscroll.nestedScroll 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.res.stringResource
import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.text.input.KeyboardType
import androidx.lifecycle.compose.dropUnlessResumed
import com.ramcosta.composedestinations.annotation.Destination import com.ramcosta.composedestinations.annotation.Destination
import com.ramcosta.composedestinations.annotation.RootGraph import com.ramcosta.composedestinations.annotation.RootGraph
import com.ramcosta.composedestinations.result.ResultBackNavigator 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.util.setAppProfileTemplate
import com.sukisu.ultra.ui.viewmodel.TemplateViewModel import com.sukisu.ultra.ui.viewmodel.TemplateViewModel
import com.sukisu.ultra.ui.viewmodel.toJSON import com.sukisu.ultra.ui.viewmodel.toJSON
import androidx.lifecycle.compose.dropUnlessResumed
/** /**
* @author weishu * @author weishu

View File

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

View File

@@ -5,51 +5,40 @@ import android.content.Context
import android.net.Uri import android.net.Uri
import android.os.Build import android.os.Build
import android.util.Log import android.util.Log
import androidx.activity.ComponentActivity
import androidx.activity.SystemBarStyle
import androidx.activity.enableEdgeToEdge
import androidx.annotation.RequiresApi import androidx.annotation.RequiresApi
import androidx.compose.animation.core.animateFloat import androidx.compose.animation.core.animateFloat
import androidx.compose.animation.core.spring import androidx.compose.animation.core.spring
import androidx.compose.animation.core.updateTransition import androidx.compose.animation.core.updateTransition
import androidx.compose.foundation.background
import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.*
import androidx.compose.material3.darkColorScheme import androidx.compose.runtime.*
import androidx.compose.material3.dynamicDarkColorScheme import androidx.compose.runtime.saveable.rememberSaveable
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.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha import androidx.compose.ui.draw.alpha
import androidx.compose.ui.draw.paint import androidx.compose.ui.draw.paint
import androidx.compose.ui.graphics.Brush import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color 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.layout.ContentScale
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.dp
import androidx.compose.ui.zIndex import androidx.compose.ui.zIndex
import androidx.core.content.edit
import androidx.core.net.toUri
import coil.compose.AsyncImagePainter import coil.compose.AsyncImagePainter
import coil.compose.rememberAsyncImagePainter import coil.compose.rememberAsyncImagePainter
import androidx.compose.foundation.background import com.sukisu.ultra.ui.util.BackgroundTransformation
import androidx.compose.runtime.saveable.rememberSaveable import com.sukisu.ultra.ui.util.saveTransformedBackground
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.unit.dp
import java.io.File import java.io.File
import java.io.FileOutputStream import java.io.FileOutputStream
import java.io.InputStream 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 { val bgImagePainter = backgroundUri.value?.let {
rememberAsyncImagePainter( rememberAsyncImagePainter(
model = it, model = it,
onError = { onError = { err ->
Log.e("ThemeSystem", "背景图加载失败: ${it.result.throwable.message}") Log.e("ThemeSystem", "背景图加载失败: ${err.result.throwable.message}")
ThemeConfig.customBackgroundUri = null ThemeConfig.customBackgroundUri = null
context.saveCustomBackground(null) context.saveCustomBackground(null)
}, },
@@ -222,7 +211,7 @@ fun KernelSUTheme(
) )
// 自定义背景层 // 自定义背景层
backgroundUri.value?.let { uri -> backgroundUri.value?.let {
Box( Box(
modifier = Modifier modifier = Modifier
.fillMaxSize() .fillMaxSize()

View File

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

View File

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

View File

@@ -32,7 +32,8 @@ import java.util.Locale;
* <p> * <p>
* Currently this file is aligned to zh.txt in ICU 4.6 * 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"; private static final String TAG = "HanziToPinyin";
// Turn on this flag when we want to check internal data structure. // 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. * Each unihans is the first one within same pinyin when collator is zh_CN.
*/ */
public static final char[] UNIHANS = { 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. * Pinyin array.
@@ -334,18 +335,17 @@ public class HanziToPinyin {
{90, 85, 0, 0, 0, 0}, {90, 85, 65, 78, 0, 0}, {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, 73, 0, 0, 0}, {90, 85, 78, 0, 0, 0},
{90, 85, 79, 0, 0, 0}, {0, 0, 0, 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 * First and last Chinese character with known Pinyin according to zh collation
*/ */
private static final String FIRST_PINYIN_UNIHAN = "\u963F"; private static final String FIRST_PINYIN_UNIHAN = "";
private static final String LAST_PINYIN_UNIHAN = "\u9FFF"; private static final String LAST_PINYIN_UNIHAN = "鿿";
private static final Collator COLLATOR = Collator.getInstance(Locale.CHINA); private static final Collator COLLATOR = Collator.getInstance(Locale.CHINA);
private static HanziToPinyin sInstance; private static HanziToPinyin sInstance;
private final boolean mHasChinaCollator;
public static class Token { public static class Token {
/** /**
@@ -381,10 +381,6 @@ public class HanziToPinyin {
public String target; public String target;
} }
protected HanziToPinyin(boolean hasChinaCollator) {
mHasChinaCollator = hasChinaCollator;
}
public static HanziToPinyin getInstance() { public static HanziToPinyin getInstance() {
synchronized (HanziToPinyin.class) { synchronized (HanziToPinyin.class) {
if (sInstance != null) { if (sInstance != null) {
@@ -402,8 +398,8 @@ public class HanziToPinyin {
return sInstance; return sInstance;
} }
} }
if (sInstance == null){//这个判断是用于处理国产ROM的兼容性问题 if (sInstance == null) {//这个判断是用于处理国产ROM的兼容性问题
if (Locale.CHINA.equals(Locale.getDefault())){ if (Locale.CHINA.equals(Locale.getDefault())) {
sInstance = new HanziToPinyin(true); sInstance = new HanziToPinyin(true);
return sInstance; return sInstance;
} }

View File

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

View File

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

View File

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

View File

@@ -9,17 +9,16 @@ import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.material3.* import androidx.compose.material3.*
import androidx.compose.runtime.* import androidx.compose.runtime.*
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import com.sukisu.ultra.R
import kotlinx.coroutines.CompletableDeferred import kotlinx.coroutines.CompletableDeferred
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import com.sukisu.ultra.R
import java.io.BufferedReader import java.io.BufferedReader
import java.io.IOException import java.io.IOException
import java.io.InputStreamReader import java.io.InputStreamReader
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.Date import java.util.*
import java.util.Locale
object ModuleModify { object ModuleModify {
@Composable @Composable
@@ -442,8 +441,4 @@ object ModuleModify {
type = "application/octet-stream" 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.Context
import android.content.Intent import android.content.Intent
import android.net.Uri import android.net.Uri
import android.util.Log
import com.sukisu.ultra.R
import java.io.BufferedReader import java.io.BufferedReader
import java.io.IOException
import java.io.InputStreamReader import java.io.InputStreamReader
import java.nio.charset.StandardCharsets import java.nio.charset.StandardCharsets
import java.util.zip.ZipInputStream import java.util.zip.ZipInputStream
import com.sukisu.ultra.R
import android.util.Log
import java.io.IOException
object ModuleUtils { object ModuleUtils {
private const val TAG = "ModuleUtils" private const val TAG = "ModuleUtils"
@@ -108,10 +108,7 @@ object ModuleUtils {
return try { return try {
val inputStream = context.contentResolver.openInputStream(uri) val inputStream = context.contentResolver.openInputStream(uri) ?: return null
if (inputStream == null) {
return null
}
val zipInputStream = ZipInputStream(inputStream) val zipInputStream = ZipInputStream(inputStream)
var entry = zipInputStream.nextEntry var entry = zipInputStream.nextEntry

View File

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

View File

@@ -1,31 +1,19 @@
package com.sukisu.ultra.ui.util package com.sukisu.ultra.ui.util
import android.content.Context import android.content.Context
import com.topjohnwu.superuser.Shell
import com.sukisu.ultra.R import com.sukisu.ultra.R
import com.topjohnwu.superuser.io.SuFile
fun getSELinuxStatus(context: Context): String { fun getSELinuxStatus(context: Context) = SuFile("/sys/fs/selinux/enforce").run {
val shell = Shell.Builder.create().build("sh") when {
val list = ArrayList<String>() !exists() -> context.getString(R.string.selinux_status_disabled)
!isFile -> context.getString(R.string.selinux_status_unknown)
val result = shell.use { !canRead() -> context.getString(R.string.selinux_status_enforcing)
it.newJob().add("getenforce").to(list, list).exec() else -> when (runCatching { newInputStream() }.getOrNull()?.bufferedReader()
} ?.use { it.runCatching { readLine() }.getOrNull()?.trim()?.toIntOrNull() }) {
1 -> context.getString(R.string.selinux_status_enforcing)
val output = list.joinToString("\n").trim() 0 -> context.getString(R.string.selinux_status_permissive)
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)
else -> context.getString(R.string.selinux_status_unknown) 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.content.pm.PackageInfo
import android.util.Log import android.util.Log
import android.widget.Toast import android.widget.Toast
import androidx.core.content.edit
import com.dergoogler.mmrl.platform.Platform.Companion.context import com.dergoogler.mmrl.platform.Platform.Companion.context
import com.sukisu.ultra.Natives import com.sukisu.ultra.Natives
import com.sukisu.ultra.R import com.sukisu.ultra.R
import com.sukisu.ultra.ui.viewmodel.SuperUserViewModel
import com.topjohnwu.superuser.Shell import com.topjohnwu.superuser.Shell
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.*
import kotlinx.coroutines.launch import org.json.JSONObject
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withContext
import java.io.File import java.io.File
import java.io.FileOutputStream import java.io.FileOutputStream
import java.io.IOException 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.text.SimpleDateFormat
import java.util.* import java.util.*
@@ -430,12 +425,10 @@ object SuSFSManager {
async(Dispatchers.IO) { async(Dispatchers.IO) {
val dataPath = "$MEDIA_DATA_PATH/${appInfo.packageName}" val dataPath = "$MEDIA_DATA_PATH/${appInfo.packageName}"
val exists = try { val exists = try {
val shell = getRootShell()
val outputList = mutableListOf<String>() val outputList = mutableListOf<String>()
val errorList = mutableListOf<String>() val errorList = mutableListOf<String>()
val result = shell.newJob() val result = Shell.cmd("[ -d \"$dataPath\" ] && echo 'exists' || echo 'not_exists'")
.add("[ -d \"$dataPath\" ] && echo 'exists' || echo 'not_exists'")
.to(outputList, errorList) .to(outputList, errorList)
.exec() .exec()

View File

@@ -8,6 +8,7 @@ import android.util.Log
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.core.content.edit
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import com.dergoogler.mmrl.platform.Platform.Companion.context 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.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import androidx.core.content.edit
class HomeViewModel : ViewModel() { class HomeViewModel : ViewModel() {
companion object { companion object {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -16,16 +16,14 @@ import androidx.core.view.WindowInsetsCompat
import androidx.core.view.updateLayoutParams import androidx.core.view.updateLayoutParams
import androidx.webkit.WebViewAssetLoader import androidx.webkit.WebViewAssetLoader
import com.dergoogler.mmrl.platform.model.ModId import com.dergoogler.mmrl.platform.model.ModId
import com.topjohnwu.superuser.Shell import com.dergoogler.mmrl.webui.interfaces.WXOptions
import com.sukisu.ultra.ui.util.createRootShell import com.sukisu.ultra.ui.util.createRootShell
import java.io.File import java.io.File
import com.dergoogler.mmrl.webui.interfaces.WXOptions
@SuppressLint("SetJavaScriptEnabled") @SuppressLint("SetJavaScriptEnabled")
class WebUIActivity : ComponentActivity() { class WebUIActivity : ComponentActivity() {
private lateinit var webviewInterface: WebViewInterface private val rootShell by lazy { createRootShell(true) }
private var webView = null as WebView?
private var rootShell: Shell? = null
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
@@ -37,8 +35,8 @@ class WebUIActivity : ComponentActivity() {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
val moduleId = intent.getStringExtra("id")!! val moduleId = intent.getStringExtra("id") ?: finishAndRemoveTask().let { return }
val name = intent.getStringExtra("name")!! val name = intent.getStringExtra("name") ?: finishAndRemoveTask().let { return }
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
@Suppress("DEPRECATION") @Suppress("DEPRECATION")
setTaskDescription(ActivityManager.TaskDescription("SukiSU-Ultra - $name")) setTaskDescription(ActivityManager.TaskDescription("SukiSU-Ultra - $name"))
@@ -53,7 +51,6 @@ class WebUIActivity : ComponentActivity() {
val moduleDir = "/data/adb/modules/${moduleId}" val moduleDir = "/data/adb/modules/${moduleId}"
val webRoot = File("${moduleDir}/webroot") val webRoot = File("${moduleDir}/webroot")
val rootShell = createRootShell(true).also { this.rootShell = it }
val webViewAssetLoader = WebViewAssetLoader.Builder() val webViewAssetLoader = WebViewAssetLoader.Builder()
.setDomain("mui.kernelsu.org") .setDomain("mui.kernelsu.org")
.addPathHandler( .addPathHandler(
@@ -72,6 +69,8 @@ class WebUIActivity : ComponentActivity() {
} }
val webView = WebView(this).apply { val webView = WebView(this).apply {
webView = this
ViewCompat.setOnApplyWindowInsetsListener(this) { view, insets -> ViewCompat.setOnApplyWindowInsetsListener(this) { view, insets ->
val inset = insets.getInsets(WindowInsetsCompat.Type.systemBars()) val inset = insets.getInsets(WindowInsetsCompat.Type.systemBars())
view.updateLayoutParams<MarginLayoutParams> { view.updateLayoutParams<MarginLayoutParams> {
@@ -85,8 +84,7 @@ class WebUIActivity : ComponentActivity() {
settings.javaScriptEnabled = true settings.javaScriptEnabled = true
settings.domStorageEnabled = true settings.domStorageEnabled = true
settings.allowFileAccess = false settings.allowFileAccess = false
webviewInterface = WebViewInterface(WXOptions(this@WebUIActivity, this, ModId(moduleId))) addJavascriptInterface(WebViewInterface(WXOptions(this@WebUIActivity, this, ModId(moduleId))), "ksu")
addJavascriptInterface(webviewInterface, "ksu")
setWebViewClient(webViewClient) setWebViewClient(webViewClient)
loadUrl("https://mui.kernelsu.org/index.html") loadUrl("https://mui.kernelsu.org/index.html")
} }
@@ -95,7 +93,13 @@ class WebUIActivity : ComponentActivity() {
} }
override fun onDestroy() { override fun onDestroy() {
rootShell.runCatching { close() }
webView?.apply {
stopLoading()
removeAllViews()
destroy()
webView = null
}
super.onDestroy() super.onDestroy()
runCatching { rootShell?.close() }
} }
} }

View File

@@ -8,11 +8,7 @@ import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge import androidx.activity.enableEdgeToEdge
import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.*
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import com.dergoogler.mmrl.platform.Platform import com.dergoogler.mmrl.platform.Platform
import com.dergoogler.mmrl.platform.model.ModId import com.dergoogler.mmrl.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.WXInterface
import com.dergoogler.mmrl.webui.interfaces.WXOptions import com.dergoogler.mmrl.webui.interfaces.WXOptions
import com.dergoogler.mmrl.webui.model.JavaScriptInterface import com.dergoogler.mmrl.webui.model.JavaScriptInterface
import com.sukisu.ultra.ui.util.*
import com.topjohnwu.superuser.CallbackList import com.topjohnwu.superuser.CallbackList
import com.topjohnwu.superuser.ShellUtils import com.topjohnwu.superuser.ShellUtils
import com.topjohnwu.superuser.internal.UiThreadHandler 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.JSONArray
import org.json.JSONObject import org.json.JSONObject
import com.sukisu.ultra.ui.util.*
import java.io.File import java.io.File
import java.util.concurrent.CompletableFuture import java.util.concurrent.CompletableFuture
@Suppress("unused")
class WebViewInterface( class WebViewInterface(
wxOptions: WXOptions, wxOptions: WXOptions,
) : WXInterface(wxOptions) { ) : WXInterface(wxOptions) {
@@ -68,56 +66,56 @@ class WebViewInterface(
options: String?, options: String?,
callbackFunc: String callbackFunc: String
) { ) {
val finalCommand = StringBuilder() val finalCommand = buildString {
processOptions(finalCommand, options) processOptions(this, options)
finalCommand.append(cmd) append(cmd)
}
val result = withNewRootShell(true) { 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 stdout = result.out.joinToString(separator = "\n")
val stderr = result.err.joinToString(separator = "\n") val stderr = result.err.joinToString(separator = "\n")
val jsCode = val jsCode =
"javascript: (function() { try { ${callbackFunc}(${result.code}, ${ "(function() { try { ${callbackFunc}(${result.code}, ${
JSONObject.quote( JSONObject.quote(
stdout stdout
) )
}, ${JSONObject.quote(stderr)}); } catch(e) { console.error(e); } })();" }, ${JSONObject.quote(stderr)}); } catch(e) { console.error(e); } })();"
webView.post { webView.post {
webView.loadUrl(jsCode) webView.evaluateJavascript(jsCode, null)
} }
} }
@JavascriptInterface @JavascriptInterface
fun spawn(command: String, args: String, options: String?, callbackFunc: String) { fun spawn(command: String, args: String, options: String?, callbackFunc: String) {
val finalCommand = StringBuilder() val finalCommand = buildString {
processOptions(this, options)
processOptions(finalCommand, options) if (!TextUtils.isEmpty(args)) {
append(command).append(" ")
if (!TextUtils.isEmpty(args)) { JSONArray(args).let { argsArray ->
finalCommand.append(command).append(" ") for (i in 0 until argsArray.length()) {
JSONArray(args).let { argsArray -> append("${argsArray.getString(i)} ")
for (i in 0 until argsArray.length()) { }
finalCommand.append(argsArray.getString(i))
finalCommand.append(" ")
} }
} else {
append(command)
} }
} else {
finalCommand.append(command)
} }
val shell = createRootShell(true) val shell = createRootShell(true)
val emitData = fun(name: String, data: String) { val emitData = fun(name: String, data: String) {
val jsCode = val jsCode =
"javascript: (function() { try { ${callbackFunc}.${name}.emit('data', ${ "(function() { try { ${callbackFunc}.${name}.emit('data', ${
JSONObject.quote( JSONObject.quote(
data data
) )
}); } catch(e) { console.error('emitData', e); } })();" }); } catch(e) { console.error('emitData', e); } })();"
webView.post { 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 { val completableFuture = CompletableFuture.supplyAsync {
future.get() future.get()
} }
completableFuture.thenAccept { result -> completableFuture.thenAccept { result ->
val emitExitCode = 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.post {
webView.loadUrl(emitExitCode) webView.evaluateJavascript(emitExitCode, null)
} }
if (result.code != 0) { if (result.code != 0) {
val emitErrCode = 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( JSONObject.quote(
result.err.joinToString( result.err.joinToString(
"\n" "\n"
@@ -155,7 +153,7 @@ class WebViewInterface(
) )
};${callbackFunc}.emit('error', err); } catch(e) { console.error('emitErr', e); } })();" };${callbackFunc}.emit('error', err); } catch(e) { console.error('emitErr', e); } })();"
webView.post { webView.post {
webView.loadUrl(emitErrCode) webView.evaluateJavascript(emitErrCode, null)
} }
} }
}.whenComplete { _, _ -> }.whenComplete { _, _ ->
@@ -208,12 +206,12 @@ class WebViewInterface(
// =================== KPM支持 ============================= // =================== KPM支持 =============================
@JavascriptInterface @JavascriptInterface
fun listAllKpm() : String { fun listAllKpm(): String {
return listKpmModules() return listKpmModules()
} }
@JavascriptInterface @JavascriptInterface
fun controlKpm(name: String, args: String) : Int { fun controlKpm(name: String, args: String): Int {
return controlKpmModule(name, args) return controlKpmModule(name, args)
} }
} }

View File

@@ -1,15 +1,13 @@
package io.sukisu.ultra; 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 class UltraShellHelper {
public static String runCmd(String cmds) { public static String runCmd(String cmds) {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
for(String str : KsuCli.INSTANCE.getGLOBAL_MNT_SHELL() for(String str : Shell.cmd(cmds)
.newJob()
.add(cmds)
.to(new ArrayList<>(), null) .to(new ArrayList<>(), null)
.exec() .exec()
.getOut()) { .getOut()) {
@@ -18,11 +16,6 @@ public class UltraShellHelper {
return sb.toString(); 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) { public static void CopyFileTo(String path, String target) {
runCmd("cp -f '" + path + "' '" + target + "' 2>&1"); runCmd("cp -f '" + path + "' '" + target + "' 2>&1");
} }

View File

@@ -1,5 +1,7 @@
package io.sukisu.ultra; 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.getKpmmgrPath;
import static com.sukisu.ultra.ui.util.KsuCliKt.getSuSFSDaemonPath; 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_KPMMGR_PATH = "/data/adb/ksu/bin/kpmmgr";
private static final String OUTSIDE_SUSFSD_PATH = "/data/adb/ksu/bin/susfsd"; private static final String OUTSIDE_SUSFSD_PATH = "/data/adb/ksu/bin/susfsd";
public static void tryToInstall() { public static void tryToInstall() {
String kpmmgrPath = getKpmmgrPath(); SuFile KpmmgrFile = new SuFile(OUTSIDE_KPMMGR_PATH);
if (UltraShellHelper.isPathExists(OUTSIDE_KPMMGR_PATH)) { if (KpmmgrFile.exists()) {
UltraShellHelper.CopyFileTo(kpmmgrPath, OUTSIDE_KPMMGR_PATH); UltraShellHelper.CopyFileTo(getKpmmgrPath(), OUTSIDE_KPMMGR_PATH);
UltraShellHelper.runCmd("chmod a+rx " + OUTSIDE_KPMMGR_PATH); boolean _ = KpmmgrFile.setReadable(true, false);
boolean _ = KpmmgrFile.setExecutable(true, false);
} }
String SuSFSDaemonPath = getSuSFSDaemonPath(); SuFile SuSFSDaemonFile = new SuFile(OUTSIDE_SUSFSD_PATH);
if (UltraShellHelper.isPathExists(OUTSIDE_SUSFSD_PATH)) { if (SuSFSDaemonFile.exists()) {
UltraShellHelper.CopyFileTo(SuSFSDaemonPath, OUTSIDE_SUSFSD_PATH); UltraShellHelper.CopyFileTo(getSuSFSDaemonPath(), OUTSIDE_SUSFSD_PATH);
UltraShellHelper.runCmd("chmod a+rx " + 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 package zako.zako.zako.zakoui.activity.component
import android.annotation.SuppressLint import android.annotation.SuppressLint
import androidx.compose.foundation.layout.*
import androidx.compose.material3.* 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.Modifier
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.navigation.NavHostController 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.isRouteOnBackStackAsState
import com.ramcosta.composedestinations.utils.rememberDestinationsNavigator 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.Natives
import com.sukisu.ultra.ksuApp import com.sukisu.ultra.ksuApp
import com.sukisu.ultra.ui.MainActivity 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.screen.BottomBarDestination
import com.sukisu.ultra.ui.theme.CardConfig.cardAlpha import com.sukisu.ultra.ui.theme.CardConfig.cardAlpha
import com.sukisu.ultra.ui.theme.CardConfig.cardElevation import com.sukisu.ultra.ui.theme.CardConfig.cardElevation
import androidx.compose.foundation.layout.windowInsetsPadding import zako.zako.zako.zakoui.activity.util.AppData
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.DataRefreshManager import zako.zako.zako.zakoui.activity.util.AppData.DataRefreshManager
import zako.zako.zako.zakoui.activity.util.AppData.getKpmVersionUse
@SuppressLint("ContextCastToActivity") @SuppressLint("ContextCastToActivity")
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)

View File

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

View File

@@ -1,11 +1,7 @@
package zako.zako.zako.zakoui.activity.util package zako.zako.zako.zakoui.activity.util
import com.sukisu.ultra.Natives import com.sukisu.ultra.Natives
import com.sukisu.ultra.ui.util.getKpmModuleCount import com.sukisu.ultra.ui.util.*
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 kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow 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 android.content.Context
import androidx.lifecycle.LifecycleCoroutineScope import androidx.lifecycle.LifecycleCoroutineScope
import com.sukisu.ultra.ui.MainActivity import com.sukisu.ultra.ui.MainActivity
import zako.zako.zako.zakoui.activity.util.AppData.DataRefreshManager
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.isActive import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import zako.zako.zako.zakoui.activity.util.AppData.DataRefreshManager
object DataRefreshUtils { object DataRefreshUtils {

View File

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

View File

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

View File

@@ -19,6 +19,8 @@ import androidx.compose.runtime.*
import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier 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.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource 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.annotation.RootGraph
import com.ramcosta.composedestinations.navigation.DestinationsNavigator import com.ramcosta.composedestinations.navigation.DestinationsNavigator
import com.sukisu.ultra.R 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.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.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext 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.io.File
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.* 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 * @author ShirkNeko

View File

@@ -9,89 +9,49 @@ import android.os.Build
import android.widget.Toast import android.widget.Toast
import androidx.activity.compose.rememberLauncherForActivityResult import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts 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.core.animateFloatAsState
import androidx.compose.animation.expandVertically import androidx.compose.foundation.*
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.layout.* 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.shape.RoundedCornerShape
import androidx.compose.foundation.verticalScroll import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.icons.Icons 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.material.icons.filled.*
import androidx.compose.material3.AlertDialog import androidx.compose.material3.*
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.runtime.* import androidx.compose.runtime.*
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip 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.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.core.content.edit import androidx.core.content.edit
import com.ramcosta.composedestinations.annotation.Destination import com.ramcosta.composedestinations.annotation.Destination
import com.ramcosta.composedestinations.annotation.RootGraph import com.ramcosta.composedestinations.annotation.RootGraph
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
import com.sukisu.ultra.Natives import com.sukisu.ultra.Natives
import com.sukisu.ultra.R import com.sukisu.ultra.R
import com.sukisu.ultra.ksuApp
import com.sukisu.ultra.ui.component.ImageEditorDialog import com.sukisu.ultra.ui.component.ImageEditorDialog
import com.sukisu.ultra.ui.component.KsuIsValid 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.*
import com.sukisu.ultra.ui.theme.CardConfig.cardElevation
import com.sukisu.ultra.ui.util.* import com.sukisu.ultra.ui.util.*
import com.topjohnwu.superuser.Shell import com.topjohnwu.superuser.Shell
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import java.util.Locale import java.util.*
import kotlin.math.roundToInt 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 * @author ShirkNeko
@@ -110,7 +70,7 @@ fun saveCardConfig(context: Context) {
/** /**
* 更多设置屏幕 * 更多设置屏幕
*/ */
@SuppressLint("LocalContextConfigurationRead", "ObsoleteSdkInt") @SuppressLint("LocalContextConfigurationRead", "LocalContextResourcesRead", "ObsoleteSdkInt")
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
@Destination<RootGraph> @Destination<RootGraph>
@Composable @Composable