@@ -15,19 +15,30 @@ Android device root solution based on [KernelSU](https://github.com/KernelSU/Ker
|
|||||||
|
|
||||||
## How to add
|
## How to add
|
||||||
|
|
||||||
Using the susfs-dev branch (integrated susfs with support for non-GKI devices)
|
Using the susfs-stable or susfs-dev branch (integrated susfs with support for non-GKI devices)
|
||||||
|
|
||||||
|
```
|
||||||
|
curl -LSs "https://raw.githubusercontent.com/ShirkNeko/KernelSU/main/kernel/setup.sh" | bash -s susfs-stable
|
||||||
|
```
|
||||||
|
|
||||||
```
|
```
|
||||||
curl -LSs "https://raw.githubusercontent.com/ShirkNeko/SukiSU-Ultra/main/kernel/setup.sh" | bash -s susfs-dev
|
curl -LSs "https://raw.githubusercontent.com/ShirkNeko/SukiSU-Ultra/main/kernel/setup.sh" | bash -s susfs-dev
|
||||||
```
|
```
|
||||||
|
|
||||||
Use main branching (no longer with support for non-GKI devices)
|
Use main branching
|
||||||
|
```
|
||||||
|
curl -LSs "https://raw.githubusercontent.com/ShirkNeko/KernelSU/main/kernel/setup.sh" | bash -s main
|
||||||
|
```
|
||||||
|
|
||||||
|
Use dev branching(With support for non-GKI devices)
|
||||||
```
|
```
|
||||||
curl -LSs "https://raw.githubusercontent.com/ShirkNeko/SukiSU-Ultra/main/kernel/setup.sh" | bash -s main
|
curl -LSs "https://raw.githubusercontent.com/ShirkNeko/SukiSU-Ultra/main/kernel/setup.sh" | bash -s main
|
||||||
```
|
```
|
||||||
|
|
||||||
## How to use integrated susfs
|
## How to use integrated susfs
|
||||||
|
|
||||||
Use the susfs-dev branch directly without any patching
|
1. Use the susfs-dev branch directly without any patching
|
||||||
|
2. Manually patch susfs using a dev branch that supports non-GKI devices.
|
||||||
|
|
||||||
|
|
||||||
## More links
|
## More links
|
||||||
@@ -83,6 +94,7 @@ Note: You only need to fill in the first two kernel versions, such as 5.10, 5.15
|
|||||||
- [Ktouls](https://github.com/Ktouls) Thanks so much for bringing me support
|
- [Ktouls](https://github.com/Ktouls) Thanks so much for bringing me support
|
||||||
- [zaoqi123](https://github.com/zaoqi123) It's not a bad idea to buy me a milk tea
|
- [zaoqi123](https://github.com/zaoqi123) It's not a bad idea to buy me a milk tea
|
||||||
- [wswzgdg](https://github.com/wswzgdg) Many thanks for supporting this project
|
- [wswzgdg](https://github.com/wswzgdg) Many thanks for supporting this project
|
||||||
|
- [yspbwx2010](https://github.com/yspbwx2010) Many thanks
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -15,20 +15,30 @@
|
|||||||
## 如何添加
|
## 如何添加
|
||||||
在内核源码的根目录下执行以下命令:
|
在内核源码的根目录下执行以下命令:
|
||||||
|
|
||||||
使用 susfs-dev 分支(已集成susfs,带非GKI设备的支持)
|
使用 susfs-stable 或者 susfs-dev 分支(已集成susfs,带非GKI设备的支持)
|
||||||
|
```
|
||||||
|
curl -LSs "https://raw.githubusercontent.com/ShirkNeko/KernelSU/main/kernel/setup.sh" | bash -s susfs-stable
|
||||||
|
```
|
||||||
|
|
||||||
```
|
```
|
||||||
curl -LSs "https://raw.githubusercontent.com/ShirkNeko/SukiSU-Ultra/main/kernel/setup.sh" | bash -s susfs-dev
|
curl -LSs "https://raw.githubusercontent.com/ShirkNeko/SukiSU-Ultra/main/kernel/setup.sh" | bash -s susfs-dev
|
||||||
```
|
```
|
||||||
|
|
||||||
使用 main 分支(不再带非GKI设备的支持)
|
|
||||||
|
使用 main 分支
|
||||||
```
|
```
|
||||||
curl -LSs "https://raw.githubusercontent.com/ShirkNeko/SukiSU-Ultra/main/kernel/setup.sh" | bash -s main
|
curl -LSs "https://raw.githubusercontent.com/ShirkNeko/SukiSU-Ultra/main/kernel/setup.sh" | bash -s main
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
使用 dev 分支(带非GKI设备的支持)
|
||||||
|
```
|
||||||
|
curl -LSs "https://raw.githubusercontent.com/ShirkNeko/KernelSU/main/kernel/setup.sh" | bash -s main
|
||||||
|
```
|
||||||
## 如何集成 susfs
|
## 如何集成 susfs
|
||||||
|
|
||||||
1. 直接使用 susfs-dev 分支,不需要再集成 susfs
|
1. 直接使用 susfs-stable 或者 susfs-dev 分支,不需要再集成 susfs
|
||||||
|
2. 使用支持非GKI设备的 dev 分支,手动补丁 susfs
|
||||||
|
|
||||||
## 钩子方法
|
## 钩子方法
|
||||||
- 此部分引用自 [rsuntk 的钩子方法](https://github.com/rsuntk/KernelSU)
|
- 此部分引用自 [rsuntk 的钩子方法](https://github.com/rsuntk/KernelSU)
|
||||||
@@ -72,7 +82,7 @@ curl -LSs "https://raw.githubusercontent.com/ShirkNeko/SukiSU-Ultra/main/kernel/
|
|||||||
1. 基于内核的 `su` 和 root 访问管理
|
1. 基于内核的 `su` 和 root 访问管理
|
||||||
2. 基于 5ec1cff 的 [Magic Mount](https://github.com/5ec1cff/KernelSU) 的模块系统
|
2. 基于 5ec1cff 的 [Magic Mount](https://github.com/5ec1cff/KernelSU) 的模块系统
|
||||||
3. [App Profile](https://kernelsu.org/guide/app-profile.html):将 root 权限锁在笼子里
|
3. [App Profile](https://kernelsu.org/guide/app-profile.html):将 root 权限锁在笼子里
|
||||||
4. 恢复对非 GKI 2.0 内核的支持(仅限susfs-dev和未进行susfs补丁的dev分支)
|
4. 恢复对非 GKI 2.0 内核的支持
|
||||||
5. 更多自定义功能
|
5. 更多自定义功能
|
||||||
|
|
||||||
|
|
||||||
@@ -85,6 +95,7 @@ curl -LSs "https://raw.githubusercontent.com/ShirkNeko/SukiSU-Ultra/main/kernel/
|
|||||||
- [Ktouls](https://github.com/Ktouls) 非常感谢你给我带来的支持
|
- [Ktouls](https://github.com/Ktouls) 非常感谢你给我带来的支持
|
||||||
- [zaoqi123](https://github.com/zaoqi123) 请我喝奶茶也不错
|
- [zaoqi123](https://github.com/zaoqi123) 请我喝奶茶也不错
|
||||||
- [wswzgdg](https://github.com/wswzgdg) 非常感谢对此项目的支持
|
- [wswzgdg](https://github.com/wswzgdg) 非常感谢对此项目的支持
|
||||||
|
- [yspbwx2010](https://github.com/yspbwx2010) 非常感谢
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -686,12 +686,6 @@ __maybe_unused int ksu_kprobe_init(void)
|
|||||||
rc = register_kprobe(&renameat_kp);
|
rc = register_kprobe(&renameat_kp);
|
||||||
pr_info("renameat kp: %d\n", rc);
|
pr_info("renameat kp: %d\n", rc);
|
||||||
|
|
||||||
#ifdef CONFIG_KPM
|
|
||||||
// KPM初始化状态
|
|
||||||
kpm_cfi_bypass_init();
|
|
||||||
// kpm_stack_init();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -699,11 +693,6 @@ __maybe_unused int ksu_kprobe_exit(void)
|
|||||||
{
|
{
|
||||||
unregister_kprobe(&prctl_kp);
|
unregister_kprobe(&prctl_kp);
|
||||||
unregister_kprobe(&renameat_kp);
|
unregister_kprobe(&renameat_kp);
|
||||||
#ifdef CONFIG_KPM
|
|
||||||
// KPM取消状态
|
|
||||||
kpm_cfi_bypass_exit();
|
|
||||||
// kpm_stack_exit();
|
|
||||||
#endif
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,2 @@
|
|||||||
obj-y += kpm.o
|
obj-y += kpm.o
|
||||||
obj-y += compact.o
|
obj-y += compact.o
|
||||||
obj-y += bypasscfi.o
|
|
||||||
@@ -1,73 +0,0 @@
|
|||||||
#include <linux/kprobes.h>
|
|
||||||
#include <linux/module.h>
|
|
||||||
#include <linux/version.h>
|
|
||||||
#include <linux/kallsyms.h>
|
|
||||||
|
|
||||||
/* CFI 检查函数符号 */
|
|
||||||
#define CFI_CHECK_FUNC "__cfi_check"
|
|
||||||
|
|
||||||
/* Kprobe 实例 */
|
|
||||||
static struct kprobe cfi_kp;
|
|
||||||
|
|
||||||
bool kpm_is_allow_address(unsigned long addr);
|
|
||||||
|
|
||||||
/*--------------------- kprobe 处理逻辑 ---------------------*/
|
|
||||||
static int handler_pre(struct kprobe *p, struct pt_regs *regs)
|
|
||||||
{
|
|
||||||
unsigned long target_addr;
|
|
||||||
|
|
||||||
/* 从寄存器获取目标地址(架构相关) */
|
|
||||||
#if defined(__aarch64__)
|
|
||||||
target_addr = regs->regs[1]; // ARM64: 第二个参数在 X1
|
|
||||||
#elif defined(__x86_64__)
|
|
||||||
target_addr = regs->si; // x86_64: 第二个参数在 RSI
|
|
||||||
#else
|
|
||||||
#error "Unsupported architecture"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* 根据自定义规则放行 */
|
|
||||||
if (kpm_is_allow_address(target_addr)) {
|
|
||||||
printk(KERN_INFO "CFI bypass at 0x%lx\n", target_addr);
|
|
||||||
#if defined(__aarch64__)
|
|
||||||
regs->regs[0] = 0; // 修改返回值:0 表示校验通过
|
|
||||||
#elif defined(__x86_64__)
|
|
||||||
regs->ax = 0; // x86 返回值在 RAX
|
|
||||||
#endif
|
|
||||||
return 0; // 跳过原始 CFI 检查
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0; // 继续执行原始检查
|
|
||||||
}
|
|
||||||
|
|
||||||
/*--------------------- 模块初始化/卸载 ---------------------*/
|
|
||||||
int kpm_cfi_bypass_init(void)
|
|
||||||
{
|
|
||||||
unsigned long cfi_check_addr;
|
|
||||||
|
|
||||||
/* 动态查找 CFI 检查函数 */
|
|
||||||
cfi_check_addr = kallsyms_lookup_name(CFI_CHECK_FUNC);
|
|
||||||
if (!cfi_check_addr) {
|
|
||||||
printk(KERN_ERR "CFI check function not found\n");
|
|
||||||
return -ENOENT;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 初始化 kprobe */
|
|
||||||
memset(&cfi_kp, 0, sizeof(cfi_kp));
|
|
||||||
cfi_kp.addr = (kprobe_opcode_t *)cfi_check_addr;
|
|
||||||
cfi_kp.pre_handler = handler_pre;
|
|
||||||
|
|
||||||
/* 注册 kprobe */
|
|
||||||
if (register_kprobe(&cfi_kp) < 0) {
|
|
||||||
printk(KERN_ERR "Register kprobe failed\n");
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
printk(KERN_INFO "CFI bypass module loaded\n");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void kpm_cfi_bypass_exit(void)
|
|
||||||
{
|
|
||||||
unregister_kprobe(&cfi_kp);
|
|
||||||
printk(KERN_INFO "CFI bypass module unloaded\n");
|
|
||||||
}
|
|
||||||
@@ -31,66 +31,20 @@ unsigned long sukisu_compact_find_symbol(const char* name);
|
|||||||
|
|
||||||
// ======================================================================
|
// ======================================================================
|
||||||
|
|
||||||
const char* kpver = "0.10";
|
|
||||||
|
|
||||||
struct CompactAddressSymbol {
|
struct CompactAddressSymbol {
|
||||||
const char* symbol_name;
|
const char* symbol_name;
|
||||||
void* addr;
|
void* addr;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct CompactAliasSymbol {
|
|
||||||
const char* symbol_name;
|
|
||||||
const char* compact_symbol_name;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct CompactProxySymbol {
|
|
||||||
const char* symbol_name;
|
|
||||||
const char* compact_symbol_name;
|
|
||||||
void* cached_address;
|
|
||||||
};
|
|
||||||
|
|
||||||
static struct CompactAddressSymbol address_symbol [] = {
|
static struct CompactAddressSymbol address_symbol [] = {
|
||||||
{ "kallsyms_lookup_name", &kallsyms_lookup_name },
|
{ "kallsyms_lookup_name", &kallsyms_lookup_name },
|
||||||
{ "compact_find_symbol", &sukisu_compact_find_symbol },
|
{ "compact_find_symbol", &sukisu_compact_find_symbol },
|
||||||
{ "compat_copy_to_user", ©_to_user },
|
|
||||||
{ "compat_strncpy_from_user", &strncpy_from_user },
|
|
||||||
{ "kpver", &kpver },
|
|
||||||
{ "is_run_in_sukisu_ultra", (void*)1 }
|
{ "is_run_in_sukisu_ultra", (void*)1 }
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct CompactAliasSymbol alias_symbol[] = {
|
|
||||||
{"compat_copy_to_user", "__arch_copy_to_user"}
|
|
||||||
};
|
|
||||||
|
|
||||||
static struct CompactProxySymbol proxy_symbol[] = {
|
|
||||||
{"kf_strncat", "strncat", NULL },
|
|
||||||
{"kf_strlen", "strlen", NULL },
|
|
||||||
{"kf_strcpy", "strcpy", NULL },
|
|
||||||
};
|
|
||||||
|
|
||||||
static unsigned long sukisu_find_proxy_symbol(const char* name) {
|
|
||||||
// 查找proxy符号
|
|
||||||
int i;
|
|
||||||
for(i = 0; i < (sizeof(proxy_symbol) / sizeof(struct CompactProxySymbol)); i++) {
|
|
||||||
struct CompactProxySymbol* symbol = &proxy_symbol[i];
|
|
||||||
if(strcmp(name, symbol->symbol_name) == 0) {
|
|
||||||
if(symbol->cached_address == NULL) {
|
|
||||||
symbol->cached_address = (void*) kallsyms_lookup_name(symbol->compact_symbol_name);
|
|
||||||
}
|
|
||||||
if(symbol->cached_address != NULL) {
|
|
||||||
return (unsigned long) &symbol->cached_address;
|
|
||||||
} else {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned long sukisu_compact_find_symbol(const char* name) {
|
unsigned long sukisu_compact_find_symbol(const char* name) {
|
||||||
int i;
|
int i;
|
||||||
unsigned long addr;
|
unsigned long addr;
|
||||||
char isFoundedProxy = 0;
|
|
||||||
|
|
||||||
// 先自己在地址表部分查出来
|
// 先自己在地址表部分查出来
|
||||||
for(i = 0; i < (sizeof(address_symbol) / sizeof(struct CompactAddressSymbol)); i++) {
|
for(i = 0; i < (sizeof(address_symbol) / sizeof(struct CompactAddressSymbol)); i++) {
|
||||||
@@ -100,34 +54,13 @@ unsigned long sukisu_compact_find_symbol(const char* name) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 如果符号名以 "kf_" 开头,尝试解析去掉前缀的部分 */
|
|
||||||
if (strncmp(name, "kf_", 3) == 0) {
|
|
||||||
addr = sukisu_find_proxy_symbol(name);
|
|
||||||
isFoundedProxy = 1;
|
|
||||||
if(addr != 0) {
|
|
||||||
return addr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 通过内核来查
|
// 通过内核来查
|
||||||
addr = kallsyms_lookup_name(name);
|
addr = kallsyms_lookup_name(name);
|
||||||
if(addr) {
|
if(addr) {
|
||||||
return addr;
|
return addr;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 查不到就查查兼容的符号
|
|
||||||
for(i = 0; i < (sizeof(alias_symbol) / sizeof(struct CompactAliasSymbol)); i++) {
|
|
||||||
struct CompactAliasSymbol* symbol = &alias_symbol[i];
|
|
||||||
if(strcmp(name, symbol->symbol_name) == 0) {
|
|
||||||
addr = kallsyms_lookup_name(symbol->compact_symbol_name);
|
|
||||||
if(addr)
|
|
||||||
return addr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!isFoundedProxy) {
|
|
||||||
return sukisu_find_proxy_symbol(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
EXPORT_SYMBOL(sukisu_compact_find_symbol);
|
||||||
|
|||||||
1290
kernel/kpm/kpm.c
1290
kernel/kpm/kpm.c
File diff suppressed because it is too large
Load Diff
@@ -4,14 +4,9 @@
|
|||||||
int sukisu_handle_kpm(unsigned long arg3, unsigned long arg4, unsigned long arg5);
|
int sukisu_handle_kpm(unsigned long arg3, unsigned long arg4, unsigned long arg5);
|
||||||
int sukisu_is_kpm_control_code(unsigned long arg2);
|
int sukisu_is_kpm_control_code(unsigned long arg2);
|
||||||
|
|
||||||
int kpm_cfi_bypass_init(void);
|
|
||||||
void kpm_cfi_bypass_exit(void);
|
|
||||||
int kpm_stack_init(void);
|
|
||||||
void kpm_stack_exit(void);
|
|
||||||
|
|
||||||
// KPM控制代码
|
// KPM控制代码
|
||||||
#define CMD_KPM_CONTROL 28
|
#define CMD_KPM_CONTROL 28
|
||||||
#define CMD_KPM_CONTROL_MAX 34
|
#define CMD_KPM_CONTROL_MAX 35
|
||||||
|
|
||||||
// 控制代码
|
// 控制代码
|
||||||
|
|
||||||
@@ -46,6 +41,8 @@ void kpm_stack_exit(void);
|
|||||||
// error will return -1
|
// error will return -1
|
||||||
#define SUKISU_KPM_PRINT 34
|
#define SUKISU_KPM_PRINT 34
|
||||||
|
|
||||||
|
#define SUKISU_KPM_VERSION 35
|
||||||
|
|
||||||
|
|
||||||
/* A64 instructions are always 32 bits. */
|
/* A64 instructions are always 32 bits. */
|
||||||
#define AARCH64_INSN_SIZE 4
|
#define AARCH64_INSN_SIZE 4
|
||||||
|
|||||||
@@ -335,16 +335,18 @@ private fun StatusCard(
|
|||||||
Spacer(modifier = Modifier.height(4.dp))
|
Spacer(modifier = Modifier.height(4.dp))
|
||||||
|
|
||||||
val suSFS = getSuSFS()
|
val suSFS = getSuSFS()
|
||||||
val translatedStatus = when (suSFS) {
|
if (lkmMode != true) {
|
||||||
"Supported" -> stringResource(R.string.status_supported)
|
val translatedStatus = when (suSFS) {
|
||||||
"Not Supported" -> stringResource(R.string.status_not_supported)
|
"Supported" -> stringResource(R.string.status_supported)
|
||||||
else -> stringResource(R.string.status_unknown)
|
"Not Supported" -> stringResource(R.string.status_not_supported)
|
||||||
}
|
else -> stringResource(R.string.status_unknown)
|
||||||
|
}
|
||||||
|
|
||||||
Text(
|
Text(
|
||||||
text = stringResource(R.string.home_susfs, translatedStatus),
|
text = stringResource(R.string.home_susfs, translatedStatus),
|
||||||
style = MaterialTheme.typography.bodyMedium
|
style = MaterialTheme.typography.bodyMedium
|
||||||
)
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -346,7 +346,11 @@ fun ModuleScreen(navigator: DestinationsNavigator) {
|
|||||||
floatingActionButton = {
|
floatingActionButton = {
|
||||||
if (!hideInstallButton) {
|
if (!hideInstallButton) {
|
||||||
val moduleInstall = stringResource(id = R.string.module_install)
|
val moduleInstall = stringResource(id = R.string.module_install)
|
||||||
val cardColor = MaterialTheme.colorScheme.secondaryContainer
|
val cardColor = if (!ThemeConfig.useDynamicColor) {
|
||||||
|
ThemeConfig.currentTheme.ButtonContrast
|
||||||
|
} else {
|
||||||
|
MaterialTheme.colorScheme.secondaryContainer
|
||||||
|
}
|
||||||
ExtendedFloatingActionButton(
|
ExtendedFloatingActionButton(
|
||||||
onClick = {
|
onClick = {
|
||||||
selectZipLauncher.launch(
|
selectZipLauncher.launch(
|
||||||
|
|||||||
@@ -140,7 +140,6 @@ fun SettingScreen(navigator: DestinationsNavigator) {
|
|||||||
loadingDialog.hide()
|
loadingDialog.hide()
|
||||||
snackBarHost.showSnackbar(context.getString(R.string.log_saved))
|
snackBarHost.showSnackbar(context.getString(R.string.log_saved))
|
||||||
}
|
}
|
||||||
// endregion
|
|
||||||
}
|
}
|
||||||
// region 配置项列表
|
// region 配置项列表
|
||||||
// 配置文件模板入口
|
// 配置文件模板入口
|
||||||
@@ -218,12 +217,12 @@ fun SettingScreen(navigator: DestinationsNavigator) {
|
|||||||
prefs.edit { putBoolean("enable_web_debugging", it) }
|
prefs.edit { putBoolean("enable_web_debugging", it) }
|
||||||
enableWebDebugging = it
|
enableWebDebugging = it
|
||||||
}
|
}
|
||||||
// endregion
|
// 更多设置
|
||||||
val newButtonTitle = stringResource(id = R.string.more_settings)
|
val newButtonTitle = stringResource(id = R.string.more_settings)
|
||||||
ListItem(
|
ListItem(
|
||||||
leadingContent = {
|
leadingContent = {
|
||||||
Icon(
|
Icon(
|
||||||
Icons.Filled.ExpandMore,
|
Icons.Filled.Settings,
|
||||||
contentDescription = newButtonTitle
|
contentDescription = newButtonTitle
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -60,6 +60,7 @@ import com.ramcosta.composedestinations.result.getOr
|
|||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import shirkneko.zako.sukisu.R
|
import shirkneko.zako.sukisu.R
|
||||||
|
import shirkneko.zako.sukisu.ui.theme.ThemeConfig
|
||||||
import shirkneko.zako.sukisu.ui.viewmodel.TemplateViewModel
|
import shirkneko.zako.sukisu.ui.viewmodel.TemplateViewModel
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -77,7 +78,11 @@ fun AppProfileTemplateScreen(
|
|||||||
val viewModel = viewModel<TemplateViewModel>()
|
val viewModel = viewModel<TemplateViewModel>()
|
||||||
val scope = rememberCoroutineScope()
|
val scope = rememberCoroutineScope()
|
||||||
val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState())
|
val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState())
|
||||||
val cardColor = MaterialTheme.colorScheme.secondaryContainer
|
val cardColor = if (!ThemeConfig.useDynamicColor) {
|
||||||
|
ThemeConfig.currentTheme.ButtonContrast
|
||||||
|
} else {
|
||||||
|
MaterialTheme.colorScheme.secondaryContainer
|
||||||
|
}
|
||||||
|
|
||||||
LaunchedEffect(Unit) {
|
LaunchedEffect(Unit) {
|
||||||
if (viewModel.templateList.isEmpty()) {
|
if (viewModel.templateList.isEmpty()) {
|
||||||
|
|||||||
@@ -43,23 +43,23 @@ object ThemeConfig {
|
|||||||
@Composable
|
@Composable
|
||||||
private fun getDarkColorScheme() = darkColorScheme(
|
private fun getDarkColorScheme() = darkColorScheme(
|
||||||
primary = ThemeConfig.currentTheme.Primary.copy(alpha = 0.8f),
|
primary = ThemeConfig.currentTheme.Primary.copy(alpha = 0.8f),
|
||||||
onPrimary = Color.White,
|
onPrimary = mixColors(ThemeConfig.currentTheme.Primary, Color.White, 0.2f),
|
||||||
primaryContainer = ThemeConfig.currentTheme.PrimaryContainer.copy(alpha = 0.15f),
|
primaryContainer = ThemeConfig.currentTheme.PrimaryContainer.copy(alpha = 0.15f),
|
||||||
onPrimaryContainer = Color.White,
|
onPrimaryContainer = mixColors(ThemeConfig.currentTheme.Primary, Color.White, 0.2f),
|
||||||
secondary = ThemeConfig.currentTheme.Secondary.copy(alpha = 0.8f),
|
secondary = ThemeConfig.currentTheme.Secondary.copy(alpha = 0.8f),
|
||||||
onSecondary = Color.White,
|
onSecondary = mixColors(ThemeConfig.currentTheme.Secondary, Color.White, 0.2f),
|
||||||
secondaryContainer = ThemeConfig.currentTheme.SecondaryContainer.copy(alpha = 0.15f),
|
secondaryContainer = ThemeConfig.currentTheme.SecondaryContainer.copy(alpha = 0.15f),
|
||||||
onSecondaryContainer = Color.White,
|
onSecondaryContainer = mixColors(ThemeConfig.currentTheme.Secondary, Color.White, 0.2f),
|
||||||
tertiary = ThemeConfig.currentTheme.Tertiary.copy(alpha = 0.8f),
|
tertiary = ThemeConfig.currentTheme.Tertiary.copy(alpha = 0.8f),
|
||||||
onTertiary = Color.White,
|
onTertiary = mixColors(ThemeConfig.currentTheme.Tertiary, Color.White, 0.2f),
|
||||||
tertiaryContainer = ThemeConfig.currentTheme.TertiaryContainer.copy(alpha = 0.15f),
|
tertiaryContainer = ThemeConfig.currentTheme.TertiaryContainer.copy(alpha = 0.15f),
|
||||||
onTertiaryContainer = Color.White,
|
onTertiaryContainer = mixColors(ThemeConfig.currentTheme.Tertiary, Color.White, 0.2f),
|
||||||
background = Color.Transparent,
|
background = Color.Transparent,
|
||||||
surface = Color.Transparent,
|
surface = Color.Transparent,
|
||||||
onBackground = Color.White.copy(alpha = 0.87f),
|
onBackground = mixColors(ThemeConfig.currentTheme.Primary, Color.White, 0.1f),
|
||||||
onSurface = Color.White.copy(alpha = 0.87f),
|
onSurface = mixColors(ThemeConfig.currentTheme.Primary, Color.White, 0.1f),
|
||||||
surfaceVariant = Color(0xFF2F2F2F),
|
surfaceVariant = Color(0xFF2F2F2F),
|
||||||
onSurfaceVariant = Color.White.copy(alpha = 0.78f),
|
onSurfaceVariant = mixColors(ThemeConfig.currentTheme.Primary, Color.White, 0.2f),
|
||||||
outline = Color.White.copy(alpha = 0.12f),
|
outline = Color.White.copy(alpha = 0.12f),
|
||||||
outlineVariant = Color.White.copy(alpha = 0.12f)
|
outlineVariant = Color.White.copy(alpha = 0.12f)
|
||||||
)
|
)
|
||||||
@@ -339,4 +339,12 @@ private fun adjustColor(color: Color): Color {
|
|||||||
luminance = maxLuminance
|
luminance = maxLuminance
|
||||||
}
|
}
|
||||||
return color.copy(luminance)
|
return color.copy(luminance)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun mixColors(color1: Color, color2: Color, ratio: Float): Color {
|
||||||
|
val r = (color1.red * ratio + color2.red * (1 - ratio))
|
||||||
|
val g = (color1.green * ratio + color2.green * (1 - ratio))
|
||||||
|
val b = (color1.blue * ratio + color2.blue * (1 - ratio))
|
||||||
|
val a = (color1.alpha * ratio + color2.alpha * (1 - ratio))
|
||||||
|
return Color(r, g, b, a)
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user