Compare commits

...

9 Commits

9 changed files with 206 additions and 97 deletions

View File

@@ -24,6 +24,8 @@ import androidx.compose.foundation.layout.only
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.systemBars
import androidx.compose.foundation.layout.union
import androidx.compose.material3.Badge
import androidx.compose.material3.BadgedBox
import androidx.compose.material3.Icon
import androidx.compose.material3.NavigationBar
import androidx.compose.material3.NavigationBarItem
@@ -38,6 +40,7 @@ import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavBackStackEntry
import androidx.navigation.NavHostController
import androidx.navigation.compose.rememberNavController
@@ -53,11 +56,13 @@ import com.rifsxd.ksunext.Natives
import com.rifsxd.ksunext.ksuApp
import com.rifsxd.ksunext.ui.screen.BottomBarDestination
import com.rifsxd.ksunext.ui.theme.KernelSUTheme
import com.rifsxd.ksunext.ui.util.*
import com.rifsxd.ksunext.ui.util.LocalSnackbarHost
import com.rifsxd.ksunext.ui.util.rootAvailable
import com.rifsxd.ksunext.ui.util.install
import com.rifsxd.ksunext.ui.util.isSuCompatDisabled
import com.rifsxd.ksunext.ui.screen.FlashIt
import com.rifsxd.ksunext.ui.viewmodel.ModuleViewModel
class MainActivity : ComponentActivity() {
@@ -103,6 +108,11 @@ class MainActivity : ComponentActivity() {
val prefs = getSharedPreferences("settings", Context.MODE_PRIVATE)
val amoledMode = prefs.getBoolean("enable_amoled", false)
val moduleViewModel: ModuleViewModel = viewModel()
val moduleUpdateCount = moduleViewModel.moduleList.count {
moduleViewModel.checkUpdate(it).first.isNotEmpty()
}
KernelSUTheme (
amoledMode = amoledMode
) {
@@ -136,7 +146,7 @@ class MainActivity : ComponentActivity() {
enter = slideInVertically(initialOffsetY = { it }) + fadeIn(),
exit = slideOutVertically(targetOffsetY = { it }) + fadeOut()
) {
BottomBar(navController)
BottomBar(navController, moduleUpdateCount)
}
},
contentWindowInsets = WindowInsets(0, 0, 0, 0)
@@ -163,11 +173,13 @@ class MainActivity : ComponentActivity() {
}
@Composable
private fun BottomBar(navController: NavHostController) {
private fun BottomBar(navController: NavHostController, moduleUpdateCount: Int) {
val navigator = navController.rememberDestinationsNavigator()
val isManager = Natives.becomeManager(ksuApp.packageName)
val fullFeatured = isManager && !Natives.requireNewKernel() && rootAvailable()
val suCompatDisabled = isSuCompatDisabled()
val suSFS = getSuSFS()
val susSUMode = susfsSUS_SU_Mode()
NavigationBar(
tonalElevation = 8.dp,
@@ -177,9 +189,14 @@ private fun BottomBar(navController: NavHostController) {
) {
BottomBarDestination.entries
.filter {
// Hide SuperUser and Module when su compat is enabled
// Hide SuperUser and Module when su compat is disabled
if (suCompatDisabled) {
it != BottomBarDestination.SuperUser && it != BottomBarDestination.Module
if (suSFS == "Supported" && susSUMode == "2") {
true
} else {
// hide SuperUser and Module
it != BottomBarDestination.SuperUser && it != BottomBarDestination.Module
}
} else true
}
.forEach { destination ->
@@ -200,10 +217,21 @@ private fun BottomBar(navController: NavHostController) {
}
},
icon = {
if (isCurrentDestOnBackStack) {
Icon(destination.iconSelected, stringResource(destination.label))
// Show badge for Module icon if moduleUpdateCount > 0
if (destination == BottomBarDestination.Module && moduleUpdateCount > 0) {
BadgedBox(badge = { Badge { Text(moduleUpdateCount.toString()) } }) {
if (isCurrentDestOnBackStack) {
Icon(destination.iconSelected, stringResource(destination.label))
} else {
Icon(destination.iconNotSelected, stringResource(destination.label))
}
}
} else {
Icon(destination.iconNotSelected, stringResource(destination.label))
if (isCurrentDestOnBackStack) {
Icon(destination.iconSelected, stringResource(destination.label))
} else {
Icon(destination.iconNotSelected, stringResource(destination.label))
}
}
},
label = { Text(stringResource(destination.label)) },

View File

@@ -95,14 +95,16 @@ fun CustomizationScreen(navigator: DestinationsNavigator) {
prefs.getBoolean("use_banner", true)
)
}
SwitchItem(
icon = Icons.Filled.ViewCarousel,
title = stringResource(id = R.string.settings_banner),
summary = stringResource(id = R.string.settings_banner_summary),
checked = useBanner
) {
prefs.edit().putBoolean("use_banner", it).apply()
useBanner = it
if (ksuVersion != null) {
SwitchItem(
icon = Icons.Filled.ViewCarousel,
title = stringResource(id = R.string.settings_banner),
summary = stringResource(id = R.string.settings_banner_summary),
checked = useBanner
) {
prefs.edit().putBoolean("use_banner", it).apply()
useBanner = it
}
}
var enableAmoled by rememberSaveable {

View File

@@ -111,11 +111,7 @@ fun HomeScreen(navigator: DestinationsNavigator) {
}
}
val moduleUpdateCount = moduleViewModel.moduleList.count {
moduleViewModel.checkUpdate(it).first.isNotEmpty()
}
StatusCard(kernelVersion, ksuVersion, lkmMode, moduleUpdateCount) {
StatusCard(kernelVersion, ksuVersion, lkmMode) {
navigator.navigate(InstallScreenDestination)
}
if (isManager && Natives.requireNewKernel()) {
@@ -332,8 +328,10 @@ private fun StatusCard(
intent.addFlags(android.content.Intent.FLAG_ACTIVITY_NEW_TASK)
if (ksuVersion != null) {
context.startActivity(intent)
} else {
} else if (ksuVersion == null && kernelVersion.isGKI()) {
onClickInstall()
} else {
Toast.makeText(context, "Something weird happened... 🤔", Toast.LENGTH_SHORT).show()
}
} else if (ksuVersion == null && kernelVersion.isGKI()) {
onClickInstall()
@@ -428,22 +426,6 @@ private fun StatusCard(
text = stringResource(R.string.home_module_count, getModuleCount()),
style = MaterialTheme.typography.bodyMedium
)
if (moduleUpdateCount > 0) {
Text(
text = stringResource(R.string.home_module_update_count, moduleUpdateCount),
style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.primary
)
}
val suSFS = getSuSFS()
if (suSFS == "Supported") {
Text(
text = "SuSFS: " + stringResource(R.string.susfs_supported),
style = MaterialTheme.typography.bodyMedium
)
}
}
}
@@ -595,7 +577,7 @@ private fun InfoCard(autoExpand: Boolean = false) {
Spacer(Modifier.height(16.dp))
InfoCardItem(
label = stringResource(R.string.home_susfs_version),
content = "${getSuSFSVersion()} (${getSuSFSVariant()}) $susSUMode",
content = "${stringResource(R.string.susfs_supported)} | ${getSuSFSVersion()} (${getSuSFSVariant()}) $susSUMode",
icon = painterResource(R.drawable.ic_sus),
)
}

View File

@@ -829,7 +829,7 @@ fun ModuleItem(
LabelItem(
text = stringResource(R.string.module_update),
style = com.dergoogler.mmrl.ui.component.LabelItemDefaults.style.copy(
containerColor = MaterialTheme.colorScheme.onTertiary,
containerColor = MaterialTheme.colorScheme.tertiaryContainer,
contentColor = MaterialTheme.colorScheme.onTertiaryContainer
)
)
@@ -1012,7 +1012,7 @@ fun ModuleItem(
Spacer(modifier = Modifier.weight(1f, true))
if (updateUrl.isNotEmpty()) {
if (updateUrl.isNotEmpty() && !module.remove && !module.update) {
Button(
modifier = Modifier.defaultMinSize(52.dp, 32.dp),
enabled = !module.remove,

View File

@@ -349,18 +349,20 @@ fun SettingScreen(navigator: DestinationsNavigator) {
}
val developer = stringResource(id = R.string.developer)
ListItem(
leadingContent = {
Icon(
Icons.Filled.DeveloperBoard,
developer
)
},
headlineContent = { Text(developer) },
modifier = Modifier.clickable {
navigator.navigate(DeveloperScreenDestination)
}
)
if (ksuVersion != null) {
ListItem(
leadingContent = {
Icon(
Icons.Filled.DeveloperBoard,
developer
)
},
headlineContent = { Text(developer) },
modifier = Modifier.clickable {
navigator.navigate(DeveloperScreenDestination)
}
)
}
val lkmMode = Natives.version >= Natives.MINIMAL_SUPPORTED_KERNEL_LKM && Natives.isLkmMode
if (lkmMode) {

View File

@@ -92,7 +92,7 @@ fun SuperUserScreen(navigator: DestinationsNavigator) {
}
)
}, onClick = {
viewModel.showSystemApps = !viewModel.showSystemApps
viewModel.updateShowSystemApps(!viewModel.showSystemApps)
showDropdown = false
})
}

View File

@@ -1,5 +1,9 @@
package com.rifsxd.ksunext.ui.viewmodel
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.content.ServiceConnection
import android.content.pm.ApplicationInfo
import android.content.pm.PackageInfo
import android.os.Parcelable
@@ -24,6 +28,9 @@ import kotlinx.coroutines.delay
import kotlinx.coroutines.withTimeoutOrNull
import java.text.Collator
import java.util.*
import kotlin.coroutines.resume
import kotlin.coroutines.suspendCoroutine
import androidx.core.content.edit
class SuperUserViewModel : ViewModel() {
val isPlatformAlive get() = Platform.isAlive
@@ -61,11 +68,19 @@ class SuperUserViewModel : ViewModel() {
}
}
private val prefs = ksuApp.getSharedPreferences("settings", Context.MODE_PRIVATE)!!
var search by mutableStateOf("")
var showSystemApps by mutableStateOf(false)
var showSystemApps by mutableStateOf(prefs.getBoolean("show_system_apps", false))
private set
var isRefreshing by mutableStateOf(false)
private set
fun updateShowSystemApps(newValue: Boolean) {
showSystemApps = newValue
prefs.edit { putBoolean("show_system_apps", newValue) }
}
private val sortedList by derivedStateOf {
val comparator = compareBy<AppInfo> {
when {

View File

@@ -167,6 +167,21 @@ fn do_cpio_cmd(magiskboot: &Path, workdir: &Path, cmd: &str) -> Result<()> {
Ok(())
}
fn do_vendor_cpio_cmd(magiskboot: &Path, workdir: &Path, cmd: &str) -> Result<()> {
let vendor_ramdisk_cpio = workdir.join("vendor_ramdisk").join("init_boot.cpio");
let status = Command::new(magiskboot)
.current_dir(workdir)
.stdout(Stdio::null())
.stderr(Stdio::null())
.arg("cpio")
.arg(vendor_ramdisk_cpio)
.arg(cmd)
.status()?;
ensure!(status.success(), "magiskboot cpio {} failed", cmd);
Ok(())
}
fn is_magisk_patched(magiskboot: &Path, workdir: &Path) -> Result<bool> {
let status = Command::new(magiskboot)
.current_dir(workdir)
@@ -234,6 +249,7 @@ pub fn restore(
.status()?;
ensure!(status.success(), "magiskboot unpack failed");
let no_ramdisk = !workdir.join("ramdisk.cpio").exists();
let is_kernelsu_patched = is_kernelsu_patched(&magiskboot, workdir)?;
ensure!(
is_kernelsu_patched,
@@ -270,16 +286,29 @@ pub fn restore(
}
if new_boot.is_none() {
// remove kernelsu.ko
do_cpio_cmd(&magiskboot, workdir, "rm kernelsu.ko")?;
if no_ramdisk {
// vendor ramdisk restore
do_vendor_cpio_cmd(&magiskboot, workdir, "rm kernelsu.ko")?;
// if init.real exists, restore it
let status = do_cpio_cmd(&magiskboot, workdir, "exists init.real").is_ok();
if status {
do_cpio_cmd(&magiskboot, workdir, "mv init.real init")?;
let status = do_vendor_cpio_cmd(&magiskboot, workdir, "exists init.real").is_ok();
if status {
do_vendor_cpio_cmd(&magiskboot, workdir, "mv init.real init")?;
} else {
let vendor_ramdisk = workdir.join("vendor_ramdisk").join("init_boot.cpio");
std::fs::remove_file(vendor_ramdisk)?;
}
} else {
let ramdisk = workdir.join("ramdisk.cpio");
std::fs::remove_file(ramdisk)?;
// remove kernelsu.ko
do_cpio_cmd(&magiskboot, workdir, "rm kernelsu.ko")?;
// if init.real exists, restore it
let status = do_cpio_cmd(&magiskboot, workdir, "exists init.real").is_ok();
if status {
do_cpio_cmd(&magiskboot, workdir, "mv init.real init")?;
} else {
let ramdisk = workdir.join("ramdisk.cpio");
std::fs::remove_file(ramdisk)?;
}
}
println!("- Repacking boot image");
@@ -441,11 +470,6 @@ fn do_patch(
assets::copy_assets_to_file("ksuinit", init_file).context("copy ksuinit failed")?;
}
// magiskboot unpack boot.img
// magiskboot cpio ramdisk.cpio 'cp init init.real'
// magiskboot cpio ramdisk.cpio 'add 0755 ksuinit init'
// magiskboot cpio ramdisk.cpio 'add 0755 <kmod> kernelsu.ko'
println!("- Unpacking boot image");
let status = Command::new(&magiskboot)
.current_dir(workdir)
@@ -468,17 +492,29 @@ fn do_patch(
let mut need_backup = false;
if !is_kernelsu_patched {
// kernelsu.ko is not exist, backup init if necessary
let status = do_cpio_cmd(&magiskboot, workdir, "exists init");
if status.is_ok() {
do_cpio_cmd(&magiskboot, workdir, "mv init init.real")?;
if no_ramdisk {
// vendor ramdisk patching
let status = do_vendor_cpio_cmd(&magiskboot, workdir, "exists init");
if status.is_ok() {
do_vendor_cpio_cmd(&magiskboot, workdir, "mv init init.real")?;
}
} else {
// kernelsu.ko is not exist, backup init if necessary
let status = do_cpio_cmd(&magiskboot, workdir, "exists init");
if status.is_ok() {
do_cpio_cmd(&magiskboot, workdir, "mv init init.real")?;
}
need_backup = flash;
}
need_backup = flash;
}
do_cpio_cmd(&magiskboot, workdir, "add 0755 init init")?;
do_cpio_cmd(&magiskboot, workdir, "add 0755 kernelsu.ko kernelsu.ko")?;
if no_ramdisk {
do_vendor_cpio_cmd(&magiskboot, workdir, "add 0755 init init")?;
do_vendor_cpio_cmd(&magiskboot, workdir, "add 0755 kernelsu.ko kernelsu.ko")?;
} else {
do_cpio_cmd(&magiskboot, workdir, "add 0755 init init")?;
do_cpio_cmd(&magiskboot, workdir, "add 0755 kernelsu.ko kernelsu.ko")?;
}
#[cfg(target_os = "android")]
if need_backup {
@@ -658,8 +694,12 @@ fn find_boot_image(
let init_boot_exist =
Path::new(&format!("/dev/block/by-name/init_boot{slot_suffix}")).exists();
let vendor_boot_exist =
Path::new(&format!("/dev/block/by-name/vendor_boot{slot_suffix}")).exists();
let boot_partition = if !is_replace_kernel && init_boot_exist && !skip_init {
format!("/dev/block/by-name/init_boot{slot_suffix}")
} else if !is_replace_kernel && vendor_boot_exist && !skip_init {
format!("/dev/block/by-name/vendor_boot{slot_suffix}")
} else {
format!("/dev/block/by-name/boot{slot_suffix}")
};

View File

@@ -167,6 +167,21 @@ fn do_cpio_cmd(magiskboot: &Path, workdir: &Path, cmd: &str) -> Result<()> {
Ok(())
}
fn do_vendor_cpio_cmd(magiskboot: &Path, workdir: &Path, cmd: &str) -> Result<()> {
let vendor_ramdisk_cpio = workdir.join("vendor_ramdisk").join("init_boot.cpio");
let status = Command::new(magiskboot)
.current_dir(workdir)
.stdout(Stdio::null())
.stderr(Stdio::null())
.arg("cpio")
.arg(vendor_ramdisk_cpio)
.arg(cmd)
.status()?;
ensure!(status.success(), "magiskboot cpio {} failed", cmd);
Ok(())
}
fn is_magisk_patched(magiskboot: &Path, workdir: &Path) -> Result<bool> {
let status = Command::new(magiskboot)
.current_dir(workdir)
@@ -234,6 +249,7 @@ pub fn restore(
.status()?;
ensure!(status.success(), "magiskboot unpack failed");
let no_ramdisk = !workdir.join("ramdisk.cpio").exists();
let is_kernelsu_patched = is_kernelsu_patched(&magiskboot, workdir)?;
ensure!(
is_kernelsu_patched,
@@ -270,16 +286,29 @@ pub fn restore(
}
if new_boot.is_none() {
// remove kernelsu.ko
do_cpio_cmd(&magiskboot, workdir, "rm kernelsu.ko")?;
if no_ramdisk {
// vendor ramdisk restore
do_vendor_cpio_cmd(&magiskboot, workdir, "rm kernelsu.ko")?;
// if init.real exists, restore it
let status = do_cpio_cmd(&magiskboot, workdir, "exists init.real").is_ok();
if status {
do_cpio_cmd(&magiskboot, workdir, "mv init.real init")?;
let status = do_vendor_cpio_cmd(&magiskboot, workdir, "exists init.real").is_ok();
if status {
do_vendor_cpio_cmd(&magiskboot, workdir, "mv init.real init")?;
} else {
let vendor_ramdisk = workdir.join("vendor_ramdisk").join("init_boot.cpio");
std::fs::remove_file(vendor_ramdisk)?;
}
} else {
let ramdisk = workdir.join("ramdisk.cpio");
std::fs::remove_file(ramdisk)?;
// remove kernelsu.ko
do_cpio_cmd(&magiskboot, workdir, "rm kernelsu.ko")?;
// if init.real exists, restore it
let status = do_cpio_cmd(&magiskboot, workdir, "exists init.real").is_ok();
if status {
do_cpio_cmd(&magiskboot, workdir, "mv init.real init")?;
} else {
let ramdisk = workdir.join("ramdisk.cpio");
std::fs::remove_file(ramdisk)?;
}
}
println!("- Repacking boot image");
@@ -301,7 +330,7 @@ pub fn restore(
let output_dir = std::env::current_dir()?;
let now = chrono::Utc::now();
let output_image = output_dir.join(format!(
"kernelsu_restore_{}.img",
"kernelsu_next_restore_{}.img",
now.format("%Y%m%d_%H%M%S")
));
@@ -441,11 +470,6 @@ fn do_patch(
assets::copy_assets_to_file("ksuinit", init_file).context("copy ksuinit failed")?;
}
// magiskboot unpack boot.img
// magiskboot cpio ramdisk.cpio 'cp init init.real'
// magiskboot cpio ramdisk.cpio 'add 0755 ksuinit init'
// magiskboot cpio ramdisk.cpio 'add 0755 <kmod> kernelsu.ko'
println!("- Unpacking boot image");
let status = Command::new(&magiskboot)
.current_dir(workdir)
@@ -468,17 +492,29 @@ fn do_patch(
let mut need_backup = false;
if !is_kernelsu_patched {
// kernelsu.ko is not exist, backup init if necessary
let status = do_cpio_cmd(&magiskboot, workdir, "exists init");
if status.is_ok() {
do_cpio_cmd(&magiskboot, workdir, "mv init init.real")?;
if no_ramdisk {
// vendor ramdisk patching
let status = do_vendor_cpio_cmd(&magiskboot, workdir, "exists init");
if status.is_ok() {
do_vendor_cpio_cmd(&magiskboot, workdir, "mv init init.real")?;
}
} else {
// kernelsu.ko is not exist, backup init if necessary
let status = do_cpio_cmd(&magiskboot, workdir, "exists init");
if status.is_ok() {
do_cpio_cmd(&magiskboot, workdir, "mv init init.real")?;
}
need_backup = flash;
}
need_backup = flash;
}
do_cpio_cmd(&magiskboot, workdir, "add 0755 init init")?;
do_cpio_cmd(&magiskboot, workdir, "add 0755 kernelsu.ko kernelsu.ko")?;
if no_ramdisk {
do_vendor_cpio_cmd(&magiskboot, workdir, "add 0755 init init")?;
do_vendor_cpio_cmd(&magiskboot, workdir, "add 0755 kernelsu.ko kernelsu.ko")?;
} else {
do_cpio_cmd(&magiskboot, workdir, "add 0755 init init")?;
do_cpio_cmd(&magiskboot, workdir, "add 0755 kernelsu.ko kernelsu.ko")?;
}
#[cfg(target_os = "android")]
if need_backup {
@@ -658,8 +694,12 @@ fn find_boot_image(
let init_boot_exist =
Path::new(&format!("/dev/block/by-name/init_boot{slot_suffix}")).exists();
let vendor_boot_exist =
Path::new(&format!("/dev/block/by-name/vendor_boot{slot_suffix}")).exists();
let boot_partition = if !is_replace_kernel && init_boot_exist && !skip_init {
format!("/dev/block/by-name/init_boot{slot_suffix}")
} else if !is_replace_kernel && vendor_boot_exist && !skip_init {
format!("/dev/block/by-name/vendor_boot{slot_suffix}")
} else {
format!("/dev/block/by-name/boot{slot_suffix}")
};