Compare commits

...

51 Commits

Author SHA1 Message Date
Rifat Azad
d1aad01df3 manager: new zygisk detection method implemented and deprecated old method
Currently only supports ReZygisk and soon ZygiskNext will hopefully follow (ZN users will have issues detecting zygisk injections until then)
2025-07-01 22:21:40 +06:00
Hinata Saine
9733b92d30 Do not translate SELinux status in Japanese (#590) 2025-07-01 15:28:25 +06:00
Pedro.js
aaf776f421 manager: fix integer underflow in get_manager_uid (#591)
This commit fixes the issue where the "uid_t" type variable, typedef'd from unsigned int, was being assigned -1, causing an integer underflow.
2025-06-30 22:17:15 +06:00
Rifat Azad
5e33aee99f manager: fix pull to refresh handling in ModuleList 2025-06-30 19:08:14 +06:00
Rifat Azad
4e3f06d405 kernel: added new prctl CMD_GET_MANAGER_UID to get the uid of the crowned manager
manager: show crowned manager uid in infocard when developer options is enabled
2025-06-30 14:22:19 +06:00
роизен
98b9863041 Update README_UA.md (#577)
Co-authored-by: Rifat Azad <33044977+rifsxd@users.noreply.github.com>
2025-06-30 13:22:45 +06:00
luigimak
29ae76d1fb Update and fix Italian translation (#583) 2025-06-30 13:21:13 +06:00
igor
2c3841558e Update translations (#580) 2025-06-30 13:20:55 +06:00
Juno Bủh
c8b357e31b Update Vietnamese Translation (#578)
* Update Vietnamese Translation

* Update Vietnamese Translation

* Update Vietnamese Strings

* Update Vietnamese Translation
2025-06-30 13:20:43 +06:00
AxelPLN(Axel Yinjia Huang)
3ad02ff50b fix: update translation for zh-rCH & zh-rTW (#575)
* docs: sync README_CN & README_TW with en

* fix: update manager translation for zh-rCN & zh-rTW

* docs: fixed some text issue

* fix: update translation about su compatibility
2025-06-30 13:20:23 +06:00
mr_vokintos
6a54b30a9d Update Russian (#574)
* Update Russian

again

* Update strings.xml
2025-06-30 13:20:06 +06:00
роизен
b0cb3bb4c2 Update Ukranian (#573)
* Update Uk

* Update strings.xml
2025-06-30 13:19:47 +06:00
Rifat Azad
7f957be99b manager: fix ListItem title size 2025-06-29 20:10:31 +06:00
Rifat Azad
a54c319d55 manager: improve padding 2025-06-29 18:50:04 +06:00
Rifat Azad
2c71531533 manager: UI overhaul 2025-06-29 15:31:17 +06:00
Rifat Azad
5c61a70e5a manager: fix disable back instance if flashing.status is flashing 2025-06-29 00:05:51 +06:00
Rifat Azad
fdf1d61735 manager: remove unused lib of mmrl and pro guard rules 2025-06-28 20:21:14 +06:00
Rifat Azad
93dc61e113 manager: change FAB visibility with scrolling gestures on module screen list 2025-06-28 19:45:32 +06:00
Rifat Azad
d80a3ebcda manager: removed deprecated webuix and allow superuser and module management capabilities even with su compat disabled 2025-06-28 17:17:49 +06:00
igor
4270fd8b1e update translations in docs and manager (#571)
Co-authored-by: Rifat Azad <33044977+rifsxd@users.noreply.github.com>
2025-06-27 22:51:05 +06:00
роизен
764dbc3782 Improve README_UA (#569)
* Improve README_UA

* Update README_UA.md
2025-06-27 22:48:49 +06:00
роизен
080ab9a952 Update Uk (#566) 2025-06-27 22:48:32 +06:00
AxelPLN(Axel Yinjia Huang)
a9cab5ccfd fix: complete the text of the module labels (#558)
- replace the string Resource ID of uninstalled & those can be updated modules
2025-06-27 22:48:18 +06:00
Rifat Azad
3d44602537 manager: check additional zygisk 64 libs instead of only 32 bit lib for zygisk detection 2025-06-27 21:13:25 +06:00
Eren
88eb2a2723 Improve README (#562)
* Improve README

* Update README.md
2025-06-27 18:39:33 +06:00
NkBe
e272e557b0 manger: fix lkm detection (#2654)
原因请看
https://github.com/SukiSU-Ultra/SukiSU-Ultra/pull/217#issuecomment-3004461174

文中介绍的是lkm的问题 但实测下来gki也有这样的问题 但修复方法通用
2025-06-27 18:02:42 +06:00
Rifat Azad
2a4794e422 manager: implemented zygisk required label for module card 2025-06-27 18:02:34 +06:00
mr_vokintos
818bdbead6 Update Russian (#557) 2025-06-27 14:37:54 +06:00
Nhật Minh
76249fa67d Update strings.xml (#553) 2025-06-27 14:37:39 +06:00
AxelPLN(Axel Yinjia Huang)
ed50b57b57 fix: update manager translation for zh-rCN & zh-rTW (#556)
* docs: complete text for README_CN & README_TW

* fix: update manager translation for zh-rCN & zh-rTW

also complete some text for module label and language switching functionality
2025-06-27 14:37:10 +06:00
Rifat Azad
f05f776a08 manager: fix Condition is always 'true' 2025-06-27 12:02:16 +06:00
Axel Yinjia Huang
c9b79c3016 manager: add basic language switching functionality 2025-06-27 11:37:12 +06:00
Rifat Azad
3ff10d6622 susfsd: remove deprecated CONFIG_KSU_SUSFS_SUS_OVERLAYFS and add CONFIG_KSU_SUSFS_HAS_MAGIC_MOUNT 2025-06-26 23:55:29 +06:00
Rifat Azad
b95d2b69b6 manager: add vendor_boot suggestion for LKM patch 2025-06-26 23:44:46 +06:00
Rifat Azad
2cd8453877 Revert "manager: move the wx platform init to the Application class so it starts as soon as the app process launches"
This reverts commit d5c4f85d73.
2025-06-26 23:25:37 +06:00
роизен
b7300b0525 Update translation for UK (#552) 2025-06-26 22:02:09 +06:00
kam821
3a278d560f Update Polish translation (#521)
* Update Polish translation

- Improve cosmetics / context-sensitive lines

* Update Polish translation - backup/restore

- Cosmetics, worth considering changing it in the future to keep option name in one line.

* Update Polish translation - homepage

- Shortened translation to work around broken formatting
2025-06-26 20:01:55 +06:00
igor
03fa2eddb2 Update portuguese translation (#518) 2025-06-26 17:43:27 +06:00
Rifat Azad
b74e953ad2 ksud: implement patching LKM vendor_ramdisk/ramdisk.cpio for compatibily needed for some pixel devices 2025-06-26 10:33:58 +06:00
Rifat Azad
39717b0a3f Revert "ksud: rust FMT"
This reverts commit 78eb3b0b22.
2025-06-25 17:58:52 +06:00
Rifat Azad
7f0eccd3d5 ksud: handle errors and non compatible ramdisk 2025-06-25 17:43:59 +06:00
Rifat Azad
78eb3b0b22 ksud: rust FMT 2025-06-25 13:25:28 +06:00
Rifat Azad
39f20bf573 manager: fix uneven spacing for empty label for superuser app section with DEFAULT label (fix #547) 2025-06-25 12:54:13 +06:00
Rifat Azad
092eb1b23d manager: use busybox for tar and du commands as not all devices has it most likely and if module size is 0 bytes then show null 2025-06-24 17:56:50 +06:00
Rifat Azad
30e2ed5db5 ksud: third test properly check if vendor is already patched or not for lkm restoration and also handle magisk patched vendor boot 2025-06-24 16:47:44 +06:00
Rifat Azad
dc7ae2db5f manager: fix status card startActivity intent fallback to not allow lkm install on non gki 2025-06-24 09:53:20 +06:00
Rifat Azad
b3b7ef1cb3 manager: dont show developer options and banner toggle when ksuversion is null 2025-06-24 09:26:14 +06:00
Rifat Azad
4de4d1e091 ksud: second test of vendor_boot patching now handling vendor_boot partition and restore partition 2025-06-24 09:17:24 +06:00
Rifat Azad
0beea57ab7 ksud: test vendor_boot patching for some newer devices 2025-06-23 18:17:46 +06:00
Rifat Azad
49aee1ff4c manager: remove susfs info from status card and merge it to info card 2025-06-21 17:01:21 +06:00
Rifat Azad
f7a3699fe3 manager: show module update count as badge in bottom bar instead of status card 2025-06-21 12:44:29 +06:00
58 changed files with 2371 additions and 1225 deletions

View File

@@ -1,63 +1,89 @@
**English** | [简体中文](README_CN.md) | [繁體中文](README_TW.md) | [Türkçe](README_TR.md) | [Português (Brasil)](README_PT-BR.md) | [한국어](README_KO.md) | [Français](README_FR.md) | [Bahasa Indonesia](README_ID.md) | [Русский](README_RU.md) | [ภาษาไทย](README_TH.md) | [Tiếng Việt](README_VI.md) | [Italiano](README_IT.md) | [Polski](README_PL.md) | [Български](README_BG.md) | [日本語](README_JA.md) | [Українська](README_UA.md)
**English** | [简体中文](README_CN.md) | [繁體中文](README_TW.md) | [Türkçe](README_TR.md) | [Português (Brasil)](README_PT-BR.md) | [한국어](README_KO.md) | [Français](README_FR.md) | [Bahasa Indonesia](README_ID.md) | [Русский](README_RU.md) | [Українська](README_UA.md) | [ภาษาไทย](README_TH.md) | [Tiếng Việt](README_VI.md) | [Italiano](README_IT.md) | [Polski](README_PL.md) | [Български](README_BG.md) | [日本語](README_JA.md)
# KernelSU Next
---
<img src="/assets/kernelsu_next.png" style="width: 96px;" alt="logo">
<div align="center">
<img src="/assets/kernelsu_next.png" width="96" alt="KernelSU Next Logo">
A kernel-based root solution for Android devices.
<h2>KernelSU Next</h2>
<p><strong>A kernel-based root solution for Android devices.</strong></p>
[![Latest Release](https://img.shields.io/github/v/release/KernelSU-Next/KernelSU-Next?label=Release&logo=github)](https://github.com/KernelSU-Next/KernelSU-Next/releases/latest)
[![Nightly Release](https://img.shields.io/badge/Nightly%20Release-gray?logo=hackthebox&logoColor=fff)](https://nightly.link/KernelSU-Next/KernelSU-Next/workflows/build-manager-ci/next/Manager)
[![License: GPL v2](https://img.shields.io/badge/License-GPL%20v2-orange.svg?logo=gnu)](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html)
[![GitHub License](https://img.shields.io/github/license/KernelSU-Next/KernelSU-Next?logo=gnu)](/LICENSE)
<p>
<a href="https://github.com/KernelSU-Next/KernelSU-Next/releases/latest">
<img src="https://img.shields.io/github/v/release/KernelSU-Next/KernelSU-Next?label=Release&logo=github" alt="Latest Release">
</a>
<a href="https://nightly.link/KernelSU-Next/KernelSU-Next/workflows/build-manager-ci/next/Manager">
<img src="https://img.shields.io/badge/Nightly%20Release-gray?logo=hackthebox&logoColor=fff" alt="Nightly Build">
</a>
<a href="https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html">
<img src="https://img.shields.io/badge/License-GPL%20v2-orange.svg?logo=gnu" alt="License: GPL v2">
</a>
<a href="/LICENSE">
<img src="https://img.shields.io/github/license/KernelSU-Next/KernelSU-Next?logo=gnu" alt="GitHub License">
</a>
</p>
</div>
## Features
---
1. Kernel-based `su` and root access management.
2. Module system based on dynamic mount system [Magic Mount](https://topjohnwu.github.io/Magisk/details.html#magic-mount) / [OverlayFS](https://en.wikipedia.org/wiki/OverlayFS).
3. [App Profile](https://kernelsu.org/guide/app-profile.html): Lock up the root power in a cage.
## 🚀 Features
## Compatibility state
- Kernel-based `su` and root access management.
- Module system based on [Magic Mount](https://topjohnwu.github.io/Magisk/details.html#magic-mount) and [OverlayFS](https://en.wikipedia.org/wiki/OverlayFS).
- [App Profile](https://kernelsu.org/guide/app-profile.html): Limit root privileges per app.
KernelSU Next officially supports most Android kernels starting from 4.4 up to 6.6.
- GKI 2.0 (5.10+) kernels can run pre-built images and LKM/KMI.
- GKI 1.0 (4.19 - 5.4) kernels need to rebuilt with KernelSU driver.
- EOL (<4.14) kernels also need to be rebuilt with KernelSU driver (3.18+ is experimental and may need some function backports).
---
Currently, only the `arm64-v8a`, `armeabi-v7a` & `x86_64` architectures are supported.
## ✅ Compatibility
## Usage
KernelSU Next supports Android kernels from **4.4 up to 6.6**:
- [Installation instruction](https://kernelsu-next.github.io/webpage/pages/installation.html)
| Kernel version | Support notes |
|----------------------|-------------------------------------------------------------------------|
| 5.10+ (GKI 2.0) | Supports pre-built images and LKM/KMI |
| 4.19 5.4 (GKI 1.0) | Requires KernelSU driver built-in |
| < 4.14 (EOL) | Requires KernelSU driver (3.18+ is experimental and may need backports) |
## Security
**Supported architectures:** `arm64-v8a`, `armeabi-v7a` and `x86_64`
For information on reporting security vulnerabilities in KernelSU, see [SECURITY.md](/SECURITY.md).
---
## License
## 📦 Installation
- Files under the `kernel` directory are [GPL-2.0-only](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html).
- All other parts except the `kernel` directory are [GPL-3.0-or-later](https://www.gnu.org/licenses/gpl-3.0.html).
Please refer to the [Installation](https://kernelsu-next.github.io/webpage/pages/installation.html) guide for setup instructions.
## Donations
---
- 0x12b5224b7aca0121c2f003240a901e1d064371c1 [ USDT BEP20 ]
## 🔐 Security
- TYUVMWGTcnR5svnDoX85DWHyqUAeyQcdjh [ USDT TRC20 ]
To report security issues, please see [SECURITY.md](/SECURITY.md).
- 0x12b5224b7aca0121c2f003240a901e1d064371c1 [ USDT ERC20 ]
---
- 0x12b5224b7aca0121c2f003240a901e1d064371c1 [ ETH ERC20 ]
## 📜 License
- Ld238uYBuRQdZB5YwdbkuU6ektBAAUByoL [ LTC ]
- **`/kernel` directory:** [GPL-2.0-only](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html).
- **All other files:** [GPL-3.0-or-later](https://www.gnu.org/licenses/gpl-3.0.html).
- 19QgifcjMjSr1wB2DJcea5cxitvWVcXMT6 [ BTC ]
---
## Credits
## 💸 Donations
- [Kernel-Assisted Superuser](https://git.zx2c4.com/kernel-assisted-superuser/about/): The KernelSU idea.
- [Magisk](https://github.com/topjohnwu/Magisk): The powerful root tool.
- [genuine](https://github.com/brevent/genuine/): APK v2 signature validation.
- [Diamorphine](https://github.com/m0nad/Diamorphine): Some rootkit skills.
- [KernelSU](https://github.com/tiann/KernelSU): Thanks to tiann, or else KernelSU Next wouldn't even exist.
- [Magic Mount Port](https://github.com/5ec1cff/KernelSU/blob/main/userspace/ksud/src/magic_mount.rs): 💜 5ec1cff for saving KernelSU!
If youd like to support the project:
- **USDT (BEP20, ERC20)**: `0x12b5224b7aca0121c2f003240a901e1d064371c1`
- **USDT (TRC20)**: `TYUVMWGTcnR5svnDoX85DWHyqUAeyQcdjh`
- **ETH (ERC20)**: `0x12b5224b7aca0121c2f003240a901e1d064371c1`
- **LTC**: `Ld238uYBuRQdZB5YwdbkuU6ektBAAUByoL`
- **BTC**: `19QgifcjMjSr1wB2DJcea5cxitvWVcXMT6`
---
## 🙏 Credits
- [Kernel-Assisted Superuser](https://git.zx2c4.com/kernel-assisted-superuser/about/) Concept inspiration
- [Magisk](https://github.com/topjohnwu/Magisk) Core root implementation
- [Genuine](https://github.com/brevent/genuine/) APK v2 signature validation
- [Diamorphine](https://github.com/m0nad/Diamorphine) Rootkit techniques
- [KernelSU](https://github.com/tiann/KernelSU) The original base that made KernelSU Next possible
- [Magic Mount Port](https://github.com/5ec1cff/KernelSU/blob/main/userspace/ksud/src/magic_mount.rs) 💜 to 5ec1cff for keeping KernelSU alive

View File

@@ -1,49 +1,90 @@
[English](README.md) | **简体中文** | [繁體中文](README_TW.md) | [Türkçe](README_TR.md) | [Português (Brasil)](README_PT-BR.md) | [한국어](README_KO.md) | [Français](README_FR.md) | [Bahasa Indonesia](README_ID.md) | [Русский](README_RU.md) | [Український](README_UA.md) | [ภาษาไทย](README_TH.md) | [Tiếng Việt](README_VI.md) | [Italiano](README_IT.md) | [Polski](README_PL.md) | [Български](README_BG.md) | [日本語](README_JA.md)
# KernelSU Next
---
<img src="/assets/kernelsu_next.png" style="width: 96px;" alt="logo">
<div align="center">
<img src="/assets/kernelsu_next.png" width="96" alt="KernelSU Next Logo">
<h2>KernelSU Next</h2>
<p><strong>安卓设备基于内核的 Root 方案</strong></p>
安卓基于内核的 Root 方案
<p>
<a href="https://github.com/KernelSU-Next/KernelSU-Next/releases/latest">
<img src="https://img.shields.io/github/v/release/KernelSU-Next/KernelSU-Next?label=Release&logo=github" alt="Latest Release">
</a>
<a href="https://nightly.link/KernelSU-Next/KernelSU-Next/workflows/build-manager-ci/next/Manager">
<img src="https://img.shields.io/badge/Nightly%20Release-gray?logo=hackthebox&logoColor=fff" alt="Nightly Build">
</a>
<a href="https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html">
<img src="https://img.shields.io/badge/License-GPL%20v2-orange.svg?logo=gnu" alt="License: GPL v2">
</a>
<a href="/LICENSE">
<img src="https://img.shields.io/github/license/KernelSU-Next/KernelSU-Next?logo=gnu" alt="GitHub License">
</a>
</p>
</div>
[![Latest Release](https://img.shields.io/github/v/release/KernelSU-Next/KernelSU-Next?label=Release&logo=github)](https://github.com/KernelSU-Next/KernelSU-Next/releases/latest)
[![Nightly Release](https://img.shields.io/badge/Nightly%20Release-gray?logo=hackthebox&logoColor=fff)](https://nightly.link/KernelSU-Next/KernelSU-Next/workflows/build-manager-ci/next/Manager)
[![License: GPL v2](https://img.shields.io/badge/License-GPL%20v2-orange.svg?logo=gnu)](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html)
[![GitHub License](https://img.shields.io/github/license/KernelSU-Next/KernelSU-Next?logo=gnu)](/LICENSE)
---
## 特性
## 🚀 特性
1. 基于内核的 `SU` 和权限管理
2. 基于动态挂载系统 [Magic Mount](https://topjohnwu.github.io/Magisk/details.html#magic-mount) / [OverlayFS](https://en.wikipedia.org/wiki/OverlayFS) 的模块系统。
3. [App Profile](https://kernelsu.org/zh_CN/guide/app-profile.html):把 Root 权限关进笼子里
- 基于内核的 `su`超级用户权限管理
- 动态挂载系统基于 **[Magic Mount](https://topjohnwu.github.io/Magisk/details.html#magic-mount)** 以及 **[OverlayFS](https://en.wikipedia.org/wiki/OverlayFS)**
- [App Profile](https://kernelsu.org/zh_CN/guide/app-profile.html):把 Root 权限关进笼子里
## 兼容状态
---
KernelSU Next 支持从 4.4 到 6.6 的大多数安卓内核
- GKI 2.05.10+)内核可运行预置镜像和 LKM/KMI
- GKI 1.04.19 - 5.4)内核需要使用 KernelSU 内核驱动重新编译
- EOL (<4.14) 内核也需要使用 KernelSU 内核驱动重新编译 (3.18+ 的版本处于试验阶段,可能需要移植一些功能)
## ✅ 兼容性
目前只支持 `arm64-v8a`, `armeabi-v7a` & `x86_64` 架构
KernelSU Next 支持从 **4.4 到 6.6** 的大多数安卓内核
## 用法
| 内核版本 | 支持情况 |
|----------------|---------------|
| 5.10+ (GKI 2.0) | 可运行预置镜像和 LKM/KMI |
| 4.19 5.4 (GKI 1.0) | 需要使用 KernelSU 内核驱动重新编译 |
| <4.14 (EOL) | 需要使用 KernelSU 内核驱动重新编译3.18+ 的版本处于试验阶段,可能需要进行回溯移植) |
- [安装说明](https://ksunext.org/pages/installation.html)
**支持的架构:**
`arm64-v8a``armeabi-v7a``x86_64`
## 安全性
---
## 📦 安装
请遵循该[安装说明](https://kernelsu-next.github.io/webpage/zh_CN/pages/installation.html)进行操作。
---
## 🔐 安全性
有关报告 KernelSU Next 漏洞的信息,请参阅 [SECURITY.md](/SECURITY.md).
## 许可证
---
- 目录 `kernel` 下所有文件为 [GPL-2.0-only](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html)
- `kernel` 目录以外的其他部分均为 [GPL-3.0-or-later](https://www.gnu.org/licenses/gpl-3.0.html)
## 📜 许可
## 鸣谢
- **目录 `/kernel` 下所有文件**为 [GPL-2.0-only](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html)
- **`/kernel` 目录以外的其他部分**均为 [GPL-3.0-or-later](https://www.gnu.org/licenses/gpl-3.0.html)
- [Kernel-Assisted Superuser](https://git.zx2c4.com/kernel-assisted-superuser/about/): KernelSU 的灵感.
- [Magisk](https://github.com/topjohnwu/Magisk): 强大的 Root 工具.
- [genuine](https://github.com/brevent/genuine/): APK v2 签名验证。
- [Diamorphine](https://github.com/m0nad/Diamorphine): 一些 Rootkit 技巧。
- [KernelSU](https://github.com/tiann/KernelSU): 感谢 tiann否则 KernelSU Next 根本不会存在。
- [Magic Mount Port](https://github.com/5ec1cff/KernelSU/blob/main/userspace/ksud/src/magic_mount.rs): 💜 5ec1cff 為了拯救 KernelSU
---
## 💸 捐赠
如果你喜欢这个项目还请支持:
- **USDT (BEP20, ERC20)**: `0x12b5224b7aca0121c2f003240a901e1d064371c1`
- **USDT (TRC20)**: `TYUVMWGTcnR5svnDoX85DWHyqUAeyQcdjh`
- **ETH (ERC20)**: `0x12b5224b7aca0121c2f003240a901e1d064371c1`
- **LTC**: `Ld238uYBuRQdZB5YwdbkuU6ektBAAUByoL`
- **BTC**: `19QgifcjMjSr1wB2DJcea5cxitvWVcXMT6`
---
## 🙏 鸣谢
- [Kernel-Assisted Superuser](https://git.zx2c4.com/kernel-assisted-superuser/about/)KernelSU 的灵感.
- [Magisk](https://github.com/topjohnwu/Magisk):强大的 Root 工具.
- [genuine](https://github.com/brevent/genuine/)APK v2 签名验证。
- [Diamorphine](https://github.com/m0nad/Diamorphine):一些 Rootkit 技巧。
- [KernelSU](https://github.com/tiann/KernelSU):感谢 tiann否则 KernelSU Next 根本不会存在。
- [Magic Mount Port](https://github.com/5ec1cff/KernelSU/blob/main/userspace/ksud/src/magic_mount.rs):💜 5ec1cff 为了拯救 KernelSU

View File

@@ -1,63 +1,89 @@
[English](README.md) | [简体中文](README_CN.md) | [繁體中文](README_TW.md) | [Türkçe](README_TR.md) | **Português (Brasil)** | [한국어](README_KO.md) | [Français](README_FR.md) | [Bahasa Indonesia](README_ID.md) | [Русский](README_RU.md) | [Український](README_UA.md) | [ภาษาไทย](README_TH.md) | [Tiếng Việt](README_VI.md) | [Italiano](README_IT.md) | [Polski](README_PL.md) | [Български](README_BG.md) | [日本語](README_JA.md)
# KernelSU Next
---
<img src="/assets/kernelsu_next.png" style="width: 96px;" alt="logo">
<div align="center">
<img src="/assets/kernelsu_next.png" width="96" alt="KernelSU Next Logo">
Uma solução root baseada em kernel para dispositivos Android.
<h2>KernelSU Next</h2>
<p><strong>Uma solução root baseada em kernel para dispositivos Android.</strong></p>
[![Latest Release](https://img.shields.io/github/v/release/KernelSU-Next/KernelSU-Next?label=Release&logo=github)](https://github.com/KernelSU-Next/KernelSU-Next/releases/latest)
[![Nightly Release](https://img.shields.io/badge/Nightly%20Release-gray?logo=hackthebox&logoColor=fff)](https://nightly.link/KernelSU-Next/KernelSU-Next/workflows/build-manager-ci/next/Manager)
[![License: GPL v2](https://img.shields.io/badge/License-GPL%20v2-orange.svg?logo=gnu)](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html)
[![GitHub License](https://img.shields.io/github/license/KernelSU-Next/KernelSU-Next?logo=gnu)](/LICENSE)
<p>
<a href="https://github.com/KernelSU-Next/KernelSU-Next/releases/latest">
<img src="https://img.shields.io/github/v/release/KernelSU-Next/KernelSU-Next?label=Release&logo=github" alt="Latest Release">
</a>
<a href="https://nightly.link/KernelSU-Next/KernelSU-Next/workflows/build-manager-ci/next/Manager">
<img src="https://img.shields.io/badge/Nightly%20Release-gray?logo=hackthebox&logoColor=fff" alt="Nightly Build">
</a>
<a href="https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html">
<img src="https://img.shields.io/badge/License-GPL%20v2-orange.svg?logo=gnu" alt="License: GPL v2">
</a>
<a href="/LICENSE">
<img src="https://img.shields.io/github/license/KernelSU-Next/KernelSU-Next?logo=gnu" alt="GitHub License">
</a>
</p>
</div>
## Características
---
1. `su` e gerenciamento de acesso root baseado em kernel.
2. Sistema de módulos baseado em sistema de montagem dinâmica [Magic Mount](https://topjohnwu.github.io/Magisk/details.html#magic-mount) / [OverlayFS](https://en.wikipedia.org/wiki/OverlayFS).
3. [Perfil do Aplicativo](https://kernelsu.org/pt_BR/guide/app-profile.html): Tranque o poder root em uma gaiola.
## 🚀 Características
## Estado de compatibilidade
- `su` e gerenciamento de acesso root baseado em kernel.
- Sistema de módulos baseado em [Magic Mount](https://topjohnwu.github.io/Magisk/details.html#magic-mount) e [OverlayFS](https://en.wikipedia.org/wiki/OverlayFS).
- [Perfil do app](https://kernelsu.org/pt_BR/guide/app-profile.html): Limitar privilégios root por app.
KernelSU Next suporta oficialmente a maioria dos kernels Android a partir de 4.4 até 6.6.
- Os kernels GKI 2.0 (5.10+) podem executar imagens pré-construídas e LKM/KMI.
- Os kernels GKI 1.0 (4.19 - 5.4) precisam ser reconstruídos com o driver KernelSU.
- Os kernels EOL (<4.14) também precisam ser reconstruídos com o driver KernelSU (3.18+ é experimental e pode precisar portar algumas funções).
---
Atualmente, apenas as arquiteturas `arm64-v8a`, `armeabi-v7a` & `x86_64` são compatíveis.
## ✅ Compatibilidade
## Uso
O KernelSU Next oferece suporte a kernels Android **4.4 até 6.6**:
- [Instruções de instalação](https://ksunext.org/pages/installation.html)
| Versão do kernel | Notas de suporte |
|----------------------|-------------------------------------------------------------------------------|
| 5.10+ (GKI 2.0) | Suporta imagens pré-compiladas e LKM/KMI |
| 4.19 5.4 (GKI 1.0) | Requer driver do KernelSU integrado |
| < 4.14 (EOL) | Requer driver do KernelSU (3.18+ é experimental e pode precisar de backports) |
## Segurança
**Arquiteturas suportadas:** `arm64-v8a`, `armeabi-v7a` e `x86_64`
Para obter informações sobre como relatar vulnerabilidades de segurança do KernelSU, consulte [SECURITY.md](/SECURITY.md).
---
## Licença
## 📦 Instalação
- Os arquivos no diretório `kernel` são [GPL-2.0-only](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html).
- Todas as outras partes, exceto o diretório `kernel` são [GPL-3.0-or-later](https://www.gnu.org/licenses/gpl-3.0.html).
Consulte o guia de [Instalação](https://kernelsu-next.github.io/webpage/pt_BR/pages/installation.html) para obter instruções de configuração.
## Doações
---
- 0x12b5224b7aca0121c2f003240a901e1d064371c1 [ USDT BEP20 ]
## 🔐 Segurança
- TYUVMWGTcnR5svnDoX85DWHyqUAeyQcdjh [ USDT TRC20 ]
Para relatar problemas de segurança, consulte [SECURITY.md](/SECURITY.md).
- 0x12b5224b7aca0121c2f003240a901e1d064371c1 [ USDT ERC20 ]
---
- 0x12b5224b7aca0121c2f003240a901e1d064371c1 [ ETH ERC20 ]
## 📜 Licença
- Ld238uYBuRQdZB5YwdbkuU6ektBAAUByoL [ LTC ]
- **Diretório `/kernel`:** [GPL-2.0-only](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html).
- **Todos os outros arquivos:** [GPL-3.0-or-later](https://www.gnu.org/licenses/gpl-3.0.html).
- 19QgifcjMjSr1wB2DJcea5cxitvWVcXMT6 [ BTC ]
---
## Créditos
## 💸 Doações
- [Kernel-Assisted Superuser](https://git.zx2c4.com/kernel-assisted-superuser/about/): A ideia do KernelSU.
- [Magisk](https://github.com/topjohnwu/Magisk): A poderosa ferramenta root.
- [genuine](https://github.com/brevent/genuine/): Validação de assinatura APK v2.
- [Diamorphine](https://github.com/m0nad/Diamorphine): Algumas habilidades de rootkit.
- [KernelSU](https://github.com/tiann/KernelSU): Obrigado ao tiann, ou então o KernelSU Next nem existiria.
- [Magic Mount Port](https://github.com/5ec1cff/KernelSU/blob/main/userspace/ksud/src/magic_mount.rs): 💜 5ec1cff por salvar o KernelSU!
Se você quiser apoiar o projeto:
- **USDT (BEP20, ERC20)**: `0x12b5224b7aca0121c2f003240a901e1d064371c1`
- **USDT (TRC20)**: `TYUVMWGTcnR5svnDoX85DWHyqUAeyQcdjh`
- **ETH (ERC20)**: `0x12b5224b7aca0121c2f003240a901e1d064371c1`
- **LTC**: `Ld238uYBuRQdZB5YwdbkuU6ektBAAUByoL`
- **BTC**: `19QgifcjMjSr1wB2DJcea5cxitvWVcXMT6`
---
## 🙏 Créditos
- [Kernel-Assisted Superuser](https://git.zx2c4.com/kernel-assisted-superuser/about/) Inspiração do conceito
- [Magisk](https://github.com/topjohnwu/Magisk) Implementação root principal
- [Genuine](https://github.com/brevent/genuine/) Validação de assinatura APK v2
- [Diamorphine](https://github.com/m0nad/Diamorphine) Técnicas de rootkit
- [KernelSU](https://github.com/tiann/KernelSU) A base original que tornou o KernelSU Next possível
- [Magic Mount Port](https://github.com/5ec1cff/KernelSU/blob/main/userspace/ksud/src/magic_mount.rs) 💜 para 5ec1cff por manter o KernelSU vivo

View File

@@ -1,50 +1,88 @@
[English](README.md) | [简体中文](README_CN.md) | **繁體中文** | [Türkçe](README_TR.md) | [Português (Brasil)](README_PT-BR.md) | [한국어](README_KO.md) | [Français](README_FR.md) | [Bahasa Indonesia](README_ID.md) | [Русский](README_RU.md) | [Український](README_UA.md) | [ภาษาไทย](README_TH.md) | [Tiếng Việt](README_VI.md) | [Italiano](README_IT.md) | [Polski](README_PL.md) | [Български](README_BG.md) | [日本語](README_JA.md)
# KernelSU Next
---
<img src="/assets/kernelsu_next.png" style="width: 96px;" alt="logo">
<div align="center">
<img src="/assets/kernelsu_next.png" width="96" alt="KernelSU Next Logo">
<h2>KernelSU Next</h2>
<p><strong>基於內核的 Android 設備 Root 解決方案</strong></p>
基於內核的 Android 設備 Root 解決方案
<p>
<a href="https://github.com/KernelSU-Next/KernelSU-Next/releases/latest">
<img src="https://img.shields.io/github/v/release/KernelSU-Next/KernelSU-Next?label=Release&logo=github" alt="Latest Release">
</a>
<a href="https://nightly.link/KernelSU-Next/KernelSU-Next/workflows/build-manager-ci/next/Manager">
<img src="https://img.shields.io/badge/Nightly%20Release-gray?logo=hackthebox&logoColor=fff" alt="Nightly Build">
</a>
<a href="https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html">
<img src="https://img.shields.io/badge/License-GPL%20v2-orange.svg?logo=gnu" alt="License: GPL v2">
</a>
<a href="/LICENSE">
<img src="https://img.shields.io/github/license/KernelSU-Next/KernelSU-Next?logo=gnu" alt="GitHub License">
</a>
</p>
</div>
[![Latest Release](https://img.shields.io/github/v/release/KernelSU-Next/KernelSU-Next?label=Release&logo=github)](https://github.com/KernelSU-Next/KernelSU-Next/releases/latest)
[![Nightly Release](https://img.shields.io/badge/Nightly%20Release-gray?logo=hackthebox&logoColor=fff)](https://nightly.link/KernelSU-Next/KernelSU-Next/workflows/build-manager-ci/next/Manager)
[![License: GPL v2](https://img.shields.io/badge/License-GPL%20v2-orange.svg?logo=gnu)](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html)
[![GitHub License](https://img.shields.io/github/license/KernelSU-Next/KernelSU-Next?logo=gnu)](/LICENSE)
---
## 特性
## 🚀 特性
1. 基於內核的 `su` 和 Root 權限管理
2. 基於動態掛載系統 [Magic Mount](https://topjohnwu.github.io/Magisk/details.html#magic-mount) / [OverlayFS](https://en.wikipedia.org/wiki/OverlayFS) 的模塊系統。
3. [App Profile](https://kernelsu.org/zh_CN/guide/app-profile.html):把 Root 權限關進籠子裡
- 基於內核的 `su` 和 Root 權限管理
- 模塊系統基於 **[Magic Mount](https://topjohnwu.github.io/Magisk/details.html#magic-mount)** 以及 **[OverlayFS](https://en.wikipedia.org/wiki/OverlayFS)**
- [App Profile](https://kernelsu.org/zh_CN/guide/app-profile.html):把 Root 權限關進籠子裡
## 兼容狀態
---
KernelSU Next 正式支持大多數從 4.4 到 6.6 的 Android 內核
- GKI 2.0 (5.10+) 內核可以運行預構建的映像和 LKM/KMI
- GKI 1.0 (4.19 - 5.4) 內核需要重新編譯 KernelSU 驅動程序
- EOL (<4.14) 內核也需要重新編譯 KernelSU 驅動程序3.18+ 是實驗性的,可能需要移植一些功能)
## ✅ 兼容狀態
目前僅支持 `arm64-v8a`, `armeabi-v7a` & `x86_64`
KernelSU Next 正式支持大多數從 **4.4 到 6.6** 的 Android 內核
## 用法
| 内核版本 | 支援狀況 |
|----------------|---------------|
| 5.10+ (GKI 2.0) | 可以運行預構建的映像和 LKM/KMI |
| 4.19 5.4 (GKI 1.0) | 需要重新編譯 KernelSU 驅動程序 |
| <4.14 (EOL) | 需要重新編譯 KernelSU 驅動程序3.18+ 是實驗性的,可能需要回溯移植一些功能) |
- [安裝說明](https://ksunext.org/pages/installation.html)
**支援的架構:**
`arm64-v8a``armeabi-v7a``x86_64`
## 安全性
---
有關報告 KernelSU Next 漏洞的信息,請參閱 [SECURITY.md](/SECURITY.md).
## 📦 用法
## 許可證
請遵循[安裝説明](https://kernelsu-next.github.io/webpage/pages/installation.html)進行操作
- 目錄 `kernel` 下所有文件為 [GPL-2.0-only](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html)
- `kernel` 目錄以外的其他部分均為 [GPL-3.0-or-later](https://www.gnu.org/licenses/gpl-3.0.html)
---
## 鳴謝
## 🔐 安全性
- [Kernel-Assisted Superuser](https://git.zx2c4.com/kernel-assisted-superuser/about/): KernelSU 的靈感.
- [Magisk](https://github.com/topjohnwu/Magisk): 強大的 Root 工具.
- [genuine](https://github.com/brevent/genuine/): APK v2 簽名驗證。
- [Diamorphine](https://github.com/m0nad/Diamorphine): 一些 Rootkit 技巧。
- [KernelSU](https://github.com/tiann/KernelSU): 感謝 tiann否則 KernelSU Next 根本不會存在。
- [Magic Mount Port](https://github.com/5ec1cff/KernelSU/blob/main/userspace/ksud/src/magic_mount.rs): 💜 5ec1cff 為了拯救 KernelSU
有關報告 KernelSU Next 漏洞的信息,請參閱 [SECURITY.md](/SECURITY.md)
---
## 📜 許可證
- **目錄 `/kernel` 下所有文件**為 [GPL-2.0-only](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html)
- **`/kernel` 目錄以外的其他部分**均為 [GPL-3.0-or-later](https://www.gnu.org/licenses/gpl-3.0.html)
---
## 💸 抖内
- **USDT (BEP20, ERC20)**: `0x12b5224b7aca0121c2f003240a901e1d064371c1`
- **USDT (TRC20)**: `TYUVMWGTcnR5svnDoX85DWHyqUAeyQcdjh`
- **ETH (ERC20)**: `0x12b5224b7aca0121c2f003240a901e1d064371c1`
- **LTC**: `Ld238uYBuRQdZB5YwdbkuU6ektBAAUByoL`
- **BTC**: `19QgifcjMjSr1wB2DJcea5cxitvWVcXMT6`
---
## 🙏 鳴謝
- [Kernel-Assisted Superuser](https://git.zx2c4.com/kernel-assisted-superuser/about/)KernelSU 的靈感.
- [Magisk](https://github.com/topjohnwu/Magisk):強大的 Root 工具.
- [genuine](https://github.com/brevent/genuine/)APK v2 簽名驗證。
- [Diamorphine](https://github.com/m0nad/Diamorphine):一些 Rootkit 技巧。
- [KernelSU](https://github.com/tiann/KernelSU):感謝 tiann否則 KernelSU Next 根本不會存在。
- [Magic Mount Port](https://github.com/5ec1cff/KernelSU/blob/main/userspace/ksud/src/magic_mount.rs):💜 5ec1cff 為了拯救 KernelSU

View File

@@ -1,63 +1,90 @@
[English](README.md) | [简体中文](README_CN.md) | [繁體中文](README_TW.md) | [Türkçe](README_TR.md) | [Português (Brasil)](README_PT-BR.md) | [한국어](README_KO.md) | [Français](README_FR.md) | [Bahasa Indonesia](README_ID.md) | [Русский](README_RU.md) | [ภาษาไทย](README_TH.md) | [Tiếng Việt](README_VI.md) | [Italiano](README_IT.md) | [Polski](README_PL.md) | [Български](README_BG.md) | [日本語](README_JA.md) | **Українська**
**Languages**:
[English](README.md) | [简体中文](README_CN.md) | [繁體中文](README_TW.md) | [Türkçe](README_TR.md) | [Português (Brasil)](README_PT-BR.md) | [한국어](README_KO.md) | [Français](README_FR.md) | [Bahasa Indonesia](README_ID.md) | [Русский](README_RU.md) | **Українська** | [ภาษาไทย](README_TH.md) | [Tiếng Việt](README_VI.md) | [Italiano](README_IT.md) | [Polski](README_PL.md) | [Български](README_BG.md) | [日本語](README_JA.md)
# KernelSU Next
---
<img src="/assets/kernelsu_next.png" style="width: 96px;" alt="logo">
<div align="center">
<img src="/assets/kernelsu_next.png" width="96" alt="KernelSU Next Logo">
Рут-рішення на основі ядра для пристроїв Android.
<h2>KernelSU Next</h2>
<p><strong>Рішення для root-прав на основі ядра для пристроїв Android.</strong></p>
[![Останній реліз](https://img.shields.io/github/v/release/KernelSU-Next/KernelSU-Next?label=Release&logo=github)](https://github.com/KernelSU-Next/KernelSU-Next/releases/latest)
[![Нічний реліз (Нестабільний)](https://img.shields.io/badge/Nightly%20Release-gray?logo=hackthebox&logoColor=fff)](https://nightly.link/KernelSU-Next/KernelSU-Next/workflows/build-manager-ci/next/Manager)
[![Ліцензія: GPL v2](https://img.shields.io/badge/License-GPL%20v2-orange.svg?logo=gnu)](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html)
[![GitHub Ліцензія](https://img.shields.io/github/license/KernelSU-Next/KernelSU-Next?logo=gnu)](/LICENSE)
<p>
<a href="https://github.com/KernelSU-Next/KernelSU-Next/releases/latest">
<img src="https://img.shields.io/github/v/release/KernelSU-Next/KernelSU-Next?label=Release&logo=github" alt="Latest Release">
</a>
<a href="https://nightly.link/KernelSU-Next/KernelSU-Next/workflows/build-manager-ci/next/Manager">
<img src="https://img.shields.io/badge/Nightly%20Release-gray?logo=hackthebox&logoColor=fff" alt="Nightly Build">
</a>
<a href="https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html">
<img src="https://img.shields.io/badge/License-GPL%20v2-orange.svg?logo=gnu" alt="License: GPL v2">
</a>
<a href="/LICENSE">
<img src="https://img.shields.io/github/license/KernelSU-Next/KernelSU-Next?logo=gnu" alt="GitHub License">
</a>
</p>
</div>
## Можливості
---
1. `su` на основі ядра та можливість контролювати дозволи руту.
2. Module system based on dynamic mount system [Magic Mount](https://topjohnwu.github.io/Magisk/details.html#magic-mount) / [OverlayFS](https://en.wikipedia.org/wiki/OverlayFS).
3. [Профілі додатків](https://kernelsu.org/guide/app-profile.html): Обмеж права руту для додатків.
## 🚀 Особливості
## Compatibility state
- Керування `su` та root-доступом на основі ядра.
- Модульна система на основі [Magic Mount](https://topjohnwu.github.io/Magisk/details.html#magic-mount) та [OverlayFS](https://en.wikipedia.org/wiki/OverlayFS).
- [Профілі програм](https://kernelsu.org/guide/app-profile.html): Обмеження root-прав для кожної програми.
KernelSU Next офіційно підтримує більшість Android ядер починаючи з 4.4 і до 6.6.
- Користувачі GKI 2.0 (5.10+) ядра можуть використовувати готові образи та LKM/KMI.
- Користувачі GKI 1.0 (4.19 - 5.4) ядра мають бути перезібрані з драйвером KernelSU.
- Користувачі EOL (<4.14) ядра також мають бути перезібрані з драйвером KernelSU (Підтримка 3.18+ експерементальна і потребує бекпортів деяких функцій в ядрі).
---
На даний момент підтримується лише архітектура `arm64-v8a`, `armeabi-v7a` & `x86_64`.
## ✅ Сумісність
## Спосіб використання
KernelSU Next підтримує ядра Android від **4.4 до 6.6**:
- [Інструкція для встановлення/інтеграції](https://ksunext.org/pages/installation.html)
| Версія ядра | Примітки підтримки |
|----------------------|-------------------------------------------------------------------------------------------|
| 5.10+ (GKI 2.0) | Підтримує попередньо створені образи та LKM/KMI |
| 4.19 5.4 (GKI 1.0) | Потрібен вбудований драйвер KernelSU |
| <4.14 (EOL) | Потрібен драйвер KernelSU (версія 3.18+ є експериментальною, може знадобитися портування) |
## Безпека
**Підтримувані архітектури:** `arm64-v8a`, `armeabi-v7a`, `x86_64`
Для інформації зв'язаною з безпекою дивіться [SECURITY.md](/SECURITY.md).
---
## Ліцензія
## 📦 Встановлення
- Всі файли в директорії `kernel` мають ліцензію [GPL-2.0-only](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html).
- Всі інші файли виключаючи директорію `kernel` мають ліцензію [GPL-3.0-or-later](https://www.gnu.org/licenses/gpl-3.0.html).
Будь ласка, зверніться до [Посібника з встановлення](https://kernelsu-next.github.io/webpage/pages/installation.html) для отримання інструкцій з налаштування.
## Підтримка розробника
---
- 0x12b5224b7aca0121c2f003240a901e1d064371c1 [ USDT BEP20 ]
## 🔐 Безпека
- TYUVMWGTcnR5svnDoX85DWHyqUAeyQcdjh [ USDT TRC20 ]
Щоб повідомити про проблеми безпеки, див [SECURITY.md](/SECURITY.md).
- 0x12b5224b7aca0121c2f003240a901e1d064371c1 [ USDT ERC20 ]
---
- 0x12b5224b7aca0121c2f003240a901e1d064371c1 [ ETH ERC20 ]
## 📜 Ліцензія
- Ld238uYBuRQdZB5YwdbkuU6ektBAAUByoL [ LTC ]
- **Каталог `/kernel`:** [GPL-2.0-only](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html)
- **Усі інші файли:** [GPL-3.0-or-later](https://www.gnu.org/licenses/gpl-3.0.html)
- 19QgifcjMjSr1wB2DJcea5cxitvWVcXMT6 [ BTC ]
---
## Подяки
## 💸 Пожертви
- [Kernel-Assisted Superuser](https://git.zx2c4.com/kernel-assisted-superuser/about/): Ідея KernelSU.
- [Magisk](https://github.com/topjohnwu/Magisk): Потужний засіб руту.
- [genuine](https://github.com/brevent/genuine/): Перевірка підпису APK v2.
- [Diamorphine](https://github.com/m0nad/Diamorphine): Деякі руткіт скіли.
- [KernelSU](https://github.com/tiann/KernelSU): Дякую tiann, інакше KernelSU Next ніколи б не існував.
- [Magic Mount Port](https://github.com/5ec1cff/KernelSU/blob/main/userspace/ksud/src/magic_mount.rs): Дякую 💜 5ec1cff за збереження KernelSU!
Якщо ви хочете підтримати проєкт:
- **USDT (BEP20, ERC20)**: `0x12b5224b7aca0121c2f003240a901e1d064371c1`
- **USDT (TRC20)**: `TYUVMWGTcnR5svnDoX85DWHyqUAeyQcdjh`
- **ETH (ERC20)**: `0x12b5224b7aca0121c2f003240a901e1d064371c1`
- **LTC**: `Ld238uYBuRQdZB5YwdbkuU6ektBAAUByoL`
- **BTC**: `19QgifcjMjSr1wB2DJcea5cxitvWVcXMT6`
---
## 🙏 Подяки
- [Kernel-Assisted Superuser](https://git.zx2c4.com/kernel-assisted-superuser/about/) Натхнення для концепції
- [Magisk](https://github.com/topjohnwu/Magisk) Топовий інструмент для root
- [Genuine](https://github.com/brevent/genuine/) Перевірка підпису APK версії 2
- [Diamorphine](https://github.com/m0nad/Diamorphine) Деякі навики RootKit
- [KernelSU](https://github.com/tiann/KernelSU) Основа для KernelSU Next
- [Magic Mount Port](https://github.com/5ec1cff/KernelSU/blob/main/userspace/ksud/src/magic_mount.rs) 💜 до 5ec1cff за збереження KernelSU

View File

@@ -328,6 +328,17 @@ int ksu_handle_prctl(int option, unsigned long arg2, unsigned long arg3,
return 0;
}
if (arg2 == CMD_GET_MANAGER_UID) {
uid_t manager_uid = ksu_get_manager_uid();
if (copy_to_user((void __user *)arg3, &manager_uid, sizeof(manager_uid))) {
pr_err("prctl reply error, cmd: %lu\n", arg2);
}
if (copy_to_user(result, &reply_ok, sizeof(reply_ok))) {
pr_err("prctl reply error, cmd: %lu\n", arg2);
}
return 0;
}
if (arg2 == CMD_HOOK_MODE) {
#ifdef CONFIG_KSU_KPROBES_HOOK
const char *mode = "Kprobes";

View File

@@ -24,6 +24,7 @@
#define CMD_IS_SU_ENABLED 14
#define CMD_ENABLE_SU 15
#define CMD_HOOK_MODE 16
#define CMD_GET_MANAGER_UID 17
#define EVENT_POST_FS_DATA 1
#define EVENT_BOOT_COMPLETED 2

View File

@@ -34,6 +34,7 @@ android {
}
buildFeatures {
aidl = true
buildConfig = true
compose = true
prefab = true
@@ -133,8 +134,5 @@ dependencies {
implementation(libs.lsposed.cxx)
implementation(libs.mmrl.platform)
compileOnly(libs.mmrl.hidden.api)
implementation(libs.mmrl.ui)
implementation(libs.mmrl.webui)
}

View File

@@ -36,12 +36,4 @@
-dontwarn javax.lang.model.util.SimpleTypeVisitor7
-dontwarn javax.lang.model.util.SimpleTypeVisitor8
-dontwarn javax.lang.model.util.Types
-dontwarn javax.tools.Diagnostic$Kind
# MMRL:webui reflection
-keep class com.dergoogler.mmrl.webui.model.ModId { *; }
-keep class com.dergoogler.mmrl.webui.interfaces.** { *; }
-keep class com.rifsxd.ksunext.ui.webui.WebViewInterface { *; }
-keep,allowobfuscation class * extends com.dergoogler.mmrl.platform.content.IService { *; }
-dontwarn javax.tools.Diagnostic$Kind

View File

@@ -0,0 +1,9 @@
// IKsuInterface.aidl
package com.rifsxd.ksunext;
import android.content.pm.PackageInfo;
import rikka.parcelablelist.ParcelableListSlice;
interface IKsuInterface {
ParcelableListSlice<PackageInfo> getPackages(int flags);
}

View File

@@ -25,6 +25,13 @@ Java_com_rifsxd_ksunext_Natives_getVersion(JNIEnv *env, jobject) {
return get_version();
}
extern "C"
JNIEXPORT jint JNICALL
Java_com_rifsxd_ksunext_Natives_getManagerUid(JNIEnv *env, jobject) {
uid_t manager_uid = get_manager_uid();
return (jint)manager_uid;
}
extern "C"
JNIEXPORT jstring JNICALL
Java_com_rifsxd_ksunext_Natives_getHookMode(JNIEnv *env, jobject) {
@@ -313,3 +320,9 @@ JNIEXPORT jboolean JNICALL
Java_com_rifsxd_ksunext_Natives_setSuEnabled(JNIEnv *env, jobject thiz, jboolean enabled) {
return set_su_enabled(enabled);
}
extern "C"
JNIEXPORT jboolean JNICALL
Java_com_rifsxd_ksunext_Natives_isZygiskEnabled(JNIEnv *env, jobject) {
return is_zygisk_enabled();
}

View File

@@ -6,6 +6,7 @@
#include <stdint.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include "ksu.h"
@@ -30,6 +31,7 @@
#define CMD_IS_SU_ENABLED 14
#define CMD_ENABLE_SU 15
#define CMD_HOOK_MODE 16
#define CMD_GET_MANAGER_UID 17
static bool ksuctl(int cmd, void* arg1, void* arg2) {
int32_t result = 0;
@@ -51,23 +53,31 @@ bool become_manager(const char* pkg) {
}
// cache the result to avoid unnecessary syscall
static bool is_lkm;
int get_version() {
static bool is_lkm = false;
int get_version(void) {
int32_t version = -1;
int32_t lkm = 0;
ksuctl(CMD_GET_VERSION, &version, &lkm);
if (!is_lkm && lkm != 0) {
int32_t flags = 0;
ksuctl(CMD_GET_VERSION, &version, &flags);
if (!is_lkm && (flags & 0x1)) {
is_lkm = true;
}
return version;
}
uid_t get_manager_uid() {
uid_t manager_uid = 0;
ksuctl(CMD_GET_MANAGER_UID, &manager_uid, nullptr);
return manager_uid;
}
const char* get_hook_mode() {
static char mode[16];
ksuctl(CMD_HOOK_MODE, mode, nullptr);
return mode;
}
bool get_allow_list(int *uids, int *size) {
return ksuctl(CMD_GET_SU_LIST, uids, size);
}
@@ -103,4 +113,8 @@ bool is_su_enabled() {
// if ksuctl failed, we assume su is enabled, and it cannot be disabled.
ksuctl(CMD_IS_SU_ENABLED, &enabled, nullptr);
return enabled;
}
bool is_zygisk_enabled() {
return !!getenv("ZYGISK_ENABLED");
}

View File

@@ -11,6 +11,8 @@ bool become_manager(const char *);
int get_version();
uid_t get_manager_uid();
const char* get_hook_mode();
bool get_allow_list(int *uids, int *size);
@@ -85,4 +87,6 @@ bool set_su_enabled(bool enabled);
bool is_su_enabled();
bool is_zygisk_enabled();
#endif //KERNELSU_KSU_H

View File

@@ -4,17 +4,12 @@ import android.app.Application
import android.system.Os
import coil.Coil
import coil.ImageLoader
import com.dergoogler.mmrl.platform.Platform
import me.zhanghai.android.appiconloader.coil.AppIconFetcher
import me.zhanghai.android.appiconloader.coil.AppIconKeyer
import okhttp3.Cache
import okhttp3.OkHttpClient
import java.io.File
import java.util.Locale
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import com.rifsxd.ksunext.ui.webui.initPlatform
lateinit var ksuApp: KernelSUApplication
@@ -26,11 +21,6 @@ class KernelSUApplication : Application() {
super.onCreate()
ksuApp = this
Platform.setHiddenApiExemptions()
// Pre-initialize WX Platform as early as possible
launchPlatformInit()
val context = this
val iconSize = resources.getDimensionPixelSize(android.R.dimen.app_icon_size)
Coil.setImageLoader(
@@ -61,11 +51,5 @@ class KernelSUApplication : Application() {
}.build()
}
private fun launchPlatformInit() {
// Use a coroutine to avoid blocking the main thread
GlobalScope.launch(Dispatchers.IO) {
initPlatform()
}
}
}

View File

@@ -0,0 +1,77 @@
package com.rifsxd.ksunext.ui;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.IBinder;
import android.os.UserHandle;
import android.os.UserManager;
import android.util.Log;
import androidx.annotation.NonNull;
import com.topjohnwu.superuser.ipc.RootService;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import com.rifsxd.ksunext.IKsuInterface;
import rikka.parcelablelist.ParcelableListSlice;
/**
* @author weishu
* @date 2023/4/18.
*/
public class KsuService extends RootService {
private static final String TAG = "KsuService";
class Stub extends IKsuInterface.Stub {
@Override
public ParcelableListSlice<PackageInfo> getPackages(int flags) {
List<PackageInfo> list = getInstalledPackagesAll(flags);
Log.i(TAG, "getPackages: " + list.size());
return new ParcelableListSlice<>(list);
}
}
@Override
public IBinder onBind(@NonNull Intent intent) {
return new Stub();
}
List<Integer> getUserIds() {
List<Integer> result = new ArrayList<>();
UserManager um = (UserManager) getSystemService(Context.USER_SERVICE);
List<UserHandle> userProfiles = um.getUserProfiles();
for (UserHandle userProfile : userProfiles) {
int userId = userProfile.hashCode();
result.add(userProfile.hashCode());
}
return result;
}
ArrayList<PackageInfo> getInstalledPackagesAll(int flags) {
ArrayList<PackageInfo> packages = new ArrayList<>();
for (Integer userId : getUserIds()) {
Log.i(TAG, "getInstalledPackagesAll: " + userId);
packages.addAll(getInstalledPackagesAsUser(flags, userId));
}
return packages;
}
List<PackageInfo> getInstalledPackagesAsUser(int flags, int userId) {
try {
PackageManager pm = getPackageManager();
Method getInstalledPackagesAsUser = pm.getClass().getDeclaredMethod("getInstalledPackagesAsUser", int.class, int.class);
return (List<PackageInfo>) getInstalledPackagesAsUser.invoke(pm, flags, userId);
} catch (Throwable e) {
Log.e(TAG, "err", e);
}
return new ArrayList<>();
}
}

View File

@@ -28,6 +28,9 @@ object Natives {
// 12569: support get hook mode
const val MINIMAL_SUPPORTED_HOOK_MODE = 12569
// 12750: support get manager UID
const val MINIMAL_SUPPORTED_MANAGER_UID = 12751
const val KERNEL_SU_DOMAIN = "u:r:su:s0"
const val ROOT_UID = 0
@@ -54,6 +57,12 @@ object Natives {
external fun uidShouldUmount(uid: Int): Boolean
/**
* Get the UID of the current root manager.
* @return manager UID, or 0 if unavailable.
*/
external fun getManagerUid(): Int
/**
* Get a string indicating the SU hook mode enabled in kernel.
* The return values are:
@@ -64,6 +73,11 @@ object Natives {
*/
external fun getHookMode(): String?
/**
* Check if Zygisk injection is enabled in the environment.
*/
external fun isZygiskEnabled(): Boolean
/**
* Get the profile of the given package.
* @param key usually the package name

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
@@ -55,13 +58,20 @@ 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.LocaleHelper
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
import com.rifsxd.ksunext.ui.viewmodel.SuperUserViewModel
class MainActivity : ComponentActivity() {
override fun attachBaseContext(newBase: Context?) {
super.attachBaseContext(newBase?.let { LocaleHelper.applyLanguage(it) })
}
override fun onCreate(savedInstanceState: Bundle?) {
// Enable edge to edge
@@ -104,6 +114,12 @@ class MainActivity : ComponentActivity() {
val prefs = getSharedPreferences("settings", Context.MODE_PRIVATE)
val amoledMode = prefs.getBoolean("enable_amoled", false)
val moduleViewModel: ModuleViewModel = viewModel()
val superUserViewModel: SuperUserViewModel = viewModel()
val moduleUpdateCount = moduleViewModel.moduleList.count {
moduleViewModel.checkUpdate(it).first.isNotEmpty()
}
KernelSUTheme (
amoledMode = amoledMode
) {
@@ -124,6 +140,16 @@ class MainActivity : ComponentActivity() {
}
}
LaunchedEffect(Unit) {
if (superUserViewModel.appList.isEmpty()) {
superUserViewModel.fetchAppList()
}
if (moduleViewModel.moduleList.isEmpty()) {
moduleViewModel.fetchModuleList()
}
}
val showBottomBar = when (currentDestination?.route) {
FlashScreenDestination.route -> false // Hide for FlashScreenDestination
ExecuteModuleActionScreenDestination.route -> false // Hide for ExecuteModuleActionScreen
@@ -137,7 +163,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)
@@ -164,7 +190,7 @@ 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()
@@ -179,17 +205,6 @@ private fun BottomBar(navController: NavHostController) {
)
) {
BottomBarDestination.entries
.filter {
// Hide SuperUser and Module when su compat is disabled
if (suCompatDisabled) {
if (suSFS == "Supported" && susSUMode == "2") {
true
} else {
// hide SuperUser and Module
it != BottomBarDestination.SuperUser && it != BottomBarDestination.Module
}
} else true
}
.forEach { destination ->
if (!fullFeatured && destination.rootRequired) return@forEach
val isCurrentDestOnBackStack by navController.isRouteOnBackStackAsState(destination.direction)
@@ -208,10 +223,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

@@ -31,6 +31,7 @@ import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.compose.ui.window.Dialog
import androidx.compose.ui.text.font.FontWeight
import com.rifsxd.ksunext.BuildConfig
import com.rifsxd.ksunext.R
@@ -83,8 +84,9 @@ private fun AboutCardContent() {
Column {
Text(
stringResource(id = R.string.app_name),
style = MaterialTheme.typography.titleSmall,
text = stringResource(id = R.string.app_name),
style = MaterialTheme.typography.titleMedium,
fontWeight = FontWeight.SemiBold,
fontSize = 18.sp
)
Text(

View File

@@ -17,6 +17,7 @@ import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.runtime.saveable.Saver
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.toArgb
@@ -402,7 +403,11 @@ private fun ConfirmDialog(visuals: ConfirmDialogVisuals, confirm: () -> Unit, di
dismiss()
},
title = {
Text(text = visuals.title)
Text(
text = visuals.title,
style = MaterialTheme.typography.titleLarge,
fontWeight = FontWeight.SemiBold
)
},
text = {
if (visuals.isMarkdown) {

View File

@@ -9,8 +9,10 @@ import androidx.compose.material3.LocalTextStyle
import androidx.compose.material3.RadioButton
import androidx.compose.material3.Switch
import androidx.compose.material3.Text
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
import androidx.compose.ui.graphics.vector.ImageVector
@@ -55,6 +57,8 @@ fun SwitchItem(
Text(
modifier = Modifier.then(stateAlpha),
text = title,
style = MaterialTheme.typography.titleMedium,
fontWeight = FontWeight.SemiBold
)
}
},

View File

@@ -37,6 +37,7 @@ import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.material3.TopAppBarScrollBehavior
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
@@ -44,6 +45,7 @@ import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.input.nestedscroll.nestedScroll
@@ -186,7 +188,11 @@ private fun AppProfileInner(
Column(modifier = modifier) {
AppMenuBox(packageName) {
ListItem(
headlineContent = { Text(appLabel) },
headlineContent = { Text(
text = appLabel,
style = MaterialTheme.typography.titleMedium,
fontWeight = FontWeight.SemiBold
) },
supportingContent = { Text(packageName) },
leadingContent = appIcon,
)
@@ -272,7 +278,11 @@ private fun TopBar(
) {
TopAppBar(
title = {
Text(stringResource(R.string.profile))
Text(
text = stringResource(R.string.profile),
style = MaterialTheme.typography.titleLarge,
fontWeight = FontWeight.Black,
)
},
navigationIcon = {
IconButton(
@@ -291,7 +301,11 @@ private fun ProfileBox(
onModeChange: (Mode) -> Unit,
) {
ListItem(
headlineContent = { Text(stringResource(R.string.profile)) },
headlineContent = { Text(
text = stringResource(R.string.profile),
style = MaterialTheme.typography.titleMedium,
fontWeight = FontWeight.SemiBold
) },
supportingContent = { Text(mode.text) },
leadingContent = { Icon(Icons.Filled.AccountCircle, null) },
)

View File

@@ -27,6 +27,7 @@ import androidx.compose.material3.TopAppBar
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.material3.TopAppBarScrollBehavior
import androidx.compose.material3.rememberTopAppBarState
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
@@ -34,6 +35,7 @@ import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.Modifier
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.LocalContext
@@ -101,7 +103,11 @@ fun BackupRestoreScreen(navigator: DestinationsNavigator) {
if (showRebootDialog) {
AlertDialog(
onDismissRequest = { showRebootDialog = false },
title = { Text(stringResource(R.string.reboot_required)) },
title = { Text(
text = stringResource(R.string.reboot_required),
style = MaterialTheme.typography.titleLarge,
fontWeight = FontWeight.SemiBold
) },
text = { Text(stringResource(R.string.reboot_message)) },
confirmButton = {
TextButton(onClick = {
@@ -128,7 +134,11 @@ fun BackupRestoreScreen(navigator: DestinationsNavigator) {
moduleBackup
)
},
headlineContent = { Text(moduleBackup) },
headlineContent = { Text(
text = moduleBackup,
style = MaterialTheme.typography.titleMedium,
fontWeight = FontWeight.SemiBold,
) },
modifier = Modifier.clickable {
scope.launch {
val result = backupDialog.awaitConfirm(title = moduleBackup, content = backupMessage)
@@ -144,7 +154,11 @@ fun BackupRestoreScreen(navigator: DestinationsNavigator) {
if (showRebootDialog) {
AlertDialog(
onDismissRequest = { showRebootDialog = false },
title = { Text(stringResource(R.string.reboot_required)) },
title = { Text(
text = stringResource(R.string.reboot_required),
style = MaterialTheme.typography.titleLarge,
fontWeight = FontWeight.SemiBold
) },
text = { Text(stringResource(R.string.reboot_message)) },
confirmButton = {
TextButton(onClick = {
@@ -176,13 +190,15 @@ fun BackupRestoreScreen(navigator: DestinationsNavigator) {
Icon(
Icons.Filled.Restore,
moduleRestore,
tint = if (useOverlayFs) androidx.compose.material3.MaterialTheme.colorScheme.onSurface.copy(alpha = 0.38f) else androidx.compose.material3.MaterialTheme.colorScheme.onSurface
tint = if (useOverlayFs) MaterialTheme.colorScheme.onSurface.copy(alpha = 0.38f) else MaterialTheme.colorScheme.onSurface
)
},
headlineContent = {
Text(
moduleRestore,
color = if (useOverlayFs) androidx.compose.material3.MaterialTheme.colorScheme.onSurface.copy(alpha = 0.38f) else androidx.compose.material3.MaterialTheme.colorScheme.onSurface
style = MaterialTheme.typography.titleMedium,
fontWeight = FontWeight.SemiBold,
color = if (useOverlayFs) MaterialTheme.colorScheme.onSurface.copy(alpha = 0.38f) else MaterialTheme.colorScheme.onSurface
)
},
modifier = Modifier.clickable(
@@ -212,7 +228,11 @@ fun BackupRestoreScreen(navigator: DestinationsNavigator) {
allowlistBackup
)
},
headlineContent = { Text(allowlistBackup) },
headlineContent = { Text(
text = allowlistBackup,
style = MaterialTheme.typography.titleMedium,
fontWeight = FontWeight.SemiBold,
) },
modifier = Modifier.clickable {
scope.launch {
val result = backupDialog.awaitConfirm(title = allowlistBackup, content = allowlistbackupMessage)
@@ -234,7 +254,11 @@ fun BackupRestoreScreen(navigator: DestinationsNavigator) {
allowlistRestore
)
},
headlineContent = { Text(allowlistRestore) },
headlineContent = { Text(
text = allowlistRestore,
style = MaterialTheme.typography.titleMedium,
fontWeight = FontWeight.SemiBold,
) },
modifier = Modifier.clickable {
scope.launch {
val result = restoreDialog.awaitConfirm(title = allowlistRestore, content = allowlistrestoreMessage)
@@ -257,7 +281,11 @@ private fun TopBar(
scrollBehavior: TopAppBarScrollBehavior? = null
) {
TopAppBar(
title = { Text(stringResource(R.string.backup_restore)) }, navigationIcon = {
title = { Text(
text = stringResource(R.string.backup_restore),
style = MaterialTheme.typography.titleLarge,
fontWeight = FontWeight.Black,
) }, navigationIcon = {
IconButton(
onClick = onBack
) { Icon(Icons.AutoMirrored.Filled.ArrowBack, contentDescription = null) }

View File

@@ -3,6 +3,7 @@ package com.rifsxd.ksunext.ui.screen
import android.content.Context
import android.content.Intent
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.WindowInsetsSides
@@ -17,6 +18,7 @@ import androidx.compose.material.icons.filled.*
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.ListItem
import androidx.compose.material3.Scaffold
import androidx.compose.material3.SnackbarHost
import androidx.compose.material3.Text
@@ -26,19 +28,28 @@ import androidx.compose.material3.TopAppBar
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.material3.TopAppBarScrollBehavior
import androidx.compose.material3.rememberTopAppBarState
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.Modifier
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.lifecycle.compose.dropUnlessResumed
import com.maxkeppeker.sheets.core.models.base.Header
import com.maxkeppeker.sheets.core.models.base.rememberUseCaseState
import com.maxkeppeler.sheets.list.ListDialog
import com.maxkeppeler.sheets.list.models.ListOption
import com.maxkeppeler.sheets.list.models.ListSelection
import com.ramcosta.composedestinations.annotation.Destination
import com.ramcosta.composedestinations.annotation.RootGraph
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
@@ -46,10 +57,12 @@ import com.ramcosta.composedestinations.navigation.EmptyDestinationsNavigator
import com.rifsxd.ksunext.Natives
import com.rifsxd.ksunext.ksuApp
import com.rifsxd.ksunext.R
import com.rifsxd.ksunext.ui.component.rememberCustomDialog
import com.rifsxd.ksunext.ui.component.SwitchItem
import com.rifsxd.ksunext.ui.util.LocaleHelper
import com.rifsxd.ksunext.ui.util.LocalSnackbarHost
import com.rifsxd.ksunext.ui.util.*
import java.util.Locale
/**
* @author rifsxd
@@ -90,19 +103,181 @@ fun CustomizationScreen(navigator: DestinationsNavigator) {
val prefs = context.getSharedPreferences("settings", Context.MODE_PRIVATE)
// Track language state with current app locale
var currentAppLocale by remember { mutableStateOf(LocaleHelper.getCurrentAppLocale(context)) }
// Listen for preference changes
LaunchedEffect(Unit) {
currentAppLocale = LocaleHelper.getCurrentAppLocale(context)
}
// Language setting with selection dialog
val languageDialog = rememberCustomDialog { dismiss ->
// Check if should use system language settings
if (LocaleHelper.useSystemLanguageSettings) {
// Android 13+ - Jump to system settings
LocaleHelper.launchSystemLanguageSettings(context)
dismiss()
} else {
// Android < 13 - Show app language selector
// Dynamically detect supported locales from resources
val supportedLocales = remember {
val locales = mutableListOf<java.util.Locale>()
// Add system default first
locales.add(java.util.Locale.ROOT) // This will represent "System Default"
// Dynamically detect available locales by checking resource directories
val resourceDirs = listOf(
"ar", "bg", "de", "fa", "fr", "hu", "in", "it",
"ja", "ko", "pl", "pt-rBR", "ru", "th", "tr",
"uk", "vi", "zh-rCN", "zh-rTW"
)
resourceDirs.forEach { dir ->
try {
val locale = when {
dir.contains("-r") -> {
val parts = dir.split("-r")
java.util.Locale.Builder()
.setLanguage(parts[0])
.setRegion(parts[1])
.build()
}
else -> java.util.Locale.Builder()
.setLanguage(dir)
.build()
}
// Test if this locale has translated resources
val config = android.content.res.Configuration()
config.setLocale(locale)
val localizedContext = context.createConfigurationContext(config)
// Try to get a translated string to verify the locale is supported
val testString = localizedContext.getString(R.string.settings_language)
val defaultString = context.getString(R.string.settings_language)
// If the string is different or it's English, it's supported
if (testString != defaultString || locale.language == "en") {
locales.add(locale)
}
} catch (e: Exception) {
// Skip unsupported locales
}
}
// Sort by display name
val sortedLocales = locales.drop(1).sortedBy { it.getDisplayName(it) }
mutableListOf<java.util.Locale>().apply {
add(locales.first()) // System default first
addAll(sortedLocales)
}
}
val allOptions = supportedLocales.map { locale ->
val tag = if (locale == java.util.Locale.ROOT) {
"system"
} else if (locale.country.isEmpty()) {
locale.language
} else {
"${locale.language}_${locale.country}"
}
val displayName = if (locale == java.util.Locale.ROOT) {
context.getString(R.string.system_default)
} else {
locale.getDisplayName(locale)
}
tag to displayName
}
val currentLocale = prefs.getString("app_locale", "system") ?: "system"
val options = allOptions.map { (tag, displayName) ->
ListOption(
titleText = displayName,
selected = currentLocale == tag
)
}
var selectedIndex by remember {
mutableIntStateOf(allOptions.indexOfFirst { (tag, _) -> currentLocale == tag })
}
ListDialog(
state = rememberUseCaseState(
visible = true,
onFinishedRequest = {
if (selectedIndex >= 0 && selectedIndex < allOptions.size) {
val newLocale = allOptions[selectedIndex].first
prefs.edit().putString("app_locale", newLocale).apply()
// Update local state immediately
currentAppLocale = LocaleHelper.getCurrentAppLocale(context)
// Apply locale change immediately for Android < 13
LocaleHelper.restartActivity(context)
}
dismiss()
},
onCloseRequest = {
dismiss()
}
),
header = Header.Default(
title = stringResource(R.string.settings_language),
),
selection = ListSelection.Single(
showRadioButtons = true,
options = options
) { index, _ ->
selectedIndex = index
}
)
}
}
val language = stringResource(id = R.string.settings_language)
// Compute display name based on current app locale (similar to the reference implementation)
val currentLanguageDisplay = remember(currentAppLocale) {
val locale = currentAppLocale
if (locale != null) {
locale.getDisplayName(locale)
} else {
context.getString(R.string.system_default)
}
}
ListItem(
leadingContent = { Icon(Icons.Filled.Translate, language) },
headlineContent = { Text(
text = language,
style = MaterialTheme.typography.titleMedium,
fontWeight = FontWeight.SemiBold,
) },
supportingContent = { Text(currentLanguageDisplay) },
modifier = Modifier.clickable {
languageDialog.show()
}
)
var useBanner by rememberSaveable {
mutableStateOf(
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 {
@@ -125,7 +300,11 @@ fun CustomizationScreen(navigator: DestinationsNavigator) {
if (showRestartDialog) {
AlertDialog(
onDismissRequest = { showRestartDialog = false },
title = { Text(stringResource(R.string.restart_required)) },
title = { Text(
text = stringResource(R.string.restart_required),
style = MaterialTheme.typography.titleLarge,
fontWeight = FontWeight.SemiBold
) },
text = { Text(stringResource(R.string.restart_app_message)) },
confirmButton = {
TextButton(onClick = {
@@ -159,7 +338,11 @@ private fun TopBar(
scrollBehavior: TopAppBarScrollBehavior? = null
) {
TopAppBar(
title = { Text(stringResource(R.string.customization)) }, navigationIcon = {
title = { Text(
text = stringResource(R.string.customization),
style = MaterialTheme.typography.titleLarge,
fontWeight = FontWeight.Black,
) }, navigationIcon = {
IconButton(
onClick = onBack
) { Icon(Icons.AutoMirrored.Filled.ArrowBack, contentDescription = null) }

View File

@@ -23,6 +23,7 @@ import androidx.compose.material3.TopAppBar
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.material3.TopAppBarScrollBehavior
import androidx.compose.material3.rememberTopAppBarState
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
@@ -30,13 +31,13 @@ import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.Modifier
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.dergoogler.mmrl.platform.Platform
import com.ramcosta.composedestinations.annotation.Destination
import com.ramcosta.composedestinations.annotation.RootGraph
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
@@ -102,25 +103,6 @@ fun DeveloperScreen(navigator: DestinationsNavigator) {
}
}
var useWebUIX by rememberSaveable {
mutableStateOf(
prefs.getBoolean("use_webuix", true)
)
}
if (ksuVersion != null) {
SwitchItem(
beta = false,
enabled = Platform.isAlive && developerOptionsEnabled,
icon = Icons.Filled.WebAsset,
title = stringResource(id = R.string.use_webuix),
summary = stringResource(id = R.string.use_webuix_summary),
checked = useWebUIX
) {
prefs.edit().putBoolean("use_webuix", it).apply()
useWebUIX = it
}
}
var enableWebDebugging by rememberSaveable {
mutableStateOf(
prefs.getBoolean("enable_web_debugging", false)
@@ -138,25 +120,6 @@ fun DeveloperScreen(navigator: DestinationsNavigator) {
enableWebDebugging = it
}
}
var useWebUIXEruda by rememberSaveable {
mutableStateOf(
prefs.getBoolean("use_webuix_eruda", false)
)
}
if (ksuVersion != null) {
SwitchItem(
beta = false,
enabled = Platform.isAlive && useWebUIX && enableWebDebugging,
icon = Icons.Filled.FormatListNumbered,
title = stringResource(id = R.string.use_webuix_eruda),
summary = stringResource(id = R.string.use_webuix_eruda_summary),
checked = useWebUIXEruda
) {
prefs.edit().putBoolean("use_webuix_eruda", it).apply()
useWebUIXEruda = it
}
}
}
}
}
@@ -168,7 +131,11 @@ private fun TopBar(
scrollBehavior: TopAppBarScrollBehavior? = null
) {
TopAppBar(
title = { Text(stringResource(R.string.developer)) }, navigationIcon = {
title = { Text(
text = stringResource(R.string.developer),
style = MaterialTheme.typography.titleLarge,
fontWeight = FontWeight.Black,
) }, navigationIcon = {
IconButton(
onClick = onBack
) { Icon(Icons.AutoMirrored.Filled.ArrowBack, contentDescription = null) }

View File

@@ -35,6 +35,7 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.input.key.Key
@@ -178,7 +179,11 @@ fun ExecuteModuleActionScreen(navigator: DestinationsNavigator, moduleId: String
@Composable
private fun TopBar(isActionRunning: Boolean, onBack: () -> Unit = {}, onSave: () -> Unit = {}) {
TopAppBar(
title = { Text(stringResource(R.string.action)) },
title = { Text(
text = stringResource(R.string.action),
style = MaterialTheme.typography.titleLarge,
fontWeight = FontWeight.Black,
) },
navigationIcon = {
IconButton(
onClick = onBack,

View File

@@ -41,6 +41,7 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.input.key.Key
@@ -151,6 +152,10 @@ fun FlashScreen(
}
}
BackHandler(enabled = flashing == FlashingStatus.FLASHING) {
// Disable back button if flashing is running
}
BackHandler(enabled = flashing != FlashingStatus.FLASHING) {
navigator.popBackStack()
if (finishIntent) activity?.finish()
@@ -400,7 +405,9 @@ private fun TopBar(
FlashingStatus.SUCCESS -> R.string.flash_success
FlashingStatus.FAILED -> R.string.flash_failed
}
)
),
style = MaterialTheme.typography.titleLarge,
fontWeight = FontWeight.Black,
)
},
navigationIcon = {

View File

@@ -11,6 +11,7 @@ import androidx.annotation.StringRes
import androidx.compose.animation.core.tween
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.animation.*
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
@@ -33,6 +34,7 @@ import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalUriHandler
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.pluralStringResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.intl.Locale
import androidx.compose.ui.text.toUpperCase
@@ -54,8 +56,6 @@ import com.rifsxd.ksunext.R
import com.rifsxd.ksunext.ui.component.rememberConfirmDialog
import com.rifsxd.ksunext.ui.util.*
import com.rifsxd.ksunext.ui.util.module.LatestVersionInfo
import com.rifsxd.ksunext.ui.viewmodel.ModuleViewModel
import com.rifsxd.ksunext.ui.viewmodel.SuperUserViewModel
import java.util.*
@OptIn(ExperimentalMaterial3Api::class)
@@ -96,28 +96,23 @@ fun HomeScreen(navigator: DestinationsNavigator) {
val lkmMode = ksuVersion?.let {
if (it >= Natives.MINIMAL_SUPPORTED_KERNEL_LKM && kernelVersion.isGKI()) Natives.isLkmMode else null
}
val superUserViewModel: SuperUserViewModel = viewModel()
val moduleViewModel: ModuleViewModel = viewModel()
LaunchedEffect(Unit) {
if (superUserViewModel.appList.isEmpty()) {
superUserViewModel.fetchAppList()
}
if (moduleViewModel.moduleList.isEmpty()) {
moduleViewModel.fetchModuleList()
}
}
val moduleUpdateCount = moduleViewModel.moduleList.count {
moduleViewModel.checkUpdate(it).first.isNotEmpty()
}
StatusCard(kernelVersion, ksuVersion, lkmMode, moduleUpdateCount) {
StatusCard(kernelVersion, ksuVersion, lkmMode) {
navigator.navigate(InstallScreenDestination)
}
if (ksuVersion != null && rootAvailable()) {
Row(
modifier = Modifier
.fillMaxWidth()
.height(IntrinsicSize.Min),
horizontalArrangement = Arrangement.spacedBy(14.dp)
) {
Box(modifier = Modifier.weight(1f)) { SuperuserCard() }
Box(modifier = Modifier.weight(1f)) { ModuleCard() }
}
}
if (isManager && Natives.requireNewKernel()) {
WarningCard(
stringResource(id = R.string.require_kernel_version).format(
@@ -145,6 +140,70 @@ fun HomeScreen(navigator: DestinationsNavigator) {
}
}
@Composable
private fun SuperuserCard() {
val count = getSuperuserCount()
ElevatedCard(
colors = CardDefaults.elevatedCardColors(
containerColor = MaterialTheme.colorScheme.secondaryContainer
)
) {
Box(
modifier = Modifier
.fillMaxSize()
.padding(16.dp),
contentAlignment = Alignment.Center
) {
Column(
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy(4.dp)
) {
Text(
text = pluralStringResource(R.plurals.home_superuser_count, count),
style = MaterialTheme.typography.bodySmall
)
Text(
text = count.toString(),
style = MaterialTheme.typography.titleMedium,
fontWeight = FontWeight.SemiBold
)
}
}
}
}
@Composable
private fun ModuleCard() {
val count = getModuleCount()
ElevatedCard(
colors = CardDefaults.elevatedCardColors(
containerColor = MaterialTheme.colorScheme.secondaryContainer
)
) {
Box(
modifier = Modifier
.fillMaxSize()
.padding(16.dp),
contentAlignment = Alignment.Center
) {
Column(
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy(4.dp)
) {
Text(
text = pluralStringResource(R.plurals.home_module_count, count),
style = MaterialTheme.typography.bodySmall
)
Text(
text = count.toString(),
style = MaterialTheme.typography.titleMedium,
fontWeight = FontWeight.SemiBold
)
}
}
}
}
@Composable
fun UpdateCard() {
val context = LocalContext.current
@@ -251,7 +310,11 @@ private fun TopBar(
rotationZ = rotation
}
)
Text(stringResource(R.string.app_name))
Text(
text = stringResource(R.string.app_name),
style = MaterialTheme.typography.titleLarge,
fontWeight = FontWeight.Black,
)
}
},
actions = {
@@ -314,7 +377,7 @@ private fun StatusCard(
ElevatedCard(
colors = CardDefaults.elevatedCardColors(containerColor = run {
if (ksuVersion != null) MaterialTheme.colorScheme.secondaryContainer
if (ksuVersion != null) MaterialTheme.colorScheme.primaryContainer
else MaterialTheme.colorScheme.errorContainer
})
) {
@@ -332,8 +395,10 @@ private fun StatusCard(
intent.addFlags(android.content.Intent.FLAG_ACTIVITY_NEW_TASK)
if (ksuVersion != null) {
context.startActivity(intent)
} else {
} else if (kernelVersion.isGKI()) {
onClickInstall()
} else {
Toast.makeText(context, "Something weird happened... 🤔", Toast.LENGTH_SHORT).show()
}
} else if (ksuVersion == null && kernelVersion.isGKI()) {
onClickInstall()
@@ -409,41 +474,15 @@ private fun StatusCard(
) {
Text(
text = stringResource(id = R.string.home_working),
style = MaterialTheme.typography.titleMedium
style = MaterialTheme.typography.titleMedium,
fontWeight = FontWeight.SemiBold
)
}
Text(
text = stringResource(R.string.home_working_version, ksuVersion),
style = MaterialTheme.typography.bodyMedium
style = MaterialTheme.typography.bodySmall
)
Text(
text = stringResource(
R.string.home_superuser_count, getSuperuserCount()
), style = MaterialTheme.typography.bodyMedium
)
Text(
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
)
}
}
}
@@ -514,6 +553,8 @@ private fun InfoCard(autoExpand: Boolean = false) {
var expanded by rememberSaveable { mutableStateOf(false) }
val developerOptionsEnabled = prefs.getBoolean("enable_developer_options", false)
LaunchedEffect(autoExpand) {
if (autoExpand) {
expanded = true
@@ -524,7 +565,7 @@ private fun InfoCard(autoExpand: Boolean = false) {
Column(
modifier = Modifier
.fillMaxWidth()
.padding(start = 24.dp, top = 24.dp, end = 24.dp, bottom = 16.dp)
.padding(start = 24.dp, top = 24.dp, end = 24.dp, bottom = 24.dp)
) {
@Composable
fun InfoCardItem(label: String, content: String, icon: Any? = null) {
@@ -546,11 +587,12 @@ private fun InfoCard(autoExpand: Boolean = false) {
Column {
Text(
text = label,
style = MaterialTheme.typography.bodyLarge
style = MaterialTheme.typography.titleMedium,
fontWeight = FontWeight.SemiBold
)
Text(
text = content,
style = MaterialTheme.typography.bodyMedium,
style = MaterialTheme.typography.bodySmall,
modifier = Modifier.padding(top = 4.dp)
)
}
@@ -561,7 +603,14 @@ private fun InfoCard(autoExpand: Boolean = false) {
val managerVersion = getManagerVersion(context)
InfoCardItem(
label = stringResource(R.string.home_manager_version),
content = "${managerVersion.first} (${managerVersion.second})",
content = if (
developerOptionsEnabled &&
Natives.version >= Natives.MINIMAL_SUPPORTED_MANAGER_UID
) {
"${managerVersion.first} (${managerVersion.second}) | UID: ${Natives.getManagerUid()}"
} else {
"${managerVersion.first} (${managerVersion.second})"
},
icon = painterResource(R.drawable.ic_ksu_next),
)
@@ -595,14 +644,23 @@ 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),
)
}
Spacer(Modifier.height(16.dp))
if (Natives.isZygiskEnabled()) {
InfoCardItem(
label = stringResource(R.string.zygisk_status),
content = stringResource(R.string.enabled),
icon = Icons.Filled.Vaccines
)
}
}
if (!expanded) {
Spacer(Modifier.height(12.dp))
Spacer(Modifier.height(16.dp))
Row(
modifier = Modifier
.fillMaxWidth(),
@@ -749,17 +807,18 @@ fun IssueReportCard() {
Column(modifier = Modifier.weight(1f)) {
Text(
text = stringResource(R.string.issue_report_title),
style = MaterialTheme.typography.titleSmall
style = MaterialTheme.typography.titleMedium,
fontWeight = FontWeight.SemiBold
)
Spacer(Modifier.height(4.dp))
Text(
text = stringResource(R.string.issue_report_body),
style = MaterialTheme.typography.bodyMedium
style = MaterialTheme.typography.bodySmall
)
Spacer(Modifier.height(4.dp))
Text(
text = stringResource(R.string.issue_report_body_2),
style = MaterialTheme.typography.bodyMedium
style = MaterialTheme.typography.bodySmall
)
}
Row(horizontalArrangement = Arrangement.spacedBy(10.dp)) {

View File

@@ -43,6 +43,7 @@ import androidx.compose.runtime.produceState
import androidx.compose.runtime.remember
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.input.nestedscroll.nestedScroll
@@ -88,7 +89,11 @@ fun InstallScreen(navigator: DestinationsNavigator) {
showLkmWarning = false
navigator.popBackStack()
},
title = { Text(stringResource(R.string.warning)) },
title = { Text(
text = stringResource(R.string.warning),
style = MaterialTheme.typography.titleLarge,
fontWeight = FontWeight.SemiBold
) },
text = { Text(stringResource(R.string.lkm_warning_message)) },
confirmButton = {
TextButton(onClick = { showLkmWarning = false }) {
@@ -234,7 +239,7 @@ private fun SelectInstallMethod(onSelected: (InstallMethod) -> Unit = {}) {
val rootAvailable = rootAvailable()
val isAbDevice = isAbDevice()
val selectFileTip = stringResource(
id = R.string.select_file_tip, if (isInitBoot()) "init_boot" else "boot"
id = R.string.select_file_tip, if (isInitBoot()) "init_boot/vendor_boot" else "boot"
)
val radioOptions =
mutableListOf<InstallMethod>(InstallMethod.SelectFile(summary = selectFileTip))
@@ -370,7 +375,11 @@ private fun TopBar(
scrollBehavior: TopAppBarScrollBehavior? = null
) {
TopAppBar(
title = { Text(stringResource(R.string.install)) }, navigationIcon = {
title = { Text(
text = stringResource(R.string.install),
style = MaterialTheme.typography.titleLarge,
fontWeight = FontWeight.Black,
) }, navigationIcon = {
IconButton(
onClick = onBack
) { Icon(Icons.AutoMirrored.Filled.ArrowBack, contentDescription = null) }

View File

@@ -77,6 +77,12 @@ import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.runtime.snapshotFlow
import androidx.compose.animation.scaleIn
import androidx.compose.animation.scaleOut
import androidx.compose.animation.core.tween
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
@@ -95,7 +101,6 @@ import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import androidx.core.net.toUri
import androidx.lifecycle.viewmodel.compose.viewModel
import com.dergoogler.mmrl.platform.Platform
import com.ramcosta.composedestinations.annotation.Destination
import com.ramcosta.composedestinations.annotation.RootGraph
import com.ramcosta.composedestinations.generated.destinations.ExecuteModuleActionScreenDestination
@@ -123,10 +128,11 @@ import com.rifsxd.ksunext.ui.util.reboot
import com.rifsxd.ksunext.ui.util.toggleModule
import com.rifsxd.ksunext.ui.util.uninstallModule
import com.rifsxd.ksunext.ui.util.restoreModule
import com.rifsxd.ksunext.ui.util.zygiskRequired
import com.rifsxd.ksunext.ui.viewmodel.ModuleViewModel
import com.rifsxd.ksunext.ui.webui.WebUIActivity
import com.rifsxd.ksunext.ui.webui.WebUIXActivity
import com.dergoogler.mmrl.ui.component.LabelItem
import com.dergoogler.mmrl.ui.component.LabelItemDefaults
import com.topjohnwu.superuser.io.SuFile
@OptIn(ExperimentalMaterial3Api::class)
@@ -165,10 +171,38 @@ fun ModuleScreen(navigator: DestinationsNavigator) {
contract = ActivityResultContracts.StartActivityForResult()
) { viewModel.fetchModuleList() }
val listState = rememberLazyListState()
var showFab by remember { mutableStateOf(true) }
LaunchedEffect(listState) {
var lastIndex = listState.firstVisibleItemIndex
var lastOffset = listState.firstVisibleItemScrollOffset
snapshotFlow { listState.firstVisibleItemIndex to listState.firstVisibleItemScrollOffset }
.collect { (currIndex, currOffset) ->
val isScrollingDown = currIndex > lastIndex ||
(currIndex == lastIndex && currOffset > lastOffset + 4)
val isScrollingUp = currIndex < lastIndex ||
(currIndex == lastIndex && currOffset < lastOffset - 4)
when {
isScrollingDown && showFab -> showFab = false
isScrollingUp && !showFab -> showFab = true
}
lastIndex = currIndex
lastOffset = currOffset
}
}
Scaffold(
topBar = {
SearchAppBar(
title = { Text(stringResource(R.string.module)) },
title = { Text(
text = stringResource(R.string.module),
style = MaterialTheme.typography.titleLarge,
fontWeight = FontWeight.Black,
) },
searchText = viewModel.search,
onSearchTextChange = { viewModel.search = it },
onClearClick = { viewModel.search = "" },
@@ -290,46 +324,58 @@ fun ModuleScreen(navigator: DestinationsNavigator) {
},
floatingActionButton = {
if (!hideInstallButton) {
val moduleInstall = stringResource(id = R.string.module_install)
val selectZipLauncher = rememberLauncherForActivityResult(
contract = ActivityResultContracts.StartActivityForResult()
) { result ->
if (result.resultCode != RESULT_OK) {
return@rememberLauncherForActivityResult
}
val data = result.data ?: return@rememberLauncherForActivityResult
val clipData = data.clipData
val uris = mutableListOf<Uri>()
if (clipData != null) {
for (i in 0 until clipData.itemCount) {
clipData.getItemAt(i)?.uri?.let { uris.add(it) }
AnimatedVisibility(
visible = showFab,
enter = scaleIn(
animationSpec = tween(200),
initialScale = 0.8f
) + fadeIn(animationSpec = tween(400)),
exit = scaleOut(
animationSpec = tween(200),
targetScale = 0.8f
) + fadeOut(animationSpec = tween(400))
) {
val moduleInstall = stringResource(id = R.string.module_install)
val selectZipLauncher = rememberLauncherForActivityResult(
contract = ActivityResultContracts.StartActivityForResult()
) { result ->
if (result.resultCode != RESULT_OK) {
return@rememberLauncherForActivityResult
}
} else {
data.data?.let { uris.add(it) }
val data = result.data ?: return@rememberLauncherForActivityResult
val clipData = data.clipData
val uris = mutableListOf<Uri>()
if (clipData != null) {
for (i in 0 until clipData.itemCount) {
clipData.getItemAt(i)?.uri?.let { uris.add(it) }
}
} else {
data.data?.let { uris.add(it) }
}
if (uris.isEmpty()) return@rememberLauncherForActivityResult
viewModel.updateZipUris(uris)
navigator.navigate(FlashScreenDestination(FlashIt.FlashModules(uris)))
viewModel.clearZipUris()
viewModel.markNeedRefresh()
}
if (uris.isEmpty()) return@rememberLauncherForActivityResult
viewModel.updateZipUris(uris)
navigator.navigate(FlashScreenDestination(FlashIt.FlashModules(uris)))
viewModel.clearZipUris()
viewModel.markNeedRefresh()
ExtendedFloatingActionButton(
onClick = {
// Select the zip files to install
val intent = Intent(Intent.ACTION_GET_CONTENT).apply {
type = "application/zip"
putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true)
}
selectZipLauncher.launch(intent)
},
icon = { Icon(Icons.Filled.Add, moduleInstall) },
text = { Text(text = moduleInstall) },
)
}
ExtendedFloatingActionButton(
onClick = {
// Select the zip files to install
val intent = Intent(Intent.ACTION_GET_CONTENT).apply {
type = "application/zip"
putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true)
}
selectZipLauncher.launch(intent)
},
icon = { Icon(Icons.Filled.Add, moduleInstall) },
text = { Text(text = moduleInstall) },
)
}
},
contentWindowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal),
@@ -362,27 +408,17 @@ fun ModuleScreen(navigator: DestinationsNavigator) {
},
onClickModule = { id, name, hasWebUi ->
if (hasWebUi) {
val wxEngine = Intent(context, WebUIXActivity::class.java)
.setData("kernelsu://webuix/$id".toUri())
.putExtra("id", id)
.putExtra("name", name)
val ksuEngine = Intent(context, WebUIActivity::class.java)
.setData("kernelsu://webui/$id".toUri())
.putExtra("id", id)
.putExtra("name", name)
webUILauncher.launch(
if (prefs.getBoolean("use_webuix", true) && Platform.isAlive) {
wxEngine
} else {
ksuEngine
}
Intent(context, WebUIActivity::class.java)
.setData(Uri.parse("kernelsu://webui/$id"))
.putExtra("id", id)
.putExtra("name", name)
)
}
},
context = context,
snackBarHost = snackBarHost
snackBarHost = snackBarHost,
listState = listState
)
}
}
@@ -400,6 +436,7 @@ private fun ModuleList(
onClickModule: (id: String, name: String, hasWebUi: Boolean) -> Unit,
context: Context,
snackBarHost: SnackbarHostState,
listState: LazyListState
) {
val failedEnable = stringResource(R.string.module_failed_to_enable)
val failedDisable = stringResource(R.string.module_failed_to_disable)
@@ -411,11 +448,13 @@ private fun ModuleList(
val rebootToApply = stringResource(R.string.reboot_to_apply)
val moduleStr = stringResource(R.string.module)
val uninstall = stringResource(R.string.uninstall)
val uninstalled = stringResource(R.string.uninstalled)
val restore = stringResource(R.string.restore)
val cancel = stringResource(android.R.string.cancel)
val moduleUninstallConfirm = stringResource(R.string.module_uninstall_confirm)
val moduleRestoreConfirm = stringResource(R.string.module_restore_confirm)
val updateText = stringResource(R.string.module_update)
val updateLable = stringResource(R.string.module_update_available)
val changelogText = stringResource(R.string.module_changelog)
val downloadingText = stringResource(R.string.module_downloading)
val startDownloadingText = stringResource(R.string.module_start_downloading)
@@ -564,22 +603,25 @@ private fun ModuleList(
}
PullToRefreshBox(
modifier = boxModifier,
isRefreshing = viewModel.isRefreshing,
onRefresh = {
viewModel.fetchModuleList()
},
isRefreshing = viewModel.isRefreshing
}
) {
LazyColumn(
modifier = modifier,
state = listState,
modifier = Modifier
.fillMaxSize()
.nestedScroll(TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState()).nestedScrollConnection),
verticalArrangement = Arrangement.spacedBy(16.dp),
contentPadding = remember {
PaddingValues(
start = 16.dp,
top = 16.dp,
end = 16.dp,
bottom = 16.dp + 56.dp + 16.dp + 48.dp + 6.dp /* Scaffold Fab Spacing + Fab container height + SnackBar height */
bottom = 16.dp
)
},
}
) {
when {
viewModel.moduleList.isEmpty() -> {
@@ -595,7 +637,6 @@ private fun ModuleList(
}
}
}
else -> {
items(viewModel.moduleList) { module ->
val scope = rememberCoroutineScope()
@@ -668,7 +709,6 @@ private fun ModuleList(
}
DownloadListener(context, onInstallModule)
}
}
@@ -783,6 +823,8 @@ fun ModuleItem(
)
}
val filterZygiskModules = Natives.isZygiskEnabled() || !module.zygiskRequired
LaunchedEffect(Unit) {
developerOptionsEnabled = prefs.getBoolean("enable_developer_options", false)
}
@@ -811,15 +853,24 @@ fun ModuleItem(
) {
LabelItem(
text = formatSize(module.size),
style = com.dergoogler.mmrl.ui.component.LabelItemDefaults.style.copy(
style = LabelItemDefaults.style.copy(
containerColor = MaterialTheme.colorScheme.secondaryContainer,
contentColor = MaterialTheme.colorScheme.onSecondaryContainer
)
)
if (module.remove) {
LabelItem(
text = stringResource(R.string.uninstall),
style = com.dergoogler.mmrl.ui.component.LabelItemDefaults.style.copy(
text = stringResource(R.string.uninstalled),
style = LabelItemDefaults.style.copy(
containerColor = MaterialTheme.colorScheme.errorContainer,
contentColor = MaterialTheme.colorScheme.onErrorContainer
)
)
}
if (!Natives.isZygiskEnabled() && module.zygiskRequired && !module.remove) {
LabelItem(
text = stringResource(R.string.zygisk_required),
style = LabelItemDefaults.style.copy(
containerColor = MaterialTheme.colorScheme.errorContainer,
contentColor = MaterialTheme.colorScheme.onErrorContainer
)
@@ -827,8 +878,8 @@ fun ModuleItem(
}
if (updateUrl.isNotEmpty() && !module.remove && !module.update) {
LabelItem(
text = stringResource(R.string.module_update),
style = com.dergoogler.mmrl.ui.component.LabelItemDefaults.style.copy(
text = stringResource(R.string.module_update_available),
style = LabelItemDefaults.style.copy(
containerColor = MaterialTheme.colorScheme.tertiaryContainer,
contentColor = MaterialTheme.colorScheme.onTertiaryContainer
)
@@ -838,7 +889,7 @@ fun ModuleItem(
if (module.update) {
LabelItem(
text = stringResource(R.string.module_updated),
style = com.dergoogler.mmrl.ui.component.LabelItemDefaults.style.copy(
style = LabelItemDefaults.style.copy(
containerColor = MaterialTheme.colorScheme.tertiaryContainer,
contentColor = MaterialTheme.colorScheme.onTertiaryContainer
)
@@ -846,19 +897,19 @@ fun ModuleItem(
}
}
if (module.enabled && !module.remove) {
if (module.hasWebUi) {
if (module.hasWebUi && filterZygiskModules) {
LabelItem(
text = stringResource(R.string.webui),
style = com.dergoogler.mmrl.ui.component.LabelItemDefaults.style.copy(
style = LabelItemDefaults.style.copy(
containerColor = MaterialTheme.colorScheme.primaryContainer,
contentColor = MaterialTheme.colorScheme.onPrimaryContainer
)
)
}
if (module.hasActionScript) {
if (module.hasActionScript && filterZygiskModules) {
LabelItem(
text = stringResource(R.string.action),
style = com.dergoogler.mmrl.ui.component.LabelItemDefaults.style.copy(
style = LabelItemDefaults.style.copy(
containerColor = MaterialTheme.colorScheme.secondaryContainer,
contentColor = MaterialTheme.colorScheme.onSecondaryContainer
)
@@ -961,7 +1012,7 @@ fun ModuleItem(
if (module.hasActionScript) {
FilledTonalButton(
modifier = Modifier.defaultMinSize(52.dp, 32.dp),
enabled = !module.remove && module.enabled,
enabled = !module.remove && module.enabled && filterZygiskModules,
onClick = {
navigator.navigate(ExecuteModuleActionScreenDestination(module.dirId))
viewModel.markNeedRefresh()
@@ -989,7 +1040,7 @@ fun ModuleItem(
if (module.hasWebUi) {
FilledTonalButton(
modifier = Modifier.defaultMinSize(52.dp, 32.dp),
enabled = !module.remove && module.enabled,
enabled = !module.remove && module.enabled && filterZygiskModules,
onClick = { onClick(module) },
interactionSource = interactionSource,
contentPadding = ButtonDefaults.TextButtonContentPadding
@@ -1012,7 +1063,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,
@@ -1089,6 +1140,7 @@ fun ModuleItem(
}
fun formatSize(size: Long): String {
if (size == 0L) return "null"
val kb = 1024
val mb = kb * 1024
val gb = mb * 1024
@@ -1118,7 +1170,8 @@ fun ModuleItemPreview() {
hasActionScript = false,
dirId = "dirId",
size = 12345678L,
banner = ""
banner = "",
zygiskRequired = false
)
ModuleItem(EmptyDestinationsNavigator, module, "", {}, {}, {}, {}, {}, false, {})
}

View File

@@ -35,6 +35,7 @@ import androidx.compose.material3.TopAppBar
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.material3.TopAppBarScrollBehavior
import androidx.compose.material3.rememberTopAppBarState
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.LaunchedEffect
@@ -43,6 +44,7 @@ import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.vector.ImageVector
@@ -54,7 +56,6 @@ import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.core.content.FileProvider
import com.dergoogler.mmrl.platform.Platform
import com.maxkeppeker.sheets.core.models.base.Header
import com.maxkeppeker.sheets.core.models.base.IconSource
import com.maxkeppeker.sheets.core.models.base.rememberUseCaseState
@@ -149,7 +150,11 @@ fun SettingScreen(navigator: DestinationsNavigator) {
if (ksuVersion != null) {
ListItem(
leadingContent = { Icon(Icons.Filled.Fence, profileTemplate) },
headlineContent = { Text(profileTemplate) },
headlineContent = { Text(
text = profileTemplate,
style = MaterialTheme.typography.titleMedium,
fontWeight = FontWeight.SemiBold
) },
supportingContent = { Text(stringResource(id = R.string.settings_profile_template_summary)) },
modifier = Modifier.clickable {
navigator.navigate(AppProfileTemplateScreenDestination)
@@ -260,7 +265,11 @@ fun SettingScreen(navigator: DestinationsNavigator) {
if (showRebootDialog) {
AlertDialog(
onDismissRequest = { showRebootDialog = false },
title = { Text(stringResource(R.string.reboot_required)) },
title = { Text(
text = stringResource(R.string.reboot_required),
style = MaterialTheme.typography.titleLarge,
fontWeight = FontWeight.SemiBold
) },
text = { Text(stringResource(R.string.reboot_message)) },
confirmButton = {
TextButton(onClick = {
@@ -304,7 +313,11 @@ fun SettingScreen(navigator: DestinationsNavigator) {
shrink
)
},
headlineContent = { Text(shrink) },
headlineContent = { Text(
text = shrink,
style = MaterialTheme.typography.titleMedium,
fontWeight = FontWeight.SemiBold
) },
modifier = Modifier.clickable {
scope.launch {
val result = shrinkDialog.awaitConfirm(title = shrink, content = shrinkMessage)
@@ -326,7 +339,11 @@ fun SettingScreen(navigator: DestinationsNavigator) {
customization
)
},
headlineContent = { Text(customization) },
headlineContent = { Text(
text = customization,
style = MaterialTheme.typography.titleMedium,
fontWeight = FontWeight.SemiBold
) },
modifier = Modifier.clickable {
navigator.navigate(CustomizationScreenDestination)
}
@@ -341,7 +358,11 @@ fun SettingScreen(navigator: DestinationsNavigator) {
backupRestore
)
},
headlineContent = { Text(backupRestore) },
headlineContent = { Text(
text = backupRestore,
style = MaterialTheme.typography.titleMedium,
fontWeight = FontWeight.SemiBold
) },
modifier = Modifier.clickable {
navigator.navigate(BackupRestoreScreenDestination)
}
@@ -349,18 +370,24 @@ 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(
text = developer,
style = MaterialTheme.typography.titleMedium,
fontWeight = FontWeight.SemiBold
) },
modifier = Modifier.clickable {
navigator.navigate(DeveloperScreenDestination)
}
)
}
val lkmMode = Natives.version >= Natives.MINIMAL_SUPPORTED_KERNEL_LKM && Natives.isLkmMode
if (lkmMode) {
@@ -378,7 +405,11 @@ fun SettingScreen(navigator: DestinationsNavigator) {
stringResource(id = R.string.export_log)
)
},
headlineContent = { Text(stringResource(id = R.string.export_log)) },
headlineContent = { Text(
text = stringResource(id = R.string.export_log),
style = MaterialTheme.typography.titleMedium,
fontWeight = FontWeight.SemiBold
) },
modifier = Modifier.clickable {
showBottomsheet = true
}
@@ -486,7 +517,11 @@ fun SettingScreen(navigator: DestinationsNavigator) {
about
)
},
headlineContent = { Text(about) },
headlineContent = { Text(
text = about,
style = MaterialTheme.typography.titleMedium,
fontWeight = FontWeight.SemiBold
) },
modifier = Modifier.clickable {
aboutDialog.show()
}
@@ -536,7 +571,11 @@ fun UninstallItem(
uninstall
)
},
headlineContent = { Text(uninstall) },
headlineContent = { Text(
text = uninstall,
style = MaterialTheme.typography.titleMedium,
fontWeight = FontWeight.SemiBold
) },
modifier = Modifier.clickable {
uninstallDialog.show()
}
@@ -603,7 +642,11 @@ private fun TopBar(
scrollBehavior: TopAppBarScrollBehavior? = null,
) {
TopAppBar(
title = { Text(stringResource(R.string.settings)) },
title = { Text(
text = stringResource(R.string.settings),
style = MaterialTheme.typography.titleLarge,
fontWeight = FontWeight.Black,
) },
windowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal),
scrollBehavior = scrollBehavior
)

View File

@@ -22,10 +22,10 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.compose.ui.text.font.FontWeight
import androidx.lifecycle.viewmodel.compose.viewModel
import coil.compose.AsyncImage
import coil.request.ImageRequest
import com.dergoogler.mmrl.platform.Platform
import com.dergoogler.mmrl.ui.component.LabelItem
import com.dergoogler.mmrl.ui.component.LabelItemDefaults
import com.ramcosta.composedestinations.annotation.Destination
@@ -57,7 +57,11 @@ fun SuperUserScreen(navigator: DestinationsNavigator) {
Scaffold(
topBar = {
SearchAppBar(
title = { Text(stringResource(R.string.superuser)) },
title = { Text(
text = stringResource(R.string.superuser),
style = MaterialTheme.typography.titleLarge,
fontWeight = FontWeight.Black,
) },
searchText = viewModel.search,
onSearchTextChange = { viewModel.search = it },
onClearClick = { viewModel.search = "" },
@@ -134,10 +138,17 @@ private fun AppItem(
) {
ListItem(
modifier = Modifier.clickable(onClick = onClickListener),
headlineContent = { Text(app.label) },
headlineContent = { Text(
text = app.label,
style = MaterialTheme.typography.titleMedium,
fontWeight = FontWeight.SemiBold,
) },
supportingContent = {
Column {
Text(app.packageName)
Text(
text = app.packageName,
style = MaterialTheme.typography.bodySmall
)
Spacer(modifier = Modifier.height(4.dp))
@@ -163,10 +174,18 @@ private fun AppItem(
LabelItem(
text = "CUSTOM",
style = LabelItemDefaults.style.copy(
containerColor = MaterialTheme.colorScheme.onTertiary,
containerColor = MaterialTheme.colorScheme.tertiaryContainer,
contentColor = MaterialTheme.colorScheme.onTertiaryContainer,
)
)
} else if (!app.allowSu && !Natives.uidShouldUmount(app.uid)) {
LabelItem(
text = "DEFAULT",
style = LabelItemDefaults.style.copy(
containerColor = MaterialTheme.colorScheme.primaryContainer,
contentColor = MaterialTheme.colorScheme.onPrimaryContainer,
)
)
}
}
}

View File

@@ -14,6 +14,9 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.safeDrawing
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.height
import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.ArrowBack
@@ -42,6 +45,7 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.Modifier
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.LocalClipboardManager
@@ -51,6 +55,7 @@ import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.dropUnlessResumed
import androidx.lifecycle.viewmodel.compose.viewModel
import com.dergoogler.mmrl.ui.component.LabelItem
import com.ramcosta.composedestinations.annotation.Destination
import com.ramcosta.composedestinations.annotation.RootGraph
import com.ramcosta.composedestinations.generated.destinations.TemplateEditorScreenDestination
@@ -62,6 +67,15 @@ import kotlinx.coroutines.launch
import com.rifsxd.ksunext.R
import com.rifsxd.ksunext.ui.viewmodel.TemplateViewModel
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.runtime.snapshotFlow
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.scaleIn
import androidx.compose.animation.scaleOut
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.animation.core.tween
/**
* @author weishu
* @date 2023/10/20.
@@ -91,6 +105,30 @@ fun AppProfileTemplateScreen(
}
}
val listState = rememberLazyListState()
var showFab by remember { mutableStateOf(true) }
LaunchedEffect(listState) {
var lastIndex = listState.firstVisibleItemIndex
var lastOffset = listState.firstVisibleItemScrollOffset
snapshotFlow { listState.firstVisibleItemIndex to listState.firstVisibleItemScrollOffset }
.collect { (currIndex, currOffset) ->
val isScrollingDown = currIndex > lastIndex ||
(currIndex == lastIndex && currOffset > lastOffset + 4)
val isScrollingUp = currIndex < lastIndex ||
(currIndex == lastIndex && currOffset < lastOffset - 4)
when {
isScrollingDown && showFab -> showFab = false
isScrollingUp && !showFab -> showFab = true
}
lastIndex = currIndex
lastOffset = currOffset
}
}
Scaffold(
topBar = {
val clipboardManager = LocalClipboardManager.current
@@ -137,18 +175,30 @@ fun AppProfileTemplateScreen(
)
},
floatingActionButton = {
ExtendedFloatingActionButton(
onClick = {
navigator.navigate(
TemplateEditorScreenDestination(
TemplateViewModel.TemplateInfo(),
false
AnimatedVisibility(
visible = showFab,
enter = scaleIn(
animationSpec = tween(200),
initialScale = 0.8f
) + fadeIn(animationSpec = tween(400)),
exit = scaleOut(
animationSpec = tween(200),
targetScale = 0.8f
) + fadeOut(animationSpec = tween(400))
) {
ExtendedFloatingActionButton(
onClick = {
navigator.navigate(
TemplateEditorScreenDestination(
TemplateViewModel.TemplateInfo(),
false
)
)
)
},
icon = { Icon(Icons.Filled.Add, null) },
text = { Text(stringResource(id = R.string.app_profile_template_create)) },
)
},
icon = { Icon(Icons.Filled.Add, null) },
text = { Text(stringResource(id = R.string.app_profile_template_create)) },
)
}
},
contentWindowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal)
) { innerPadding ->
@@ -160,11 +210,12 @@ fun AppProfileTemplateScreen(
}
) {
LazyColumn(
state = listState,
modifier = Modifier
.fillMaxSize()
.nestedScroll(scrollBehavior.nestedScrollConnection),
contentPadding = remember {
PaddingValues(bottom = 16.dp + 56.dp + 16.dp /* Scaffold Fab Spacing + Fab container height */)
PaddingValues(bottom = 16.dp /* Scaffold Fab Spacing + Fab container height */)
}
) {
items(viewModel.templateList, key = { it.id }) { app ->
@@ -186,7 +237,11 @@ private fun TemplateItem(
.clickable {
navigator.navigate(TemplateEditorScreenDestination(template, !template.local))
},
headlineContent = { Text(template.name) },
headlineContent = { Text(
text = template.name,
style = MaterialTheme.typography.titleMedium,
fontWeight = FontWeight.SemiBold,
) },
supportingContent = {
Column {
Text(
@@ -195,14 +250,19 @@ private fun TemplateItem(
fontSize = MaterialTheme.typography.bodySmall.fontSize,
)
Text(template.description)
FlowRow {
LabelText(label = "UID: ${template.uid}")
LabelText(label = "GID: ${template.gid}")
LabelText(label = template.context)
Spacer(modifier = Modifier.height(4.dp))
FlowRow(
horizontalArrangement = Arrangement.spacedBy(4.dp)
) {
LabelItem(text = "UID: ${template.uid}")
LabelItem(text = "GID: ${template.gid}")
LabelItem(text = template.context)
if (template.local) {
LabelText(label = "local")
LabelItem(text = "local")
} else {
LabelText(label = "remote")
LabelItem(text = "remote")
}
}
}
@@ -221,7 +281,11 @@ private fun TopBar(
) {
TopAppBar(
title = {
Text(stringResource(R.string.settings_profile_template))
Text(
text = stringResource(R.string.settings_profile_template),
style = MaterialTheme.typography.titleLarge,
fontWeight = FontWeight.Black,
)
},
navigationIcon = {
IconButton(

View File

@@ -35,6 +35,7 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
import androidx.compose.ui.input.nestedscroll.nestedScroll
@@ -262,7 +263,11 @@ private fun TopBar(
TopAppBar(
title = {
Column {
Text(title)
Text(
text = title,
style = MaterialTheme.typography.titleLarge,
fontWeight = FontWeight.Black,
)
if (summary.isNotBlank()) {
Text(
text = summary,

View File

@@ -59,6 +59,9 @@ fun KernelSUTheme(
surfaceContainerLowest = dynamicScheme.surfaceContainerLowest.blend(AMOLED_BLACK, 0.6f),
surfaceContainerHigh = dynamicScheme.surfaceContainerHigh.blend(AMOLED_BLACK, 0.6f),
surfaceContainerHighest = dynamicScheme.surfaceContainerHighest.blend(AMOLED_BLACK, 0.6f),
primaryContainer = dynamicScheme.primaryContainer.blend(AMOLED_BLACK, 0.6f),
secondaryContainer = dynamicScheme.secondaryContainer.blend(AMOLED_BLACK, 0.6f),
tertiaryContainer = dynamicScheme.tertiaryContainer.blend(AMOLED_BLACK, 0.6f)
)
}
dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {

View File

@@ -28,6 +28,7 @@ import java.io.File
* @date 2023/1/1.
*/
private const val TAG = "KsuCli"
private const val BUSYBOX = "/data/adb/ksu/bin/busybox"
private fun ksuDaemonMagicPath(): String {
return ksuApp.applicationInfo.nativeLibraryDir + File.separator + "libksud_magic.so"
@@ -490,7 +491,7 @@ fun moduleBackup(): Boolean {
val internalBackupDir = "/sdcard/.ksunext/modules"
val internalBackupPath = "$internalBackupDir/$tarName"
val tarCmd = "tar -cpf $tarPath -C /data/adb/modules $(ls /data/adb/modules)"
val tarCmd = "$BUSYBOX tar -cpf $tarPath -C /data/adb/modules $(ls /data/adb/modules)"
val tarResult = ShellUtils.fastCmd(shell, tarCmd).trim()
if (tarResult.isNotEmpty()) return false
@@ -511,7 +512,7 @@ fun moduleRestore(): Boolean {
val tarPath = ShellUtils.fastCmd(shell, findTarCmd).trim()
if (tarPath.isEmpty()) return false
val extractCmd = "tar -xpf $tarPath -C /data/adb/modules_update"
val extractCmd = "$BUSYBOX tar -xpf $tarPath -C /data/adb/modules_update"
val extractResult = ShellUtils.fastCmd(shell, extractCmd).trim()
return extractResult.isEmpty()
}
@@ -533,7 +534,7 @@ fun allowlistBackup(): Boolean {
val internalBackupDir = "/sdcard/.ksunext/allowlist"
val internalBackupPath = "$internalBackupDir/$tarName"
val tarCmd = "tar -cpf $tarPath -C /data/adb/ksu .allowlist"
val tarCmd = "$BUSYBOX tar -cpf $tarPath -C /data/adb/ksu .allowlist"
val tarResult = ShellUtils.fastCmd(shell, tarCmd).trim()
if (tarResult.isNotEmpty()) return false
@@ -556,7 +557,7 @@ fun allowlistRestore(): Boolean {
if (tarPath.isEmpty()) return false
// Extract the tar to /data/adb/ksu (restores .allowlist folder with permissions)
val extractCmd = "tar -xpf $tarPath -C /data/adb/ksu"
val extractCmd = "$BUSYBOX tar -xpf $tarPath -C /data/adb/ksu"
val extractResult = ShellUtils.fastCmd(shell, extractCmd).trim()
return extractResult.isEmpty()
}
@@ -623,7 +624,7 @@ fun currentMountSystem(): String {
fun getModuleSize(dir: File): Long {
val shell = getRootShell()
val cmd = "du -sb '${dir.absolutePath}' | awk '{print \$1}'"
val cmd = "$BUSYBOX du -sb '${dir.absolutePath}' | awk '{print \$1}'"
val result = ShellUtils.fastCmd(shell, cmd).trim()
return result.toLongOrNull() ?: 0L
}
@@ -632,6 +633,14 @@ fun isSuCompatDisabled(): Boolean {
return Natives.version >= Natives.MINIMAL_SUPPORTED_SU_COMPAT && !Natives.isSuEnabled()
}
fun zygiskRequired(dir: File): Boolean {
val shell = getRootShell()
val zygiskLib = "${dir.absolutePath}/zygisk"
val cmd = "ls \"$zygiskLib\""
val result = ShellUtils.fastCmdResult(shell, cmd)
return result
}
fun setAppProfileTemplate(id: String, template: String): Boolean {
val shell = getRootShell()
val escapedTemplate = template.replace("\"", "\\\"")

View File

@@ -0,0 +1,149 @@
package com.rifsxd.ksunext.ui.util
import android.annotation.TargetApi
import android.app.Activity
import android.content.Context
import android.content.Intent
import android.content.res.Configuration
import android.net.Uri
import android.os.Build
import android.provider.Settings
import java.util.Locale
object LocaleHelper {
/**
* Check if should use system language settings (Android 13+)
*/
val useSystemLanguageSettings: Boolean
get() = Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU
/**
* Launch system app locale settings (Android 13+)
*/
fun launchSystemLanguageSettings(context: Context) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
try {
val intent = Intent(Settings.ACTION_APP_LOCALE_SETTINGS).apply {
data = Uri.fromParts("package", context.packageName, null)
}
context.startActivity(intent)
} catch (e: Exception) {
// Fallback to app language settings if system settings not available
}
}
}
/**
* Apply saved language setting to context (for Android < 13)
*/
fun applyLanguage(context: Context): Context {
// On Android 13+, language is handled by system
if (useSystemLanguageSettings) {
return context
}
val prefs = context.getSharedPreferences("settings", Context.MODE_PRIVATE)
val localeTag = prefs.getString("app_locale", "system") ?: "system"
return if (localeTag == "system") {
context
} else {
val locale = parseLocaleTag(localeTag)
setLocale(context, locale)
}
}
/**
* Set locale for context (Android < 13)
*/
private fun setLocale(context: Context, locale: Locale): Context {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
updateResources(context, locale)
} else {
updateResourcesLegacy(context, locale)
}
}
@TargetApi(Build.VERSION_CODES.N)
private fun updateResources(context: Context, locale: Locale): Context {
val configuration = Configuration()
configuration.setLocale(locale)
configuration.setLayoutDirection(locale)
return context.createConfigurationContext(configuration)
}
@SuppressWarnings("deprecation")
private fun updateResourcesLegacy(context: Context, locale: Locale): Context {
Locale.setDefault(locale)
val resources = context.resources
val configuration = resources.configuration
configuration.locale = locale
configuration.setLayoutDirection(locale)
resources.updateConfiguration(configuration, resources.displayMetrics)
return context
}
/**
* Parse locale tag to Locale object
*/
private fun parseLocaleTag(tag: String): Locale {
return try {
if (tag.contains("_")) {
val parts = tag.split("_")
Locale.Builder()
.setLanguage(parts[0])
.setRegion(parts.getOrNull(1) ?: "")
.build()
} else {
Locale.Builder()
.setLanguage(tag)
.build()
}
} catch (e: Exception) {
Locale.getDefault()
}
}
/**
* Restart activity to apply language change (Android < 13)
*/
fun restartActivity(context: Context) {
if (context is Activity && !useSystemLanguageSettings) {
context.recreate()
}
}
/**
* Get current app locale
*/
fun getCurrentAppLocale(context: Context): Locale? {
return if (useSystemLanguageSettings) {
// Android 13+ - get from system app locale settings
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
try {
val localeManager = context.getSystemService(Context.LOCALE_SERVICE) as? android.app.LocaleManager
val locales = localeManager?.applicationLocales
if (locales != null && !locales.isEmpty) {
locales.get(0)
} else {
null // System default
}
} catch (e: Exception) {
null // System default
}
} else {
null // System default
}
} else {
// Android < 13 - get from SharedPreferences
val prefs = context.getSharedPreferences("settings", Context.MODE_PRIVATE)
val localeTag = prefs.getString("app_locale", "system") ?: "system"
if (localeTag == "system") {
null // System default
} else {
parseLocaleTag(localeTag)
}
}
}
}

View File

@@ -9,8 +9,6 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.dergoogler.mmrl.platform.Platform
import com.dergoogler.mmrl.platform.TIMEOUT_MILLIS
import kotlinx.coroutines.delay
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
@@ -23,6 +21,7 @@ import com.rifsxd.ksunext.ksuApp
import com.rifsxd.ksunext.ui.util.HanziToPinyin
import com.rifsxd.ksunext.ui.util.listModules
import com.rifsxd.ksunext.ui.util.getModuleSize
import com.rifsxd.ksunext.ui.util.zygiskRequired
import org.json.JSONArray
import org.json.JSONObject
@@ -48,7 +47,8 @@ class ModuleViewModel : ViewModel() {
val hasActionScript: Boolean,
val dirId: String,
val size: Long,
val banner: String
val banner: String,
val zygiskRequired: Boolean
)
data class ModuleUpdateInfo(
@@ -108,21 +108,9 @@ class ModuleViewModel : ViewModel() {
viewModelScope.launch {
withContext(Dispatchers.Main) {
isRefreshing = true
}
isRefreshing = true
withContext(Dispatchers.IO) {
withTimeoutOrNull(TIMEOUT_MILLIS) {
while (!Platform.isAlive) {
delay(500)
}
} ?: run {
isRefreshing = false
Log.e(TAG, "Platform is not alive, aborting fetchModuleList")
return@withContext
}
val start = SystemClock.elapsedRealtime()
val oldModuleList = modules
@@ -139,6 +127,7 @@ class ModuleViewModel : ViewModel() {
val dirId = obj.getString("dir_id")
val moduleDir = File("/data/adb/modules/$dirId")
val size = getModuleSize(moduleDir)
val zygiskRequired = zygiskRequired(moduleDir)
ModuleInfo(
id,
@@ -155,7 +144,8 @@ class ModuleViewModel : ViewModel() {
obj.optBoolean("action"),
dirId,
size,
obj.optString("banner")
obj.optString("banner"),
zygiskRequired
)
}.toList()
isNeedRefresh = false

View File

@@ -6,6 +6,7 @@ import android.content.Intent
import android.content.ServiceConnection
import android.content.pm.ApplicationInfo
import android.content.pm.PackageInfo
import android.os.IBinder
import android.os.Parcelable
import android.os.SystemClock
import android.util.Log
@@ -15,15 +16,16 @@ import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.lifecycle.ViewModel
import com.dergoogler.mmrl.platform.Platform
import com.dergoogler.mmrl.platform.TIMEOUT_MILLIS
import com.topjohnwu.superuser.Shell
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import kotlinx.parcelize.Parcelize
import com.rifsxd.ksunext.IKsuInterface
import com.rifsxd.ksunext.Natives
import com.rifsxd.ksunext.ksuApp
import com.rifsxd.ksunext.ui.KsuService
import com.rifsxd.ksunext.ui.util.HanziToPinyin
import com.rifsxd.ksunext.ui.webui.getInstalledPackagesAll
import com.rifsxd.ksunext.ui.util.KsuCli
import kotlinx.coroutines.delay
import kotlinx.coroutines.withTimeoutOrNull
import java.text.Collator
@@ -33,7 +35,6 @@ import kotlin.coroutines.suspendCoroutine
import androidx.core.content.edit
class SuperUserViewModel : ViewModel() {
val isPlatformAlive get() = Platform.isAlive
companion object {
private const val TAG = "SuperUserViewModel"
@@ -117,24 +118,56 @@ class SuperUserViewModel : ViewModel() {
}
}
private suspend inline fun connectKsuService(
crossinline onDisconnect: () -> Unit = {}
): Pair<IBinder, ServiceConnection> = suspendCoroutine {
val connection = object : ServiceConnection {
override fun onServiceDisconnected(name: ComponentName?) {
onDisconnect()
}
override fun onServiceConnected(name: ComponentName?, binder: IBinder?) {
it.resume(binder as IBinder to this)
}
}
val intent = Intent(ksuApp, KsuService::class.java)
val task = KsuService.bindOrTask(
intent,
Shell.EXECUTOR,
connection,
)
val shell = KsuCli.SHELL
task?.let { it1 -> shell.execTask(it1) }
}
private fun stopKsuService() {
val intent = Intent(ksuApp, KsuService::class.java)
KsuService.stop(intent)
}
suspend fun fetchAppList() {
isRefreshing = true
withContext(Dispatchers.IO) {
withTimeoutOrNull(TIMEOUT_MILLIS) {
while (!isPlatformAlive) {
delay(500)
}
} ?: return@withContext // Exit early if timeout
val result = connectKsuService {
Log.w(TAG, "KsuService disconnected")
}
withContext(Dispatchers.IO) {
val pm = ksuApp.packageManager
val start = SystemClock.elapsedRealtime()
val packages = Platform.getInstalledPackagesAll {
Log.e(TAG, "getInstalledPackagesAll:", it)
Toast.makeText(ksuApp, "Something went wrong, check logs", Toast.LENGTH_SHORT).show()
val binder = result.first
val allPackages = IKsuInterface.Stub.asInterface(binder).getPackages(0)
withContext(Dispatchers.Main) {
stopKsuService()
}
val packages = allPackages.list
apps = packages.map {
val appInfo = it.applicationInfo
val uid = appInfo!!.uid
@@ -145,7 +178,6 @@ class SuperUserViewModel : ViewModel() {
profile = profile,
)
}.filter { it.packageName != ksuApp.packageName }
profileOverrides = emptyMap()
Log.i(TAG, "load cost: ${SystemClock.elapsedRealtime() - start}")
}
}

View File

@@ -1,73 +0,0 @@
package com.rifsxd.ksunext.ui.webui
import android.content.ServiceConnection
import android.content.pm.PackageInfo
import android.util.Log
import com.dergoogler.mmrl.platform.Platform
import com.dergoogler.mmrl.platform.hiddenApi.HiddenPackageManager
import com.dergoogler.mmrl.platform.hiddenApi.HiddenUserManager
import com.dergoogler.mmrl.platform.model.IProvider
import com.dergoogler.mmrl.platform.model.PlatformIntent
import com.rifsxd.ksunext.Natives
import com.rifsxd.ksunext.ksuApp
import com.topjohnwu.superuser.ipc.RootService
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.withContext
class KsuLibSuProvider : IProvider {
override val name = "KsuLibSu"
override fun isAvailable() = true
override suspend fun isAuthorized() = Natives.becomeManager(ksuApp.packageName)
private val serviceIntent
get() = PlatformIntent(
ksuApp,
Platform.KsuNext,
SuService::class.java
)
override fun bind(connection: ServiceConnection) {
RootService.bind(serviceIntent.intent, connection)
}
override fun unbind(connection: ServiceConnection) {
RootService.stop(serviceIntent.intent)
}
}
suspend fun initPlatform() = withContext(Dispatchers.IO) {
try {
val active = Platform.init {
this.context = ksuApp
this.platform = Platform.KsuNext
this.provider = from(KsuLibSuProvider())
}
while (!active) {
delay(1000)
}
return@withContext active
} catch (e: Exception) {
Log.e("KsuLibSu", "Failed to initialize platform", e)
return@withContext false
}
}
fun Platform.Companion.getInstalledPackagesAll(catch: (Exception) -> Unit = {}): List<PackageInfo> =
try {
val packages = mutableListOf<PackageInfo>()
val userInfos = userManager.getUsers()
for (userInfo in userInfos) {
packages.addAll(packageManager.getInstalledPackages(0, userInfo.id))
}
packages
} catch (e: Exception) {
catch(e)
packageManager.getInstalledPackages(0, userManager.myUserId)
}

View File

@@ -1,14 +0,0 @@
package com.rifsxd.ksunext.ui.webui
import android.content.Intent
import android.os.IBinder
import com.dergoogler.mmrl.platform.model.PlatformIntent.Companion.getPlatform
import com.dergoogler.mmrl.platform.service.ServiceManager
import com.topjohnwu.superuser.ipc.RootService
class SuService : RootService() {
override fun onBind(intent: Intent): IBinder {
val mode = intent.getPlatform()
return ServiceManager(mode)
}
}

View File

@@ -15,8 +15,6 @@ import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.updateLayoutParams
import androidx.webkit.WebViewAssetLoader
import com.dergoogler.mmrl.platform.model.ModId
import com.dergoogler.mmrl.webui.interfaces.WXOptions
import com.topjohnwu.superuser.Shell
import com.rifsxd.ksunext.ui.util.createRootShell
import java.io.File
@@ -41,10 +39,9 @@ class WebUIActivity : ComponentActivity() {
val name = intent.getStringExtra("name")!!
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
@Suppress("DEPRECATION")
setTaskDescription(ActivityManager.TaskDescription("KernelSU Next - $name"))
setTaskDescription(ActivityManager.TaskDescription("KSUNEXT - $name"))
} else {
val taskDescription =
ActivityManager.TaskDescription.Builder().setLabel("KernelSU Next - $name").build()
val taskDescription = ActivityManager.TaskDescription.Builder().setLabel("KSUNEXT - $name").build()
setTaskDescription(taskDescription)
}
@@ -65,7 +62,7 @@ class WebUIActivity : ComponentActivity() {
val webViewClient = object : WebViewClient() {
override fun shouldInterceptRequest(
view: WebView,
request: WebResourceRequest,
request: WebResourceRequest
): WebResourceResponse? {
return webViewAssetLoader.shouldInterceptRequest(request.url)
}
@@ -85,9 +82,7 @@ class WebUIActivity : ComponentActivity() {
settings.javaScriptEnabled = true
settings.domStorageEnabled = true
settings.allowFileAccess = false
webviewInterface = WebViewInterface(
WXOptions(this@WebUIActivity, this, ModId(moduleId))
)
webviewInterface = WebViewInterface(this@WebUIActivity, this, moduleDir)
addJavascriptInterface(webviewInterface, "ksu")
setWebViewClient(webViewClient)
loadUrl("https://mui.kernelsu.org/index.html")

View File

@@ -1,113 +0,0 @@
package com.rifsxd.ksunext.ui.webui
import android.app.ActivityManager
import android.os.Build
import android.os.Bundle
import android.webkit.WebView
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.lifecycle.lifecycleScope
import com.dergoogler.mmrl.platform.Platform
import com.dergoogler.mmrl.platform.model.ModId
import com.dergoogler.mmrl.ui.component.Loading
import com.dergoogler.mmrl.webui.screen.WebUIScreen
import com.dergoogler.mmrl.webui.util.rememberWebUIOptions
import com.rifsxd.ksunext.BuildConfig
import com.rifsxd.ksunext.ui.theme.KernelSUTheme
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
class WebUIXActivity : ComponentActivity() {
private lateinit var webView: WebView
private val userAgent
get(): String {
val ksuVersion = BuildConfig.VERSION_CODE
val platform = Platform.get("Unknown") {
platform.name
}
val platformVersion = Platform.get(-1) {
moduleManager.versionCode
}
val osVersion = Build.VERSION.RELEASE
val deviceModel = Build.MODEL
return "KernelSU Next/$ksuVersion (Linux; Android $osVersion; $deviceModel; $platform/$platformVersion)"
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
webView = WebView(this)
lifecycleScope.launch {
initPlatform()
}
val moduleId = intent.getStringExtra("id")!!
val name = intent.getStringExtra("name")!!
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
@Suppress("DEPRECATION")
setTaskDescription(ActivityManager.TaskDescription("KernelSU Next - $name"))
} else {
val taskDescription =
ActivityManager.TaskDescription.Builder().setLabel("KernelSU Next - $name").build()
setTaskDescription(taskDescription)
}
val prefs = getSharedPreferences("settings", MODE_PRIVATE)
setContent {
KernelSUTheme {
var isLoading by remember { mutableStateOf(true) }
LaunchedEffect(Platform.isAlive) {
while (!Platform.isAlive) {
delay(1000)
}
isLoading = false
}
if (isLoading) {
Loading()
return@KernelSUTheme
}
val webDebugging = prefs.getBoolean("enable_web_debugging", false)
val erudaInject = prefs.getBoolean("use_webuix_eruda", false)
val dark = isSystemInDarkTheme()
val options = rememberWebUIOptions(
modId = ModId(moduleId),
debug = webDebugging,
appVersionCode = BuildConfig.VERSION_CODE,
isDarkMode = dark,
enableEruda = erudaInject,
cls = WebUIXActivity::class.java,
userAgentString = userAgent
)
WebUIScreen(
webView = webView,
options = options,
interfaces = listOf(
WebViewInterface.factory()
)
)
}
}
}
}

View File

@@ -1,17 +1,16 @@
package com.rifsxd.ksunext.ui.webui
import android.app.Activity
import android.content.Context
import android.os.Handler
import android.os.Looper
import android.text.TextUtils
import android.view.Window
import android.webkit.JavascriptInterface
import android.webkit.WebView
import android.widget.Toast
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.WindowInsetsControllerCompat
import com.dergoogler.mmrl.webui.interfaces.WXInterface
import com.dergoogler.mmrl.webui.interfaces.WXOptions
import com.dergoogler.mmrl.webui.model.JavaScriptInterface
import com.topjohnwu.superuser.CallbackList
import com.topjohnwu.superuser.ShellUtils
import com.topjohnwu.superuser.internal.UiThreadHandler
@@ -24,15 +23,10 @@ import java.io.File
import java.util.concurrent.CompletableFuture
class WebViewInterface(
wxOptions: WXOptions,
) : WXInterface(wxOptions) {
override var name: String = "ksu"
companion object {
fun factory() = JavaScriptInterface(WebViewInterface::class.java)
}
private val modDir get() = "/data/adb/modules/${modId.id}"
val context: Context,
private val webView: WebView,
private val modDir: String
) {
@JavascriptInterface
fun exec(cmd: String): String {
@@ -65,7 +59,7 @@ class WebViewInterface(
fun exec(
cmd: String,
options: String?,
callbackFunc: String,
callbackFunc: String
) {
val finalCommand = StringBuilder()
processOptions(finalCommand, options)
@@ -174,9 +168,9 @@ class WebViewInterface(
if (context is Activity) {
Handler(Looper.getMainLooper()).post {
if (enable) {
hideSystemUI(activity.window)
hideSystemUI(context.window)
} else {
showSystemUI(activity.window)
showSystemUI(context.window)
}
}
}
@@ -185,7 +179,7 @@ class WebViewInterface(
@JavascriptInterface
fun moduleInfo(): String {
val moduleInfos = JSONArray(listModules())
val currentModuleInfo = JSONObject()
var currentModuleInfo = JSONObject()
currentModuleInfo.put("moduleDir", modDir)
val moduleId = File(modDir).getName()
for (i in 0 until moduleInfos.length()) {
@@ -195,7 +189,7 @@ class WebViewInterface(
continue
}
val keys = currentInfo.keys()
var keys = currentInfo.keys()
for (key in keys) {
currentModuleInfo.put(key, currentInfo.get(key))
}
@@ -208,12 +202,8 @@ class WebViewInterface(
fun hideSystemUI(window: Window) =
WindowInsetsControllerCompat(window, window.decorView).let { controller ->
controller.hide(WindowInsetsCompat.Type.systemBars())
controller.systemBarsBehavior =
WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
controller.systemBarsBehavior = WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
}
fun showSystemUI(window: Window) =
WindowInsetsControllerCompat(
window,
window.decorView
).show(WindowInsetsCompat.Type.systemBars())
WindowInsetsControllerCompat(window, window.decorView).show(WindowInsetsCompat.Type.systemBars())

View File

@@ -1,6 +1,6 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:width="22dp"
android:height="22dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path

View File

@@ -1,224 +1,228 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="issue_report_title">Hai problemi?</string>
<string name="issue_report_body">Hai riscontrato un bug o vuoi inviarci un feedback?</string>
<string name="issue_report_body_2">Segnalacelo il prima possibile!</string>
<string name="issue_report_github">Segnala su GitHub</string>
<string name="issue_report_telegram">Contatto tramite Telegram</string>
<string name="issue_report_github_link">https://github.com/KernelSU-Next/KernelSU-Next/issues</string>
<string name="issue_report_telegram_link">https://t.me/ksunext</string>
<string name="confirm">Confermare</string>
<string name="app_name" translatable="false">KernelSU Next</string>
<string name="home">Home</string>
<string name="home_not_installed">Non installato</string>
<string name="home_click_to_install">Clicca per installare</string>
<string name="lkm_mode_deprecated">La modalità LKM è ora obsoleta!</string>
<string name="lkm_alternative_suggestion">Installa il kernel GKI o integra KernelSU Next sul tuo dispositivo.</string>
<string name="home_working">In esecuzione</string>
<string name="home_working_version">Versione: %d</string>
<string name="home_superuser_count">Applicazioni con accesso root: %d</string>
<string name="home_module_count">Moduli: %d</string>
<string name="home_module_update_count">Aggiornamenti: %d</string>
<string name="home_failure">Firma KernelSU Next v2 non trovata nel kernel! [ !KSU_NEXT || != size/hash ]</string>
<string name="home_failure_tip">Chiedi allo sviluppatore del kernel di integrare KernelSU Next!</string>
<string name="home_kernel">Versione Kernel</string>
<string name="hook_mode">Modalità Hook</string>
<string name="enable">Abilita</string>
<string name="disable">Disabilita</string>
<string name="enabled">Abilitato</string>
<string name="disabled">Disabilitato</string>
<string name="susfs_supported">Supportato</string>
<string name="home_susfs">SuSFS: %s</string>
<string name="home_susfs_version">Versione SuSFS</string>
<string name="home_susfs_sus_su">SuS SU</string>
<string name="home_android">Versione Android</string>
<string name="home_manager_version">Versione del manager</string>
<string name="home_abi">ABI</string>
<string name="home_selinux_status">Stato di SELinux</string>
<string name="selinux_status_disabled">Disabilitato</string>
<string name="selinux_status_enforcing">Enforcing</string>
<string name="selinux_status_permissive">Permissive</string>
<string name="selinux_status_unknown">Sconosciuto</string>
<string name="superuser">Accesso root</string>
<string name="module_failed_to_enable">Impossibile abilitare il modulo: %s</string>
<string name="module_failed_to_disable">Impossibile disabilitare il modulo: %s</string>
<string name="module_empty">Nessun modulo installato</string>
<string name="module">Modulo</string>
<string name="module_install_prompt_with_name">Verranno installati i seguenti moduli: %1$s</string>
<string name="module_sort_a_to_z">Ordina (A → Z)</string>
<string name="module_sort_z_to_a">Ordina (Z → A)</string>
<string name="module_size_low_to_high">Ordina (Basso → Alto)</string>
<string name="module_size_high_to_low">Ordina (Alto → Basso)</string>
<string name="uninstall">Disinstalla</string>
<string name="restore">Ripristina</string>
<string name="module_install">Installa</string>
<string name="install">Installa</string>
<string name="reboot">Riavvia</string>
<string name="uninstalled">Disinstallato</string>
<string name="settings">Impostazioni</string>
<string name="reboot_userspace">Riavvio rapido</string>
<string name="reboot_recovery">Riavvia in Recovery</string>
<string name="reboot_bootloader">Riavvia in Bootloader</string>
<string name="reboot_download">Riavvia in Download</string>
<string name="reboot_edl">Riavvia in EDL</string>
<string name="about">Informazioni</string>
<string name="module_uninstall_confirm">Sei sicuro di voler disinstallare il modulo %s?</string>
<string name="module_uninstall_success">%s disinstallato</string>
<string name="module_uninstall_failed">Impossibile disinstallare: %s</string>
<string name="module_restore_confirm">Vuoi davvero ripristinare il modulo %s?</string>
<string name="module_restore_success">%s ripristinato</string>
<string name="module_restore_failed">Impossibile ripristinare: %s</string>
<string name="module_version">Versione</string>
<string name="module_author">Autore</string>
<string name="module_id">ID</string>
<string name="module_version_code">Codice</string>
<string name="module_update_json">UpdateJson</string>
<string name="module_update_json_empty">Vuoto</string>
<string name="enable_developer_options">Abilita le opzioni sviluppatore</string>
<string name="enable_developer_options_summary">Mostra le impostazioni nascoste e le informazioni di debug rilevanti solo per gli sviluppatori.</string>
<string name="module_overlay_fs_not_available">I moduli non sono disponibili in quanto overlayFS è disabilitato dal kernel.</string>
<string name="refresh">Ricarica</string>
<string name="show_system_apps">Mostra app di sistema</string>
<string name="hide_system_apps">Nascondi app di sistema</string>
<string name="export_log">Esportare i registri</string>
<string name="safe_mode">Modalità provvisoria</string>
<string name="reboot_to_apply">Riavvia per applicare la modifica</string>
<string name="module_magisk_conflict">I moduli sono disabilitati perché in conflitto con Magisk!</string>
<string name="home_mount_system">Modulo system</string>
<string name="home_magic_mount">Magic Mount</string>
<string name="home_overlayfs_mount">OverlayFS</string>
<string name="unavailable">Non disponibile</string>
<string name="use_overlay_fs">Usa OverlayFS</string>
<string name="use_overlay_fs_summary">Alterna l\'utilizzo di OverlayFS su Magic Mount per il sistema di montaggio di KernelSU Next.</string>
<string name="reboot_required">Riavvio richiesto</string>
<string name="reboot_message">Le modifiche avranno effetto dopo il riavvio del sistema. Vuoi riavviare ora?</string>
<string name="module_restore">Ripristina modulo</string>
<string name="module_restore_message">Ripristina i moduli dal backup recente.</string>
<string name="backup_restore">Backup &amp; Ripristino</string>
<string name="module_backup">Backup modulo</string>
<string name="module_backup_message">Esegui il backup dei moduli attualmente installati.</string>
<string name="allowlist_restore">Ripristina la lista consentita</string>
<string name="allowlist_restore_message">Ripristina la lista consentita dal backup recente.</string>
<string name="allowlist_backup">Backup lista consentita</string>
<string name="allowlist_backup_message">Backup della lista consentita attualmente configurata.</string>
<string name="warning">Avvertimento</string>
<string name="warning_message">Questa funzionalità è ancora in versione beta e in fase di sviluppo. Assicurati di effettuare il backup dei tuoi moduli prima di procedere. Utilizza questa funzionalità solo se ne comprendi i potenziali rischi. Procedi con cautela.</string>
<string name="proceed">Procedere</string>
<string name="cancel">Cancellare</string>
<string name="later">Dopo</string>
<string name="lkm_warning_message">La patch LKM si basa su componenti closed source. Vuoi continuare?</string>
<string name="home_next_kernelsu">🔥 Next build</string>
<string name="home_next_kernelsu_repo">https://github.com/KernelSU-Next/KernelSU-Next</string>
<string name="home_next_kernelsu_body">Branch sperimentale di Next. Dai un\'occhiata su GitHub!</string>
<string name="home_experimental_kernelsu">⚠️ Sviluppo sperimentale, attenzione!</string>
<string name="home_experimental_kernelsu_repo">127.0.0.1</string>
<string name="home_experimental_kernelsu_body">KernelSU Next è una versione non ufficiale che è sempre in fase di sviluppo sperimentale attivo. Viene fornita così com\'è, senza garanzie di stabilità, prestazioni o affidabilità.</string>
<string name="home_experimental_kernelsu_body_point_1"> • Usalo a tuo rischio e pericolo: potrebbero verificarsi crash, comportamenti imprevisti o problemi di sistema.</string>
<string name="home_experimental_kernelsu_body_point_2"> • Nessuna garanzia: gli sviluppatori non sono responsabili per eventuali perdite di dati, danni al sistema o altre conseguenze derivanti dal suo utilizzo.</string>
<string name="home_experimental_kernelsu_body_point_3"> • Solo a scopo di test: destinato agli utenti che comprendono i rischi e hanno dimestichezza con la risoluzione dei problemi.</string>
<string name="about_source_code"><![CDATA[Visualizza il codice sorgente su %1$s]]></string>
<string name="profile" translatable="false">Profilo dell\'App</string>
<string name="profile_default">Predefinito</string>
<string name="profile_template">Modello</string>
<string name="profile_custom">Personalizzato</string>
<string name="profile_name">Nome profilo</string>
<string name="profile_namespace">Spazio dei nomi del mount</string>
<string name="profile_namespace_inherited">Ereditato</string>
<string name="profile_namespace_global">Globale</string>
<string name="profile_namespace_individual">Individuale</string>
<string name="profile_groups">Gruppi</string>
<string name="profile_capabilities">Capacità</string>
<string name="profile_selinux_context">Contesto SELinux</string>
<string name="profile_umount_modules">Scollega moduli</string>
<string name="failed_to_update_app_profile">Aggiornamento App Profile per %s fallito</string>
<string name="require_kernel_version">La versione attualmente installata di KernelSU Next %1$d è troppo vecchia ed il gestore non può funzionare correttamente. Si prega di aggiornare alla versione %2$d o successiva!</string>
<string name="settings_umount_modules_default">Scollega moduli</string>
<string name="settings_umount_modules_default_summary">Il valore predefinito per \"Scollega moduli\" in App Profile. Se attivato, rimuoverà tutte le modifiche al sistema da parte dei moduli per le applicazioni che non hanno un profilo impostato.</string>
<string name="settings_susfs_toggle">Nascondi kprobes hook</string>
<string name="settings_susfs_toggle_summary">Questa opzione disabilita gli hook kprobe creati da ksu e, al loro posto, attiva gli hook non-kprobe incorporati, implementando la stessa funzionalità che verrebbe applicata a un kernel non-GKI, che non supporta kprobe.</string>
<string name="profile_umount_modules_summary">Attivando questa opzione permetterai a KernelSU Next di ripristinare ogni file modificato dai moduli per questa app.</string>
<string name="profile_selinux_domain">Dominio</string>
<string name="profile_selinux_rules">Regole</string>
<string name="module_update">Aggiorna</string>
<string name="module_updated">Aggiornato</string>
<string name="module_downloading">Sto scaricando il modulo: %s</string>
<string name="module_start_downloading">Inizia a scaricare: %s</string>
<string name="new_version_available">Nuova versione: %s disponibile, tocca per aggiornare.</string>
<string name="launch_app">Apri</string>
<string name="close">Chiudi</string>
<string name="force_stop_app">Arresto forzato</string>
<string name="restart_app">Riavvia</string>
<string name="settings_amoled_mode">Modalità AMOLED</string>
<string name="settings_amoled_mode_summary">Abilita un tema nero puro, utile per gli schermi AMOLED, per ridurre l\'affaticamento degli occhi e risparmiare batteria.</string>
<string name="restart_required">Riavvio richiesto</string>
<string name="restart_app_message">Affinché la modifica abbia effetto, è necessario riavviare l\'app.</string>
<string name="failed_to_update_sepolicy">Aggiornamento regole SELinux per %s fallito</string>
<string name="su_not_allowed">Non è consentito concedere i privilegi di superutente per: %s</string>
<string name="module_changelog">Registro aggiornamenti</string>
<string name="settings_profile_template">Modello App Profile</string>
<string name="settings_profile_template_summary">Gestisci il modello locale e online di App Profile</string>
<string name="app_profile_template_create">Crea modello</string>
<string name="app_profile_template_edit">Modifica modello</string>
<string name="app_profile_template_id">ID</string>
<string name="app_profile_template_id_invalid">Modello ID non valido</string>
<string name="app_profile_template_name">Nome</string>
<string name="app_profile_template_description">Descrizione</string>
<string name="app_profile_template_save">Salva</string>
<string name="app_profile_template_delete">Elimina</string>
<string name="app_profile_template_view">Visualizza modello</string>
<string name="app_profile_template_readonly">Sola lettura</string>
<string name="app_profile_template_id_exist">L\'ID del modello è già in uso!</string>
<string name="app_profile_import_export">Importa/Esporta</string>
<string name="app_profile_import_from_clipboard">Importa dagli appunti</string>
<string name="app_profile_export_to_clipboard">Esporta negli appunti</string>
<string name="app_profile_template_export_empty">Impossibile trovare un modello locale da esportare!</string>
<string name="app_profile_template_import_success">Importato con successo</string>
<string name="app_profile_template_sync">Sincronizza modelli online</string>
<string name="app_profile_template_save_failed">Impossibile salvare il modello</string>
<string name="app_profile_template_import_empty">Gli appunti sono vuoti!</string>
<string name="module_changelog_failed">Impossibile reperire il changelog: %s</string>
<string name="settings_check_update">Controlla aggiornamenti</string>
<string name="settings_check_update_summary">Controlla automaticamente gli aggiornamenti all\'apertura dell\'applicazione.</string>
<string name="grant_root_failed">Impossibile ottenere l\'accesso root!</string>
<string name="action">Azione</string>
<string name="webui">WebUI</string>
<string name="open">Apri</string>
<string name="enable_web_debugging">Abilita il debug di WebView</string>
<string name="enable_web_debugging_summary">Può essere usato per il debug di WebUI, è consigliato attivarlo solo quando necessario.</string>
<string name="direct_install">Installazione diretta (Raccomandata)</string>
<string name="select_file">Scegli un file</string>
<string name="install_inactive_slot">Installa nello slot inattivo (dopo OTA)</string>
<string name="install_inactive_slot_warning">Il tuo dispositivo sarà **FORZATO** ad avviarsi nello slot inattivo dopo il riavvio!\nUsa questa opzione solo quando l\'applicazione dell\'aggiornamento OTA è terminata.\nProcedere?</string>
<string name="install_next">Avanti</string>
<string name="select_file_tip">Si consiglia l\'immagine della partizione %1$s</string>
<string name="select_kmi">Scegli il KMI</string>
<string name="shrink_sparse_image">Riduci al minimo l\'immagine sparse</string>
<string name="shrink_sparse_image_message">Ridimensiona l\'immagine sparse dei moduli alla sua reale dimensione. Nota che questo potrebbe causare malfunzionamenti dei moduli, quindi utilizzala solo quando necessario (Come per il backup).</string>
<string name="settings_uninstall">Disinstalla</string>
<string name="settings_uninstall_temporary">Disinstalla temporaneamente</string>
<string name="settings_uninstall_permanent">Disinstalla permanentemente</string>
<string name="settings_restore_stock_image">Ripristina immagine originale</string>
<string name="settings_uninstall_temporary_message">Disinstalla temporaneamente KernelSU Next, ripristina lo stato originale dopo il prossimo riavvio.</string>
<string name="settings_uninstall_permanent_message">Disinstalla KernelSU Next (root e tutti i moduli) completamente e permanentemente.</string>
<string name="settings_restore_stock_image_message">Ripristina l\'immagine di fabbrica originale (se il backup è presente), solitamente usato prima di applicare l\'OTA; se devi disinstallare KernelSU Next, utilizza invece \"Disinstalla permanentemente\".</string>
<string name="flashing">Installazione</string>
<string name="flash_success">Installazione completata</string>
<string name="flash_failed">Installazione fallita</string>
<string name="selected_lkm">LKM selezionato: %s</string>
<string name="save_log">Salva registri</string>
<string name="log_saved">Registri salvati</string>
<string name="send_log">Invia log</string>
<string name="settings_disable_su">Disabilita la compatibilità su</string>
<string name="settings_disable_su_summary">Disattiva temporaneamente la possibilità per qualsiasi app di ottenere privilegi di root tramite il comando su (i processi di root esistenti non saranno interessati).</string>
<string name="settings_language">Lingua</string>
<string name="settings_legacyui">Usa Legacy UI</string>
<string name="settings_legacyui_summary">Passa allo stile precedente dell\'interfaccia utente.</string>
<string name="settings_banner">Abilita banner</string>
<string name="settings_banner_summary">Mostra banner di sfondo per i moduli.</string>
<string name="use_webuix">Usa WebUI X</string>
<string name="use_webuix_summary">Usa WebUI X invece di WebUI che supporta più API.</string>
<string name="use_webuix_eruda">Iniettare Eruda in WebUI X</string>
<string name="use_webuix_eruda_summary">Inietta una console di debug in WebUI X per semplificare il debug. Richiede che il debug web sia attivo.</string>
<string name="customization">Personalizzazione</string>
<string name="developer">Sviluppatore</string>
<string name="issue_report_title">Hai problemi?</string>
<string name="issue_report_body">Hai riscontrato un bug o vuoi inviarci un feedback?</string>
<string name="issue_report_body_2">Segnalacelo il prima possibile!</string>
<string name="issue_report_github">Segnala su GitHub</string>
<string name="issue_report_telegram">Contatto tramite Telegram</string>
<string name="issue_report_github_link">https://github.com/KernelSU-Next/KernelSU-Next/issues</string>
<string name="issue_report_telegram_link">https://t.me/ksunext</string>
<string name="confirm">Confermare</string>
<string name="app_name" translatable="false">KernelSU Next</string>
<string name="home">Home</string>
<string name="home_not_installed">Non installato</string>
<string name="home_click_to_install">Clicca per installare</string>
<string name="lkm_mode_deprecated">La modalità LKM è ora obsoleta!</string>
<string name="lkm_alternative_suggestion">Installa il kernel GKI o integra KernelSU Next sul tuo dispositivo.</string>
<string name="home_working">In esecuzione</string>
<string name="home_working_version">Versione: %d</string>
<string name="home_superuser_count">Super Utente(i)</string>
<string name="home_module_count">Moduli</string>
<string name="home_module_update_count">Aggiornamenti: %d</string>
<string name="home_failure">Firma KernelSU Next v2 non trovata nel kernel! [ !KSU_NEXT || != size/hash ]</string>
<string name="home_failure_tip">Chiedi allo sviluppatore del kernel di integrare KernelSU Next!</string>
<string name="home_kernel">Versione Kernel</string>
<string name="hook_mode">Modalità Hook</string>
<string name="enable">Abilita</string>
<string name="disable">Disabilita</string>
<string name="enabled">Abilitato</string>
<string name="disabled">Disabilitato</string>
<string name="susfs_supported">Supportato</string>
<string name="home_susfs">SuSFS: %s</string>
<string name="home_susfs_version">Versione SuSFS</string>
<string name="home_susfs_sus_su">SuS SU</string>
<string name="home_android">Versione Android</string>
<string name="home_manager_version">Versione del manager</string>
<string name="home_abi">ABI</string>
<string name="home_selinux_status">Stato di SELinux</string>
<string name="selinux_status_disabled">Disabilitato</string>
<string name="selinux_status_enforcing">Enforcing</string>
<string name="selinux_status_permissive">Permissive</string>
<string name="selinux_status_unknown">Sconosciuto</string>
<string name="superuser">Accesso root</string>
<string name="module_failed_to_enable">Impossibile abilitare il modulo: %s</string>
<string name="module_failed_to_disable">Impossibile disabilitare il modulo: %s</string>
<string name="module_empty">Nessun modulo installato</string>
<string name="module">Modulo</string>
<string name="module_install_prompt_with_name">Verranno installati i seguenti moduli: %1$s</string>
<string name="module_sort_a_to_z">Ordina (A → Z)</string>
<string name="module_sort_z_to_a">Ordina (Z → A)</string>
<string name="module_size_low_to_high">Ordina (Basso → Alto)</string>
<string name="module_size_high_to_low">Ordina (Alto → Basso)</string>
<string name="uninstall">Disinstalla</string>
<string name="restore">Ripristina</string>
<string name="module_install">Installa</string>
<string name="install">Installa</string>
<string name="reboot">Riavvia</string>
<string name="uninstalled">Disinstallato</string>
<string name="settings">Impostazioni</string>
<string name="reboot_userspace">Riavvio rapido</string>
<string name="reboot_recovery">Riavvia in Recovery</string>
<string name="reboot_bootloader">Riavvia in Bootloader</string>
<string name="reboot_download">Riavvia in Download</string>
<string name="reboot_edl">Riavvia in EDL</string>
<string name="about">Informazioni</string>
<string name="module_uninstall_confirm">Sei sicuro di voler disinstallare il modulo %s?</string>
<string name="module_uninstall_success">%s disinstallato</string>
<string name="module_uninstall_failed">Impossibile disinstallare: %s</string>
<string name="module_restore_confirm">Vuoi davvero ripristinare il modulo %s?</string>
<string name="module_restore_success">%s ripristinato</string>
<string name="module_restore_failed">Impossibile ripristinare: %s</string>
<string name="module_version">Versione</string>
<string name="module_author">Autore</string>
<string name="module_id">ID</string>
<string name="module_version_code">Codice</string>
<string name="module_update_json">UpdateJson</string>
<string name="module_update_json_empty">Vuoto</string>
<string name="enable_developer_options">Abilita le opzioni sviluppatore</string>
<string name="enable_developer_options_summary">Mostra le impostazioni nascoste e le informazioni di debug rilevanti solo per gli sviluppatori.</string>
<string name="module_overlay_fs_not_available">I moduli non sono disponibili in quanto overlayFS è disabilitato dal kernel.</string>
<string name="refresh">Ricarica</string>
<string name="show_system_apps">Mostra app di sistema</string>
<string name="hide_system_apps">Nascondi app di sistema</string>
<string name="export_log">Esportare i registri</string>
<string name="safe_mode">Modalità provvisoria</string>
<string name="reboot_to_apply">Riavvia per applicare la modifica</string>
<string name="module_magisk_conflict">I moduli sono disabilitati perché in conflitto con Magisk!</string>
<string name="home_mount_system">Modulo system</string>
<string name="home_magic_mount">Magic Mount</string>
<string name="home_overlayfs_mount">OverlayFS</string>
<string name="unavailable">Non disponibile</string>
<string name="use_overlay_fs">Usa OverlayFS</string>
<string name="use_overlay_fs_summary">Alterna l\'utilizzo di OverlayFS su Magic Mount per il sistema di montaggio di KernelSU Next.</string>
<string name="reboot_required">Riavvio richiesto</string>
<string name="reboot_message">Le modifiche avranno effetto dopo il riavvio del sistema. Vuoi riavviare ora?</string>
<string name="module_restore">Ripristina modulo</string>
<string name="module_restore_message">Ripristina i moduli dal backup recente.</string>
<string name="backup_restore">Backup &amp; Ripristino</string>
<string name="module_backup">Backup modulo</string>
<string name="module_backup_message">Esegui il backup dei moduli attualmente installati.</string>
<string name="allowlist_restore">Ripristina la lista consentita</string>
<string name="allowlist_restore_message">Ripristina la lista consentita dal backup recente.</string>
<string name="allowlist_backup">Backup lista consentita</string>
<string name="allowlist_backup_message">Backup della lista consentita attualmente configurata.</string>
<string name="warning">Avvertimento</string>
<string name="warning_message">Questa funzionalità è ancora in versione beta e in fase di sviluppo. Assicurati di effettuare il backup dei tuoi moduli prima di procedere. Utilizza questa funzionalità solo se ne comprendi i potenziali rischi. Procedi con cautela.</string>
<string name="proceed">Procedere</string>
<string name="cancel">Cancellare</string>
<string name="later">Dopo</string>
<string name="lkm_warning_message">La patch LKM si basa su componenti closed source. Vuoi continuare?</string>
<string name="home_next_kernelsu">🔥 Next build</string>
<string name="home_next_kernelsu_repo">https://github.com/KernelSU-Next/KernelSU-Next</string>
<string name="home_next_kernelsu_body">Branch sperimentale di Next. Dai un\'occhiata su GitHub!</string>
<string name="home_experimental_kernelsu">⚠️ Sviluppo sperimentale, attenzione!</string>
<string name="home_experimental_kernelsu_repo">127.0.0.1</string>
<string name="home_experimental_kernelsu_body">KernelSU Next è una versione non ufficiale che è sempre in fase di sviluppo sperimentale attivo. Viene fornita così com\'è, senza garanzie di stabilità, prestazioni o affidabilità.</string>
<string name="home_experimental_kernelsu_body_point_1"> • Usalo a tuo rischio e pericolo: potrebbero verificarsi crash, comportamenti imprevisti o problemi di sistema.</string>
<string name="home_experimental_kernelsu_body_point_2"> • Nessuna garanzia: gli sviluppatori non sono responsabili per eventuali perdite di dati, danni al sistema o altre conseguenze derivanti dal suo utilizzo.</string>
<string name="home_experimental_kernelsu_body_point_3"> • Solo a scopo di test: destinato agli utenti che comprendono i rischi e hanno dimestichezza con la risoluzione dei problemi.</string>
<string name="about_source_code"><![CDATA[Visualizza il codice sorgente su %1$s]]></string>
<string name="profile" translatable="false">Profilo dell\'App</string>
<string name="profile_default">Predefinito</string>
<string name="profile_template">Modello</string>
<string name="profile_custom">Personalizzato</string>
<string name="profile_name">Nome profilo</string>
<string name="profile_namespace">Spazio dei nomi del mount</string>
<string name="profile_namespace_inherited">Ereditato</string>
<string name="profile_namespace_global">Globale</string>
<string name="profile_namespace_individual">Individuale</string>
<string name="profile_groups">Gruppi</string>
<string name="profile_capabilities">Capacità</string>
<string name="profile_selinux_context">Contesto SELinux</string>
<string name="profile_umount_modules">Scollega moduli</string>
<string name="failed_to_update_app_profile">Aggiornamento App Profile per %s fallito</string>
<string name="require_kernel_version">La versione attualmente installata di KernelSU Next %1$d è troppo vecchia ed il gestore non può funzionare correttamente. Si prega di aggiornare alla versione %2$d o successiva!</string>
<string name="settings_umount_modules_default">Scollega moduli</string>
<string name="settings_umount_modules_default_summary">Il valore predefinito per \"Scollega moduli\" in App Profile. Se attivato, rimuoverà tutte le modifiche al sistema da parte dei moduli per le applicazioni che non hanno un profilo impostato.</string>
<string name="settings_susfs_toggle">Nascondi kprobes hook</string>
<string name="settings_susfs_toggle_summary">Questa opzione disabilita gli hook kprobe creati da ksu e, al loro posto, attiva gli hook non-kprobe incorporati, implementando la stessa funzionalità che verrebbe applicata a un kernel non-GKI, che non supporta kprobe.</string>
<string name="profile_umount_modules_summary">Attivando questa opzione permetterai a KernelSU Next di ripristinare ogni file modificato dai moduli per questa app.</string>
<string name="profile_selinux_domain">Dominio</string>
<string name="profile_selinux_rules">Regole</string>
<string name="module_update">Aggiorna</string>
<string name="module_update_available">Aggiorna</string>
<string name="module_updated">Aggiornato</string>
<string name="module_downloading">Sto scaricando il modulo: %s</string>
<string name="module_start_downloading">Inizia a scaricare: %s</string>
<string name="new_version_available">Nuova versione: %s disponibile, tocca per aggiornare.</string>
<string name="launch_app">Apri</string>
<string name="close">Chiudi</string>
<string name="force_stop_app">Arresto forzato</string>
<string name="restart_app">Riavvia</string>
<string name="settings_amoled_mode">Modalità AMOLED</string>
<string name="settings_amoled_mode_summary">Abilita un tema nero puro, utile per gli schermi AMOLED, per ridurre l\'affaticamento degli occhi e risparmiare batteria.</string>
<string name="restart_required">Riavvio richiesto</string>
<string name="restart_app_message">Affinché la modifica abbia effetto, è necessario riavviare l\'app.</string>
<string name="failed_to_update_sepolicy">Aggiornamento regole SELinux per %s fallito</string>
<string name="su_not_allowed">Non è consentito concedere i privilegi di superutente per: %s</string>
<string name="module_changelog">Registro aggiornamenti</string>
<string name="settings_profile_template">Modello App Profile</string>
<string name="settings_profile_template_summary">Gestisci il modello locale e online di App Profile</string>
<string name="app_profile_template_create">Crea modello</string>
<string name="app_profile_template_edit">Modifica modello</string>
<string name="app_profile_template_id">ID</string>
<string name="app_profile_template_id_invalid">Modello ID non valido</string>
<string name="app_profile_template_name">Nome</string>
<string name="app_profile_template_description">Descrizione</string>
<string name="app_profile_template_save">Salva</string>
<string name="app_profile_template_delete">Elimina</string>
<string name="app_profile_template_view">Visualizza modello</string>
<string name="app_profile_template_readonly">Sola lettura</string>
<string name="app_profile_template_id_exist">L\'ID del modello è già in uso!</string>
<string name="app_profile_import_export">Importa/Esporta</string>
<string name="app_profile_import_from_clipboard">Importa dagli appunti</string>
<string name="app_profile_export_to_clipboard">Esporta negli appunti</string>
<string name="app_profile_template_export_empty">Impossibile trovare un modello locale da esportare!</string>
<string name="app_profile_template_import_success">Importato con successo</string>
<string name="app_profile_template_sync">Sincronizza modelli online</string>
<string name="app_profile_template_save_failed">Impossibile salvare il modello</string>
<string name="app_profile_template_import_empty">Gli appunti sono vuoti!</string>
<string name="module_changelog_failed">Impossibile reperire il changelog: %s</string>
<string name="settings_check_update">Controlla aggiornamenti</string>
<string name="settings_check_update_summary">Controlla automaticamente gli aggiornamenti all\'apertura dell\'applicazione.</string>
<string name="grant_root_failed">Impossibile ottenere l\'accesso root!</string>
<string name="action">Azione</string>
<string name="webui">WebUI</string>
<string name="open">Apri</string>
<string name="enable_web_debugging">Abilita il debug di WebView</string>
<string name="enable_web_debugging_summary">Può essere usato per il debug di WebUI, è consigliato attivarlo solo quando necessario.</string>
<string name="direct_install">Installazione diretta (Raccomandata)</string>
<string name="select_file">Scegli un file</string>
<string name="install_inactive_slot">Installa nello slot inattivo (dopo OTA)</string>
<string name="install_inactive_slot_warning">Il tuo dispositivo sarà **FORZATO** ad avviarsi nello slot inattivo dopo il riavvio!\nUsa questa opzione solo quando l\'applicazione dell\'aggiornamento OTA è terminata.\nProcedere?</string>
<string name="install_next">Avanti</string>
<string name="select_file_tip">Si consiglia l\'immagine della partizione %1$s</string>
<string name="select_kmi">Scegli il KMI</string>
<string name="shrink_sparse_image">Riduci al minimo l\'immagine sparse</string>
<string name="shrink_sparse_image_message">Ridimensiona l\'immagine sparse dei moduli alla sua reale dimensione. Nota che questo potrebbe causare malfunzionamenti dei moduli, quindi utilizzala solo quando necessario (Come per il backup).</string>
<string name="settings_uninstall">Disinstalla</string>
<string name="settings_uninstall_temporary">Disinstalla temporaneamente</string>
<string name="settings_uninstall_permanent">Disinstalla permanentemente</string>
<string name="settings_restore_stock_image">Ripristina immagine originale</string>
<string name="settings_uninstall_temporary_message">Disinstalla temporaneamente KernelSU Next, ripristina lo stato originale dopo il prossimo riavvio.</string>
<string name="settings_uninstall_permanent_message">Disinstalla KernelSU Next (root e tutti i moduli) completamente e permanentemente.</string>
<string name="settings_restore_stock_image_message">Ripristina l\'immagine di fabbrica originale (se il backup è presente), solitamente usato prima di applicare l\'OTA; se devi disinstallare KernelSU Next, utilizza invece \"Disinstalla permanentemente\".</string>
<string name="flashing">Installazione</string>
<string name="flash_success">Installazione completata</string>
<string name="flash_failed">Installazione fallita</string>
<string name="selected_lkm">LKM selezionato: %s</string>
<string name="save_log">Salva registri</string>
<string name="log_saved">Registri salvati</string>
<string name="send_log">Invia log</string>
<string name="settings_disable_su">Disabilita la compatibilità su</string>
<string name="settings_disable_su_summary">Disattiva temporaneamente la possibilità per qualsiasi app di ottenere privilegi di root tramite il comando su (i processi di root esistenti non saranno interessati).</string>
<string name="settings_language">Lingua</string>
<string name="system_default">Predefinito del sistema</string>
<string name="settings_legacyui">Usa Legacy UI</string>
<string name="settings_legacyui_summary">Passa allo stile precedente dell\'interfaccia utente.</string>
<string name="settings_banner">Abilita banner</string>
<string name="settings_banner_summary">Mostra banner di sfondo per i moduli.</string>
<string name="use_webuix">Usa WebUI X</string>
<string name="use_webuix_summary">Usa WebUI X invece di WebUI che supporta più API.</string>
<string name="use_webuix_eruda">Iniettare Eruda in WebUI X</string>
<string name="use_webuix_eruda_summary">Inietta una console di debug in WebUI X per semplificare il debug. Richiede che il debug web sia attivo.</string>
<string name="customization">Personalizzazione</string>
<string name="developer">Sviluppatore</string>
<string name="sucompat_disabled">SUCOMPAT DISATTIVATO</string>
<string name="zygisk_required">Zygisk richiesto</string>
</resources>

View File

@@ -25,9 +25,9 @@
<string name="home_android">Android バージョン</string>
<string name="home_manager_version">アプリのバージョン</string>
<string name="home_selinux_status">SELinuxの状態</string>
<string name="selinux_status_disabled">無効</string>
<string name="selinux_status_enforcing">強制</string>
<string name="selinux_status_permissive">許可モード</string>
<string name="selinux_status_disabled">Disabled</string>
<string name="selinux_status_enforcing">Enforcing</string>
<string name="selinux_status_permissive">Permissive</string>
<string name="selinux_status_unknown">不明</string>
<string name="superuser">スーパーユーザー</string>
<string name="module_failed_to_enable">モジュールを有効にできませんでした:%s</string>

View File

@@ -9,7 +9,7 @@
<string name="issue_report_telegram_link">https://t.me/ksunext</string>
<string name="confirm">Potwierdź</string>
<string name="app_name" translatable="false">KernelSU Next</string>
<string name="home">Strona główna</string>
<string name="home">Główna</string>
<string name="home_not_installed">Niezainstalowany</string>
<string name="home_click_to_install">Kliknij, aby zainstalować</string>
<string name="lkm_mode_deprecated">Tryb LKM jest już przestarzały!</string>
@@ -22,7 +22,7 @@
<string name="home_failure">Nie znaleziono sygnatury KernelSU Next v2 w jądrze! [ !KSU_NEXT || != size/hash ]</string>
<string name="home_failure_tip">Poproś swojego programistę jądra o integrację KernelSU Next!</string>
<string name="home_kernel">Wersja jądra</string>
<string name="hook_mode">Tryb hookowania</string>
<string name="hook_mode">Rodzaj hooków</string>
<string name="enable">Aktywuj</string>
<string name="disable">Dezaktywuj</string>
<string name="enabled">Aktywny</string>
@@ -61,7 +61,7 @@
<string name="reboot_bootloader">Reboot do trybu Bootloader</string>
<string name="reboot_download">Reboot do trybu Download</string>
<string name="reboot_edl">Reboot do trybu EDL</string>
<string name="about">O autorze</string>
<string name="about">O aplikacji</string>
<string name="module_uninstall_confirm">Czy na pewno chcesz odinstalować moduł %s?</string>
<string name="module_uninstall_success">%s został odinstalowany</string>
<string name="module_uninstall_failed">Nie można odinstalować: %s</string>
@@ -94,7 +94,7 @@
<string name="reboot_message">Zmiany zaczną obowiązywać po restarcie systemu. Czy chcesz teraz uruchomić ponownie system?</string>
<string name="module_restore">Przywróć moduły z kopii zapasowej</string>
<string name="module_restore_message">Przywróć moduły z ostatniej kopii zapasowej.</string>
<string name="backup_restore">Tworzenie / przywracanie kopii zapasowej</string>
<string name="backup_restore">Tworzenie i przywracanie kopii zapasowej</string>
<string name="module_backup">Utwórz kopię zapasową modułów</string>
<string name="module_backup_message">Utwórz kopię zapasową obecnie zainstalowanych modułów.</string>
<string name="allowlist_restore">Przywróć listę zgód z kopii zapasowej</string>

View File

@@ -16,8 +16,8 @@
<string name="lkm_alternative_suggestion">Instale o kernel GKI ou integre o KernelSU Next ao seu dispositivo.</string>
<string name="home_working">Em execução</string>
<string name="home_working_version">Versão: %d</string>
<string name="home_superuser_count">SuperUsuários: %d</string>
<string name="home_module_count">Módulos: %d</string>
<string name="home_superuser_count">SuperUsuário(s)</string>
<string name="home_module_count">Módulo(s)</string>
<string name="home_module_update_count">Atualizações: %d</string>
<string name="home_failure">Assinatura KernelSU Next v2 não encontrada no kernel! [ !KSU_NEXT || != size/hash ]</string>
<string name="home_failure_tip">Peça ao seu desenvolvedor de kernel para integrar o KernelSU Next!</string>
@@ -40,8 +40,8 @@
<string name="selinux_status_permissive">Permissivo</string>
<string name="selinux_status_unknown">Desconhecido</string>
<string name="superuser">SuperUsuário</string>
<string name="module_failed_to_enable">Falha ao ativar o módulo %s</string>
<string name="module_failed_to_disable">Falha ao desativar o módulo %s</string>
<string name="module_failed_to_enable">Não foi possível ativar o módulo %s</string>
<string name="module_failed_to_disable">Não foi possível desativar o módulo %s</string>
<string name="module_empty">Nenhum módulo instalado</string>
<string name="module">Módulo</string>
<string name="module_install_prompt_with_name">Os seguintes módulos serão instalados: %1$s</string>
@@ -140,6 +140,7 @@
<string name="profile_selinux_domain">Domínio</string>
<string name="profile_selinux_rules">Regras</string>
<string name="module_update">Atualizar</string>
<string name="module_update_available">Atualizar</string>
<string name="module_updated">Atualizado</string>
<string name="module_downloading">Baixando módulo %s</string>
<string name="module_start_downloading">Começando a baixar %s</string>
@@ -153,7 +154,7 @@
<string name="restart_required">Reinicialização necessária</string>
<string name="restart_app_message">O app precisa ser reiniciado para que essa alteração tenha efeito.</string>
<string name="failed_to_update_sepolicy">Falha ao atualizar as regras do SELinux para: %s</string>
<string name="su_not_allowed">O acesso de SuperUsuário não é permitido para: %s</string>
<string name="su_not_allowed">Não foi possível conceder acesso de SuperUsuário a %s</string>
<string name="module_changelog">Registro de alterações</string>
<string name="settings_profile_template">Modelo do Perfil do Aplicativo</string>
<string name="settings_profile_template_summary">Gerencie o modelo local e online do Perfil do Aplicativo.</string>
@@ -177,7 +178,7 @@
<string name="app_profile_template_save_failed">Falha ao salvar o modelo</string>
<string name="app_profile_template_import_empty">A área de transferência está vazia!</string>
<string name="module_changelog_failed">Falha ao buscar o registro de alterações: %s</string>
<string name="settings_check_update">Verificar por atualização</string>
<string name="settings_check_update">Verificar por atualizações</string>
<string name="settings_check_update_summary">Verifique automaticamente se há atualizações ao abrir o app</string>
<string name="grant_root_failed">Falha ao conceder acesso root!</string>
<string name="action">Ação</string>
@@ -185,22 +186,22 @@
<string name="open">Abrir</string>
<string name="enable_web_debugging">Ativar depuração do WebView</string>
<string name="enable_web_debugging_summary">Pode ser usado para depurar o WebUI. Por favor, ative somente quando necessário.</string>
<string name="direct_install">Instalação direta (recomendada)</string>
<string name="direct_install">Instalação direta (Recomendada)</string>
<string name="select_file">Selecione um arquivo</string>
<string name="install_inactive_slot">Instalar no slot inativo (após o OTA)</string>
<string name="install_inactive_slot">Instalar no slot inativo (Após o OTA)</string>
<string name="install_inactive_slot_warning">Seu dispositivo será **FORÇADO** a inicializar no slot inativo atual após uma reinicialização!\nSó use esta opção após a conclusão do OTA.\nDeseja continuar?</string>
<string name="install_next">Próximo</string>
<string name="select_file_tip">A imagem da partição %1$s é recomendada</string>
<string name="select_kmi">Selecionar KMI</string>
<string name="shrink_sparse_image">Minimizar imagem esparsa</string>
<string name="shrink_sparse_image_message">Redimensione a imagem esparsa onde o módulo está localizado para seu tamanho real. Observe que isso pode fazer com que o módulo funcione de forma anormal, portanto, use-o somente quando necessário (como um backup).</string>
<string name="shrink_sparse_image_message">Redimensione a imagem esparsa onde o módulo está localizado para seu tamanho real. Observe que isso pode fazer com que o módulo funcione de forma anormal, portanto, use-o somente quando necessário (Como um backup).</string>
<string name="settings_uninstall">Desinstalar</string>
<string name="settings_uninstall_temporary">Desinstalar temporariamente</string>
<string name="settings_uninstall_permanent">Desinstalar permanentemente</string>
<string name="settings_restore_stock_image">Restaurar imagem de fábrica</string>
<string name="settings_uninstall_temporary_message">Desinstale temporariamente o KernelSU Next e restaure ao estado original após a próxima reinicialização.</string>
<string name="settings_uninstall_permanent_message">Desinstale o KernelSU Next (root e todos os módulos) completamente e permanentemente.</string>
<string name="settings_restore_stock_image_message">Restaure a imagem de fábrica (se existir um backup), geralmente usada antes do OTA. Se você precisar desinstalar o KernelSU Next, use \"Desinstalar permanentemente\".</string>
<string name="settings_uninstall_permanent_message">Desinstale o KernelSU Next (Root e todos os módulos) completamente e permanentemente.</string>
<string name="settings_restore_stock_image_message">Restaure a imagem de fábrica (Se existir um backup), geralmente usada antes do OTA. Se você precisar desinstalar o KernelSU Next, use \"Desinstalar permanentemente\".</string>
<string name="flashing">Flashando</string>
<string name="flash_success">Flash bem-sucedido</string>
<string name="flash_failed">Flash falhou</string>
@@ -209,8 +210,9 @@
<string name="log_saved">Registros salvos</string>
<string name="send_log">Compartilhar registros</string>
<string name="settings_disable_su">Desativar compatibilidade su</string>
<string name="settings_disable_su_summary">Desative temporariamente a capacidade de qualquer app obter privilégios root por meio do comando su (processos root existentes não serão afetados).</string>
<string name="settings_disable_su_summary">Desative temporariamente a capacidade de qualquer app obter privilégios root por meio do comando su (Processos root existentes não serão afetados).</string>
<string name="settings_language">Idioma</string>
<string name="system_default">Padrão do sistema</string>
<string name="settings_legacyui">Usar IU antiga</string>
<string name="settings_legacyui_summary">Mude para o estilo de interface do usuário anterior.</string>
<string name="settings_banner">Ativar banners</string>
@@ -220,4 +222,7 @@
<string name="use_webuix_eruda">Injetar Eruda no WebUI X</string>
<string name="use_webuix_eruda_summary">Injeta um console de depuração no WebUI X para facilitar a depuração. Requer que a depuração web esteja ativada.</string>
<string name="customization">Personalização</string>
<string name="developer">Desenvolvedor</string>
<string name="sucompat_disabled">SUCOMPAT DESATIVADO</string>
<string name="zygisk_required">Zygisk necessário</string>
</resources>

View File

@@ -15,10 +15,10 @@
<string name="lkm_mode_deprecated">Режим LKM теперь устарел!</string>
<string name="lkm_alternative_suggestion">Установите GKI ядро или интегрируйте ядра рядом с вашим устройством.</string>
<string name="home_working">Работает</string>
<string name="home_working_version">Версия: %d</string>
<string name="home_superuser_count">Superusers: %d</string>
<string name="home_module_count">Модули: %d</string>
<string name="home_module_update_count">Обновления: %d</string>
<string name="home_working_version">Версия драйвера: %d</string>
<string name="home_superuser_count">Выдано прав</string>
<string name="home_module_count">Модулей</string>
<string name="home_module_update_count">Обновлений: %d</string>
<string name="home_failure">Подпись KernelSU Next v2 не найдена в ядре! [!KSU_NEXT || != size/hash]</string>
<string name="home_failure_tip">Попросите вашего разработчика ядра интегрировать KernelSU Next!</string>
<string name="home_kernel">Версия ядра</string>
@@ -44,11 +44,11 @@
<string name="module_failed_to_disable">Не удалось отключить модуль: %s</string>
<string name="module_empty">Нет установленных модулей</string>
<string name="module">Модули</string>
<string name="module_install_prompt_with_name">Следующие модуль(и) будут установлены: %1$s</string>
<string name="module_install_prompt_with_name">Следующие модули будут установлены: %1$s</string>
<string name="module_sort_a_to_z">Сортировать (А → Я)</string>
<string name="module_sort_z_to_a">Сортировать (Я → А)</string>
<string name="module_size_low_to_high">Сортировать (Низкий → Высокий)</string>
<string name="module_size_high_to_low">Сортировать (Высокий → Низкий)</string>
<string name="module_size_low_to_high">Сортировать (Меньше → Больше)</string>
<string name="module_size_high_to_low">Сортировать (Больше → Меньше)</string>
<string name="uninstall">Удалить</string>
<string name="restore">Восстановить</string>
<string name="module_install">Установить</string>
@@ -140,6 +140,7 @@
<string name="profile_selinux_domain">Домен</string>
<string name="profile_selinux_rules">Правила</string>
<string name="module_update">Обновить</string>
<string name="module_update_available">Обновить</string>
<string name="module_updated">Обновлено</string>
<string name="module_downloading">Загрузка модуля: %s</string>
<string name="module_start_downloading">Начало загрузки: %s</string>
@@ -180,9 +181,9 @@
<string name="settings_check_update">Проверять обновления</string>
<string name="settings_check_update_summary">Автоматически проверять обновления при открытии приложения.</string>
<string name="grant_root_failed">Не удалось предоставить root-доступ!</string>
<string name="action">Запустить</string>
<string name="action">Скрипт</string>
<string name="webui">WebUI</string>
<string name="open">Открыть</string>
<string name="open">WebUI</string>
<string name="enable_web_debugging">Отладка WebView</string>
<string name="enable_web_debugging_summary">Можно использовать для отладки WebUI. Включайте только при необходимости.</string>
<string name="direct_install">Прямая установка (Рекомендуется)</string>
@@ -205,17 +206,23 @@
<string name="flash_success">Прошивка выполнена успешно</string>
<string name="flash_failed">Ошибка прошивки</string>
<string name="selected_lkm">Выбран LKM: %s</string>
<string name="save_log">Сохранить логи</string>
<string name="save_log">Сохранить\nлоги</string>
<string name="log_saved">Логи сохранены</string>
<string name="send_log">Поделиться логами</string>
<string name="send_log">Поделиться\nлогами</string>
<string name="settings_disable_su">Откл. совместимость с su</string>
<string name="settings_disable_su_summary">Временно отключить возможность приложениям получать права root через команду su (существующие процессы с правами root не будут затронуты).</string>
<string name="settings_language">Язык</string>
<string name="system_default">Системный</string>
<string name="settings_legacyui">Использовать старый UI</string>
<string name="settings_legacyui_summary">Переключиться на предыдущий стиль интерфейса.</string>
<string name="settings_banner">Включить баннеры</string>
<string name="settings_banner_summary">Показывать фоновые баннеры для модулей.</string>
<string name="use_webuix">Использовать WebUI X</string>
<string name="use_webuix_summary">Использовать WebUI X вместо WebUI, который поддерживает больше API.</string>
<string name="use_webuix_eruda">Инжект Eruda в WebUI X</string>
<string name="use_webuix_eruda_summary">Инжектить консоль отладки в WebUI X, чтобы упростить отладку. Требуется включить отладку WebView.</string>
<string name="customization">Кастомизация</string>
<string name="developer">Для разработчиков</string>
<string name="sucompat_disabled">SUCOMPAT ОТКЛЮЧЕН</string>
<string name="zygisk_required">Требуется Zygisk</string>
</resources>

View File

@@ -15,14 +15,16 @@
<string name="lkm_mode_deprecated">Режим LKM тепер застарілий!</string>
<string name="lkm_alternative_suggestion">Встановіть ядро GKI або інтегруйте ядра поруч із вашим пристроєм.</string>
<string name="home_working">Працює</string>
<string name="home_working_version">Версія: %d</string>
<string name="home_superuser_count">SuperUsers: %d</string>
<string name="home_module_count">Модулі: %d</string>
<string name="home_working_version">Версія драйвера: %d</string>
<string name="home_superuser_count">Видано прав</string>
<string name="home_module_count">Модулів</string>
<string name="home_module_update_count">Оновлення: %d</string>
<string name="home_failure">Підпис KernelSU Next v2 не знайдено в ядрі! [!KSU_NEXT || != size/hash]</string>
<string name="home_failure_tip">Попросіть вашого розробника ядра інтегрувати KernelSU Next!</string>
<string name="home_kernel">Версія ядра</string>
<string name="hook_mode">Режим хуків</string>
<string name="enable">Увімкнути</string>
<string name="disable">Вимкнути</string>
<string name="disabled">Вимкнено</string>
<string name="enabled">Увімкнено</string>
<string name="susfs_supported">Доступно</string>
@@ -42,18 +44,18 @@
<string name="module_failed_to_disable">Не вдалося вимкнути модуль: %s</string>
<string name="module_empty">Нема встановлених модулів</string>
<string name="module">Модулі</string>
<string name="module_install_prompt_with_name">Наступні модуль(і) будуть встановлені: %1$s</string>
<string name="module_install_prompt_with_name">Наступні модулі будуть встановлені: %1$s</string>
<string name="module_sort_a_to_z">Сортувати (А-Я)</string>
<string name="module_sort_z_to_a">Сортувати (Я-А)</string>
<string name="module_size_low_to_high">Сортувати (Низький → Великий)</string>
<string name="module_size_high_to_low">Сортувати (Великий → Низький)</string>
<string name="module_size_low_to_high">Сортувати (Менше → Більше)</string>
<string name="module_size_high_to_low">Сортувати (Більше → Менше)</string>
<string name="uninstall">Видалити</string>
<string name="restore">Відновити</string>
<string name="module_install">Встановити</string>
<string name="install">Встановити</string>
<string name="reboot">Перезавантаження</string>
<string name="uninstalled">Видалено</string>
<string name="settings">Налаштування</string>
<string name="settings">Параметри</string>
<string name="reboot_userspace">М’яка перезавантаження</string>
<string name="reboot_recovery">Перезавантаження в Recovery</string>
<string name="reboot_bootloader">Перезавантаження в Bootloader</string>
@@ -116,7 +118,7 @@
<string name="home_experimental_kernelsu_body_point_3"> • Лише для тестування: призначено для користувачів, які розуміють ризики і готові вирішувати виникаючі проблеми.</string>
<string name="about_source_code"><![CDATA[Вихідний код на %1$s]]></string>
<string name="profile" translatable="false">Профіль додатка</string>
<string name="profile_default">За замовчуванням</string>
<string name="profile_default">Дефолт</string>
<string name="profile_template">Шаблон</string>
<string name="profile_custom">Налаштувати</string>
<string name="profile_name">Ім’я профілю</string>
@@ -138,6 +140,7 @@
<string name="profile_selinux_domain">Домен</string>
<string name="profile_selinux_rules">Правила</string>
<string name="module_update">Оновити</string>
<string name="module_update_available">Оновити</string>
<string name="module_updated">Оновлено</string>
<string name="module_downloading">Завантаження модуля: %s</string>
<string name="module_start_downloading">Початок завантаження: %s</string>
@@ -178,9 +181,9 @@
<string name="settings_check_update">Перевіряти оновлення</string>
<string name="settings_check_update_summary">Автоматично перевіряти оновлення при відкритті додатка.</string>
<string name="grant_root_failed">Не вдалося надати root-доступ!</string>
<string name="action">Запустити</string>
<string name="action">Скрипт</string>
<string name="webui">WebUI</string>
<string name="open">Відкрити</string>
<string name="open">WebUI</string>
<string name="enable_web_debugging">Відладка WebView</string>
<string name="enable_web_debugging_summary">Можна використовувати для відладки WebUI. Увімкніть лише при необхідності.</string>
<string name="direct_install">Пряма установка (Рекомендовано)</string>
@@ -203,12 +206,13 @@
<string name="flash_success">Прошивка виконана успішно</string>
<string name="flash_failed">Помилка прошивки</string>
<string name="selected_lkm">Вибрано LKM: %s</string>
<string name="save_log">Зберегти логи</string>
<string name="save_log">Зберегти\nлоги</string>
<string name="log_saved">Логи збережено</string>
<string name="send_log">Поділитися логами</string>
<string name="send_log">Поділитися\nлогами</string>
<string name="settings_disable_su">Вимкн. сумісність із su</string>
<string name="settings_disable_su_summary">Тимчасово вимкнути можливість додаткам отримувати права root через команду su (існуючі процеси з правами root не будуть зачеплені).</string>
<string name="settings_language">Мова</string>
<string name="system_default">Системний</string>
<string name="settings_legacyui">Використовувати застарілий UI</string>
<string name="settings_legacyui_summary">Перейти до попереднього стилю інтерфейсу користувача.</string>
<string name="settings_banner">Увімкнути банери</string>
@@ -218,5 +222,7 @@
<string name="use_webuix_eruda">Інжект Eruda у WebUI X</string>
<string name="use_webuix_eruda_summary">Інжектити консоль відладки у WebUI X, щоб спростити відладку. Потрібно увімкнути відладку WebView.</string>
<string name="customization">Кастомізація</string>
<string name="developer">Розробник</string>
<string name="developer">Для Розробників</string>
<string name="sucompat_disabled">SUCOMPAT ВИМКНЕНО</string>
<string name="zygisk_required">Потрібно Zygisk</string>
</resources>

View File

@@ -10,25 +10,30 @@
<string name="confirm">Xác nhận</string>
<string name="app_name" translatable="false">KernelSU Next</string>
<string name="home">Trang chủ</string>
<string name="home_not_installed">Hỗ trợ nhưng chưa cài đặt</string>
<string name="home_click_to_install">Bấm để cài đặt</string>
<string name="home_not_installed">Chưa cài đặt</string>
<string name="home_click_to_install">Nhấn để cài đặt</string>
<string name="lkm_mode_deprecated">Chế độ LKM không còn được hỗ trợ!</string>
<string name="lkm_alternative_suggestion">Cài đặt kernel GKI hoặc tự nhúng KernelSU Next vào kernel của bạn.</string>
<string name="home_working">Đã cài đặt và hoạt động</string>
<string name="lkm_alternative_suggestion">Cài đặt Kernel GKI hoặc tự nhúng KernelSU Next vào Kernel của bạn</string>
<string name="home_working">Đang hoạt động</string>
<string name="home_working_version">Phiên bản: %d</string>
<string name="home_superuser_count">Ứng dụng đã cấp su: %d</string>
<string name="home_module_count">Module: %d</string>
<string name="home_failure">Không tìm thấy chữ kí KernelSU Next v2 (chưa cài đặt, nhúng sai cách hoặc nhầm trình quản lý?) [ !KSU_NEXT || != size/hash ]</string>
<string name="home_failure_tip">Hỏi hoặc tự nhúng KernelSU Next vào kernel của bạn!</string>
<string name="home_kernel">Phiên bản kernel</string>
<string name="enabled">Kích hoạt</string>
<string name="disabled">Vô hiệu hoá</string>
<string name="home_superuser_count">Superuser(s)</string>
<string name="home_module_count">Module(s)</string>
<string name="home_module_update_count">Cập nhật: %d</string>
<string name="home_failure">Không tìm thấy chữ ký KernelSU Next v2 trong Kernel! [ !KSU_NEXT || != size/hash ]</string>
<string name="home_failure_tip">Yêu cầu nhà phát triển Kernel của bạn tích hợp KernelSU Next!</string>
<string name="home_kernel">Phiên bản Kernel</string>
<string name="hook_mode">Chế độ móc</string>
<string name="enable">Kích hoạt</string>
<string name="disable">Vô hiệu hoá</string>
<string name="enabled">Đã kích hoạt</string>
<string name="disabled">Đã vô hiệu hoá</string>
<string name="susfs_supported">Hỗ trợ</string>
<string name="home_susfs">SuSFS: %s</string>
<string name="home_susfs_version">Phiên bản SuSFS</string>
<string name="home_susfs_sus_su">SuS SU</string>
<string name="home_android">Phiên bản Android</string>
<string name="home_manager_version">Phiên bản trình quản lý</string>
<string name="home_abi">ABI</string>
<string name="home_selinux_status">Trạng thái SELinux</string>
<string name="selinux_status_disabled">Vô hiệu hoá</string>
<string name="selinux_status_enforcing">Enforcing</string>
@@ -37,81 +42,85 @@
<string name="superuser">Superuser</string>
<string name="module_failed_to_enable">Không thể kích hoạt module: %s</string>
<string name="module_failed_to_disable">Không thể vô hiệu hoá module: %s</string>
<string name="module_empty">Chưa cài module nào</string>
<string name="module_empty">Chưa cài đặt module nào</string>
<string name="module">Module</string>
<string name="module_install_prompt_with_name">Bạn có THẬT SỰ muốn cài module này không (kiểm tra trước khi cài ASAF) %1$s?</string>
<string name="module_sort_a_to_z">Sắp xếp (A-Z)</string>
<string name="module_sort_z_to_a">Sắp xếp (Z-A)</string>
<string name="module_install_prompt_with_name">Các module sau đây sẽ được cài đặt: %1$s</string>
<string name="module_sort_a_to_z">Sắp xếp (AZ)</string>
<string name="module_sort_z_to_a">Sắp xếp (ZA)</string>
<string name="module_size_low_to_high">Sắp xếp (Nhỏ → Lớn)</string>
<string name="module_size_high_to_low">Sắp xếp (Lớn → Nhỏ)</string>
<string name="uninstall">Gỡ cài đặt</string>
<string name="restore">Khôi phục</string>
<string name="module_install">Cài đặt</string>
<string name="install">Cài đặt</string>
<string name="reboot">Khởi động lại</string>
<string name="uninstalled">Đã gỡ cài đặt</string>
<string name="settings">Cài đặt</string>
<string name="reboot_userspace">Khởi động mềm</string>
<string name="reboot_recovery">Vào Recovery</string>
<string name="reboot_bootloader">Vào Bootloader</string>
<string name="reboot_download">Vào Download Mode</string>
<string name="reboot_edl">Vào EDL</string>
<string name="reboot_userspace">Khởi động lại mềm</string>
<string name="reboot_recovery">Khởi động lại vào Recovery</string>
<string name="reboot_bootloader">Khởi động lại vào Bootloader</string>
<string name="reboot_download">Khởi động lại vào Download Mode</string>
<string name="reboot_edl">Khởi động lại vào EDL</string>
<string name="about">Thông tin</string>
<string name="module_uninstall_confirm">Bạn có THẬT SỰ muốn gỡ module %s?</string>
<string name="module_uninstall_success">%s đã được gỡ</string>
<string name="module_uninstall_confirm">Bạn có THẬT SỰ muốn gỡ cài đặt module %s không?</string>
<string name="module_uninstall_success">%s đã được gỡ cài đặt</string>
<string name="module_uninstall_failed">Gỡ cài đặt thất bại: %s</string>
<string name="module_restore_confirm">Khôi phục module lại như lúc cài %s?</string>
<string name="module_restore_success">%s đã khôi phục</string>
<string name="module_restore_failed">Khôi phục thất bại, vui lòng cài lại: %s</string>
<string name="module_restore_confirm">Bạn có CHẮC CHẮN muốn khôi phục module %s không?</string>
<string name="module_restore_success">%s đã được khôi phục</string>
<string name="module_restore_failed">Khôi phục thất bại: %s</string>
<string name="module_version">Phiên bản</string>
<string name="module_author">Người tạo ra</string>
<string name="module_author">Tác giả</string>
<string name="module_id">ID</string>
<string name="module_version_code">Code</string>
<string name="module_update_json">UpdateJson</string>
<string name="module_update_json">JSON cập nhật</string>
<string name="module_update_json_empty">Trống</string>
<string name="enable_developer_options">Kích hoạt tính năng dành cho nhà phát triển</string>
<string name="enable_developer_options_summary">Hin thị những cài đặt ẩn và nhũng log DÀNH RIÊNG CHO NHÀ PHÁT TRIỂN.</string>
<string name="module_overlay_fs_not_available">Không thể sử dụng module vì flag Overlayfs không được kích hoạt trong kernel, vui lòng kích hoạt</string>
<string name="enable_developer_options">Tùy chọn nhà phát triển</string>
<string name="enable_developer_options_summary">Hin thị các cài đặt ẩn và thông tin gỡ lỗi chỉ dành cho nhà phát triển</string>
<string name="module_overlay_fs_not_available">Các module không khả dụng vì OverlayFS bị vô hiệu hóa trên Kernel</string>
<string name="refresh">Làm mới</string>
<string name="show_system_apps">Hin thị ứng dụng hệ thống</string>
<string name="show_system_apps">Hin thị ứng dụng hệ thống</string>
<string name="hide_system_apps">Ẩn ứng dụng hệ thống</string>
<string name="export_log">Xuất logs</string>
<string name="safe_mode">CHẾ ĐỘ AN TOÀN</string>
<string name="reboot_to_apply">Khởi động lại để có hiệu lực</string>
<string name="module_magisk_conflict">Vui lòng gỡ magisk để sử dụng module</string>
<string name="home_mount_system">Cơ chế mount</string>
<string name="module_magisk_conflict">Các module không khả dụng do xung đột với Magisk!</string>
<string name="home_mount_system">Cơ chế Mount</string>
<string name="home_magic_mount">Magic Mount</string>
<string name="home_overlayfs_mount">OverlayFS</string>
<string name="unavailable">Không khả dụng</string>
<string name="use_overlay_fs">Sử dụng OverlayFS</string>
<string name="use_overlay_fs_summary">Gạt bật tắt chuyển giữa phương thức OverlayFS và Magic Mount cho các tập mount hệ thống.</string>
<string name="use_overlay_fs_summary">Gạt bật/tắt chuyển đổi phương thức OverlayFS và Magic Mount cho hệ thống</string>
<string name="reboot_required">Yêu cầu khởi động lại</string>
<string name="reboot_message">Thay đổi sẽ có hiệu lực sau khi khởi động lại, khởi động ngay?</string>
<string name="module_restore">Khôi phục module</string>
<string name="module_restore_message">Khôi phục module từ bản sao lưu gần nhất.</string>
<string name="module_restore_message">Khôi phục các module từ bản sao lưu gần nhất</string>
<string name="backup_restore">Sao lưu &amp; Khôi phục</string>
<string name="module_backup">Sao lưu module</string>
<string name="module_backup_message">Sao lưu những module đang được cài đặt.</string>
<string name="allowlist_restore">Khôi phục danh sách ngoại trừ</string>
<string name="allowlist_restore_message">Khôi phục danh sách ngoại trừ từ bản sao lưu gần nhất</string>
<string name="allowlist_backup">Sao lưu danh sách ngoại trừ</string>
<string name="allowlist_backup_message">Sao lưu danh sách ngoại trừ từ bản sao lưu gần nhất</string>
<string name="module_backup_message">Sao lưu các module hiện đang được cài đặt</string>
<string name="allowlist_restore">Khôi phục danh sách cho phép</string>
<string name="allowlist_restore_message">Khôi phục danh sách cho phép từ bản sao lưu gần nhất</string>
<string name="allowlist_backup">Sao lưu danh sách cho phép</string>
<string name="allowlist_backup_message">Sao lưu danh sách cho phép hiện đang được thiết lập</string>
<string name="warning">LƯU Ý!!!</string>
<string name="warning_message">Tính năng này vẫn đang trong giai đoạn thử nghiệm, chỉ sử dụng khi bạn chịu trách nghiệm và đủ hiểu biết </string>
<string name="warning_message">Tính năng này vẫn đang trong giai đoạn thử nghiệm, chỉ sử dụng khi bạn đủ hiểu biết và tự chịu trách nhiệm</string>
<string name="proceed">Tiếp tục</string>
<string name="cancel">Huỷ</string>
<string name="cancel">Huỷ bỏ</string>
<string name="later">Để sau</string>
<string name="lkm_warning_message">Bản vá LKM dựa trên mã nguồn đóng. Bạn có muốn tiếp tục không?</string>
<string name="home_next_kernelsu">🔥 Bản dựng tiếp theo</string>
<string name="home_next_kernelsu_repo">https://github.com/KernelSU-Next/KernelSU-Next</string>
<string name="home_next_kernelsu_body">Nhánh thử nghiệm tiếp theo. Xem trong GitHub!</string>
<string name="home_next_kernelsu_body">Nhánh thử nghiệm tiếp theo. Hãy xem trên GitHub!</string>
<string name="home_experimental_kernelsu">⚠️ Cảnh báo nhánh thử nghiệm cho nhà phát triển!</string>
<string name="home_experimental_kernelsu_repo">127.0.0.1</string>
<string name="home_experimental_kernelsu_body">KernelSU Next là 1 nhánh của KernelSU luôn có những nhánh thử nghiệm và cập nhật hàng ngày, nó có thể không ổn định</string>
<string name="home_experimental_kernelsu_body_point_1"> • Tự chịu trách nghiệm: crashes, những thứ kì lạ, không vào được 1 vài ứng dụng (ngân hàng),... có thể xảy ra.</string>
<string name="home_experimental_kernelsu_body">KernelSU Next là một nhánh của KernelSU luôn có những thử nghiệm và cập nhật hàng ngày, nó có thể không ổn định</string>
<string name="home_experimental_kernelsu_body_point_1"> • Tự chịu trách nghiệm: crashes, những thứ kì lạ, không vào được 1 vài ứng dụng (ngân hàng),... có thể xảy ra</string>
<string name="home_experimental_kernelsu_body_point_2"> • Không bảo hành: Các nhà phát triển KernelSU Next và bên thứ 3 sẽ không chịu trách nghiệm cho hardbrick, bootloop, mất dữ liệu,...</string>
<string name="home_experimental_kernelsu_body_point_3">Đùng để thử nghiệm là chính: Chỉ dành cho những người có kĩ năng, kinh nghiệm, trải nghiệm .</string>
<string name="home_experimental_kernelsu_body_point_3">Dùng để thử nghiệm là chính: Chỉ dành cho những người có k năng, kinh nghiệm trải nghiệm</string>
<string name="about_source_code"><![CDATA[Xem mã nguồn tại %1$s]]></string>
<string name="profile" translatable="false">Hồ sơ ứng dụng</string>
<string name="profile" translatable="false">App Profile</string>
<string name="profile_default">Mặc định</string>
<string name="profile_template">Mẫu</string>
<string name="profile_custom">Tuỳ biến</string>
<string name="profile_template">Bản mẫu</string>
<string name="profile_custom">Tuỳ chỉnh</string>
<string name="profile_name">Tên hồ sơ</string>
<string name="profile_namespace">Mount namespace</string>
<string name="profile_namespace_inherited">Thừa hưởng</string>
@@ -119,85 +128,101 @@
<string name="profile_namespace_individual">Riêng biệt</string>
<string name="profile_groups">Nhóm</string>
<string name="profile_capabilities">Tính tương thích</string>
<string name="profile_selinux_context">Nội dung của SELinux</string>
<string name="profile_selinux_context">Bối cảnh SELinux</string>
<string name="profile_umount_modules">Umount modules</string>
<string name="failed_to_update_app_profile">Cập nhật hồ sơ ứng dụng thất bại cho %s</string>
<string name="require_kernel_version">Phiên bản KernelSU Next được nhúng trong kernel là %1$d và nó quá thấp để trình quản lý hoạt động. Vui lòng cập nhật lên phiên bản %2$d hoặc!</string>
<string name="settings_umount_modules_default">Umount modules cho toàn hệ thống</string>
<string name="settings_umount_modules_default_summary">Giá trị mặc định chung cho \"Umount modules\" trong Hồ sơ ứng dụng. Nếu bật, mọi thay đổi hệ thống do module gây ra sẽ bị gỡ bỏ đối với các ứng dụng chưa đặt hồ sơ riêng. (Modder OEM rom hoặc những người muốn chép file vào phân vùng hệ thống nên tắt).</string>
<string name="settings_susfs_toggle">Ẩn kprobes hook</string>
<string name="settings_susfs_toggle_summary">Tính năng này sẽ vô hiệu hoá những kprobes hook đã tạo bởi kernelsu và sẽ kích hoạt và nhúng những non-kprobes hook, đảm bảo những kernel non-GKI(EOL) hoạt động ổn định.</string>
<string name="profile_umount_modules_summary">Kích hoạt tính năng này sẽ tách các ứng dụng chưa cấp hồ sơ riêng và root.</string>
<string name="failed_to_update_app_profile">Cập nhật Hồ sơ ứng dụng cho %s thất bại</string>
<string name="require_kernel_version">Phiên bản KernelSU Next hiện tại %1$d quá thấp để trình quản lý hoạt động bình thường. Vui lòng cập nhật lên phiên bản %2$d hoặc cao hơn!</string>
<string name="settings_umount_modules_default">Umount modules</string>
<string name="settings_umount_modules_default_summary">Giá trị mặc định chung cho \"Umount modules\" trong Hồ sơ ứng dụng. Nếu được bật, mọi thay đổi hệ thống do các module gây ra sẽ bị gỡ bỏ khỏi hệ thống và các ứng dụng chưa thiết lập hồ sơ</string>
<string name="settings_susfs_toggle">Vô hiệu hoá kprobes hook</string>
<string name="settings_susfs_toggle_summary">Tùy chọn này vô hiệu hóa kprobes hook do KernelSU tạo ra và thay vào đó sẽ kích hoạt non-kprobes hook được nhúng, đảm bảo những Kernel Non-GKI(EOL) hoạt động ổn định</string>
<string name="profile_umount_modules_summary">Bật tùy chọn này sẽ cho phép KernelSU Next khôi phục mọi file đã được các module sửa đổi trong ứng dụng này</string>
<string name="profile_selinux_domain">Tên miền</string>
<string name="profile_selinux_rules">Quyền</string>
<string name="module_update">Có cập nhật!</string>
<string name="module_downloading">Tải xuống module: %s</string>
<string name="module_start_downloading">Đang tải xuống module: %s</string>
<string name="new_version_available">Phiên bản %s đã ra mắt, bấm để cập nhật.</string>
<string name="profile_selinux_rules">Quy tắc</string>
<string name="module_update">Cập nhật</string>
<string name="module_update_available">Cập nhật</string>
<string name="module_updated">Đã cập nhật</string>
<string name="module_downloading">Đang tải xuống module: %s</string>
<string name="module_start_downloading">Bắt đầu tải xuống: %s</string>
<string name="new_version_available">Phiên bản mới %s đã có sẵn, nhấn để cập nhật</string>
<string name="launch_app">Mở</string>
<string name="close">Đóng</string>
<string name="force_stop_app">Buộc tắt</string>
<string name="force_stop_app">Buộc dừng</string>
<string name="restart_app">Khởi động lại</string>
<string name="failed_to_update_sepolicy">Cập nhật phân quyền SELinux thất bại cho: %s</string>
<string name="su_not_allowed">Cấp quyền SU không được phép cho: %s</string>
<string name="module_changelog">Nhật kí thay đổi</string>
<string name="settings_profile_template">Mẫu hồ sơ ứng dụng</string>
<string name="settings_profile_template_summary">Quản lý/Tải xuống trực tuyến hồ sơ ứng dụng</string>
<string name="app_profile_template_create">Tạo mẫu hồ sơ ứng dụng</string>
<string name="app_profile_template_edit">Sửa mẫu hồ sơ ứng dụng</string>
<string name="settings_amoled_mode">Chế độ OLED</string>
<string name="settings_amoled_mode_summary">Bật chủ đề màu đen tuyền hữu ích cho màn hình OLED để giảm mỏi mắt và tiết kiệm pin</string>
<string name="restart_required">Yêu cầu khởi động lại</string>
<string name="restart_app_message">Cần khởi động lại app để thay đổi này có hiệu lực</string>
<string name="failed_to_update_sepolicy">Cập nhật quy tắc SELinux cho %s thất bại</string>
<string name="su_not_allowed">Không được phép cấp quyền SU cho %s</string>
<string name="module_changelog">Changelog</string>
<string name="settings_profile_template">Mẫu Hồ sơ ứng dụng</string>
<string name="settings_profile_template_summary">Quản lý mẫu cục bộ và trực tuyến của Hồ sơ ứng dụng</string>
<string name="app_profile_template_create">Tạo mẫu</string>
<string name="app_profile_template_edit">Chỉnh sửa mẫu</string>
<string name="app_profile_template_id">ID</string>
<string name="app_profile_template_id_invalid">ID mẫu không hợp lệ/tồn tại</string>
<string name="app_profile_template_id_invalid">ID mẫu không hợp lệ</string>
<string name="app_profile_template_name">Tên</string>
<string name="app_profile_template_description">Chi tiết</string>
<string name="app_profile_template_description">Miêu tả</string>
<string name="app_profile_template_save">Lưu</string>
<string name="app_profile_template_delete">Xoá</string>
<string name="app_profile_template_view">Xem mẫu hồ sơ ứng dụng</string>
<string name="app_profile_template_view">Xem mẫu</string>
<string name="app_profile_template_readonly">Chỉ đọc</string>
<string name="app_profile_template_id_exist">ID mẫu hồ sơ ứng dụng đã tồn tại</string>
<string name="app_profile_template_id_exist">ID mẫu đã tồn tại!</string>
<string name="app_profile_import_export">Nhập/Xuất</string>
<string name="app_profile_import_from_clipboard">Nhập từ bảng nhớ</string>
<string name="app_profile_export_to_clipboard">Xuất đến bảng nhớ</string>
<string name="app_profile_template_export_empty">Không tìm thấy hồ sơ ứng dụng nội bộ!</string>
<string name="app_profile_import_from_clipboard">Nhập từ b nhớ tạm clipboard</string>
<string name="app_profile_export_to_clipboard">Xuất vào bộ nhớ tạm clipboard</string>
<string name="app_profile_template_export_empty">Không tìm thấy mẫu cục bộ để xuất!</string>
<string name="app_profile_template_import_success">Nhập thành công</string>
<string name="app_profile_template_sync">Đồng bộ với hồ sơ ứng dụng trực tuyến</string>
<string name="app_profile_template_save_failed">Lưu hồ sơ ứng dụng thất bại</string>
<string name="app_profile_template_import_empty">Bảng nhớ tạm đang trống hoặc sai thông tin!</string>
<string name="module_changelog_failed">Đọc nhất kí thay đôi thất bại: %s</string>
<string name="app_profile_template_sync">Đồng bộ hoá các mẫu trực tuyến</string>
<string name="app_profile_template_save_failed">Lưu mẫu thất bại</string>
<string name="app_profile_template_import_empty">B nhớ tạm đang trống!</string>
<string name="module_changelog_failed">Lấy changelog thất bại: %s</string>
<string name="settings_check_update">Kiểm tra cập nhật</string>
<string name="settings_check_update_summary">Tự động kiểm tra cập nhật khi mở ứng dụng.</string>
<string name="settings_check_update_summary">Tự động kiểm tra cập nhật khi mở ứng dụng</string>
<string name="grant_root_failed">Cấp quyền root thất bại!</string>
<string name="action">Chạy</string>
<string name="action">Khởi chạy</string>
<string name="webui">WebUI</string>
<string name="open">Mở</string>
<string name="enable_web_debugging">Kích hoạt WebView debugging</string>
<string name="enable_web_debugging_summary">Sử dụng để debug WebUI. Sử dụng khi bạn có kinh nghiệm, kĩ năng.</string>
<string name="direct_install">Cài đặt trực tiếp (cho GKI 2.0)</string>
<string name="enable_web_debugging">Gỡ lỗi WebView</string>
<string name="enable_web_debugging_summary">Có thể sử dụng để gỡ lỗi WebUI. Vui lòng chỉ bật khi cần thiết</string>
<string name="direct_install">Cài đặt trực tiếp (Khuyến nghị)</string>
<string name="select_file">Chọn file</string>
<string name="install_inactive_slot">Cài đặt vào phân vùng update (cho người OTA)</string>
<string name="install_inactive_slot_warning">Thiết bị của bạn sẽ BUỘC khởi động vào phân dùng chưa được sử dụng (A hoặc B)\nSử dụng sau khi update OTA.\nTiếp?</string>
<string name="install_next">Tiếp</string>
<string name="select_file_tip">Tập tin %1$s được khuyến khích</string>
<string name="install_inactive_slot">Cài đặt vào phân vùng chưa được sử dụng (Sau OTA)</string>
<string name="install_inactive_slot_warning">Thiết bị của bạn sẽ **BUỘC** phải khởi động vào phân vùng chưa được sử dụng!\nChỉ sử dụng tùy chọn này sau khi OTA hoàn tất.\nTiếp tục?</string>
<string name="install_next">Kế tiếp</string>
<string name="select_file_tip">Phân vùng %1$s được khuyến nghị</string>
<string name="select_kmi">Chọn KMI</string>
<string name="shrink_sparse_image">Nén module.img</string>
<string name="shrink_sparse_image_message">Nén lại module.img về kích thước thực, CHỈ SỬ DỤNG KHI CÓ HIỂU BIẾT VÀ CẦN THIẾT!?!?!?!?!.</string>
<string name="shrink_sparse_image">Nén file .img</string>
<string name="shrink_sparse_image_message">Thay đổi kích thước file .img nơi đặt module theo kích thước thực tế của nó. Lưu ý điều này có thể khiến module hoạt động bất thường, vì vậy vui lòng chỉ sử dụng khi cần thiết (Chẳng hạn như để sao lưu)</string>
<string name="settings_uninstall">Gỡ cài đặt</string>
<string name="settings_uninstall_temporary">Gỡ cài đặt tạm thời</string>
<string name="settings_uninstall_permanent">Gỡ cài đặt sạch</string>
<string name="settings_restore_stock_image">Khôi phục phân vùng khởi động về mặc định</string>
<string name="settings_uninstall_temporary_message">Tạm thời gỡ KernelSU Next.</string>
<string name="settings_uninstall_permanent_message">Gỡ cài đặt sạch hoàn toàn, trả về trạng thái ban đầu.</string>
<string name="settings_restore_stock_image_message">Khôi phục lại boot lúc đầu (nếu có); nếu bạn cần gỡ hẳn KernelSU Next, sử dụng\"Gỡ cài đặt sạch\".</string>
<string name="flashing">Đang cài đặt</string>
<string name="flash_success">Cài đặt thành công</string>
<string name="flash_failed">Cài đặt thất bại</string>
<string name="selected_lkm">Đã chọn file LKM: %s</string>
<string name="settings_uninstall_temporary_message">Gỡ cài đặt tạm thời KernelSU Next, khôi phục lại trạng thái ban đầu sau lần khởi động lại tiếp theo</string>
<string name="settings_uninstall_permanent_message">Gỡ cài đặt KernelSU Next (Root và tất cả các module) sạch hoàn toàn, trả về trạng thái ban đầu</string>
<string name="settings_restore_stock_image_message">Khôi phục lại boot lúc đầu (Nếu có bản sao lưu), thường được sử dụng trước OTA; nếu bạn cần gỡ hẳn KernelSU Next, sử dụng \"Gỡ cài đặt sạch\"</string>
<string name="flashing">Đang Flash...</string>
<string name="flash_success">Flash thành công</string>
<string name="flash_failed">Flash thất bại</string>
<string name="selected_lkm">Đã chọn LKM: %s</string>
<string name="save_log">Lưu logs</string>
<string name="log_saved">Logs đã được lưu</string>
<string name="send_log">Chia sẻ logs</string>
<string name="settings_disable_su">Vô hiệu hoá khả năng của lệnh SU</string>
<string name="settings_disable_su_summary">Vô hiệu hoá khả năng thực thi lệnh SU để lấy quyền root (những app đã cấp trước đó không bị ảnh hưởng).</string>
<string name="settings_disable_su">Vô hiệu hoá lệnh SU</string>
<string name="settings_disable_su_summary">Vô hiệu hoá khả năng thực thi lệnh SU để lấy quyền root (Những app đã cấp trước đó không bị ảnh hưởng)</string>
<string name="settings_language">Ngôn ngữ</string>
<string name="use_webuix">Dùng WebUI X</string>
<string name="use_webuix_summary">Dùng WebUI X thay vì WebUI (hỗ trợ nhiều API hơn).</string>
<string name="use_webuix_eruda">Nhúng nhân Eruda vào WebUI X</string>
<string name="use_webuix_eruda_summary">Nhúng trình gỡ lỗi vào WebUI X để việc sửa lỗi thuận tiện hơn. Yêu cầu gỡ lỗi webview được bật.</string>
<string name="system_default">Mặc định theo hệ thống</string>
<string name="settings_legacyui">Sử dụng giao diện cũ</string>
<string name="settings_legacyui_summary">Chuyển về kiểu giao diện cũ</string>
<string name="settings_banner">Hiển thị banner</string>
<string name="settings_banner_summary">Hiển thị banner cho các module</string>
<string name="use_webuix">Sử dụng WebUI X</string>
<string name="use_webuix_summary">Sử dụng WebUI X thay cho KSU WebUI, vì hỗ trợ nhiều API hơn</string>
<string name="use_webuix_eruda">Nhúng Eruda vào WebUI X</string>
<string name="use_webuix_eruda_summary">Nhúng trình gỡ lỗi vào WebUI X để việc gỡ lỗi thuận tiện hơn. Yêu cầu gỡ lỗi WebView được bật</string>
<string name="customization">Tùy biến</string>
<string name="developer">Nhà phát triển</string>
<string name="sucompat_disabled">ĐÃ VÔ HIỆU HÓA SUCOMPAT</string>
<string name="zygisk_required">Yêu cầu Zygisk</string>
</resources>

View File

@@ -3,7 +3,7 @@
<string name="issue_report_title">遇到问题?</string>
<string name="issue_report_body">发现错误或者有改进建议?</string>
<string name="issue_report_body_2">快向我们报告吧!</string>
<string name="issue_report_github"> GitHub 报告</string>
<string name="issue_report_github"> GitHub 报告</string>
<string name="issue_report_telegram">在 Telegram 获得最新消息</string>
<string name="issue_report_github_link">https://github.com/KernelSU-Next/KernelSU-Next/issues</string>
<string name="issue_report_telegram_link">https://t.me/ksunext</string>
@@ -18,7 +18,7 @@
<string name="home_working_version">版本:%d</string>
<string name="home_superuser_count">超级用户数:%d</string>
<string name="home_module_count">模块数:%d</string>
<string name="home_module_update_count">可更新模块: %d</string>
<string name="home_module_update_count">%d 个模块可更新!</string>
<string name="home_failure">内核中未找到 KernelSU Next V2 签名! [ !KSU_NEXT || != size/hash ]</string>
<string name="home_failure_tip">请让你的内核开发人员集成 KernelSU Next</string>
<string name="home_kernel">内核版本</string>
@@ -47,19 +47,19 @@
<string name="module_install_prompt_with_name">是否要继续安装模块 %1$s </string>
<string name="module_sort_a_to_z">按 A - Z 排序</string>
<string name="module_sort_z_to_a">按 Z - A 排序</string>
<string name="module_size_low_to_high">按模块大小正序排序</string>
<string name="module_size_high_to_low">按模块大小倒序排序</string>
<string name="module_size_low_to_high">正序排序(按模块大小</string>
<string name="module_size_high_to_low">倒序排序(按模块大小</string>
<string name="uninstall">卸载</string>
<string name="restore">恢复</string>
<string name="module_install">安装</string>
<string name="install">安装</string>
<string name="reboot">重启</string>
<string name="uninstalled">卸载</string>
<string name="uninstalled">重启以应用卸载</string>
<string name="settings">设置</string>
<string name="reboot_userspace">软重启</string>
<string name="reboot_recovery">重启到 Recovery</string>
<string name="reboot_recovery">重启到恢复模式</string>
<string name="reboot_bootloader">重启到 Bootloader</string>
<string name="reboot_download">重启到 Download</string>
<string name="reboot_download">重启到下载模式</string>
<string name="reboot_edl">重启到 EDL 模式</string>
<string name="about">关于</string>
<string name="module_uninstall_confirm">确定要卸载模块 %s 吗?</string>
@@ -76,14 +76,14 @@
<string name="module_update_json_empty">无更新配置</string>
<string name="enable_developer_options">启用开发者模式</string>
<string name="enable_developer_options_summary">显示隐藏的开发者专用设置和调试信息。</string>
<string name="module_overlay_fs_not_available">OverlayFS 被内核禁用,模块系统不可用</string>
<string name="module_overlay_fs_not_available">OverlayFS 被内核禁用,模块系统不可用</string>
<string name="refresh">刷新</string>
<string name="show_system_apps">显示系统应用</string>
<string name="hide_system_apps">隐藏系统应用</string>
<string name="export_log">导出日志</string>
<string name="safe_mode">安全模式</string>
<string name="reboot_to_apply">重启生效</string>
<string name="module_magisk_conflict">由于与 Magisk 冲突,模块系统不可用</string>
<string name="module_magisk_conflict">由于与 Magisk 冲突,模块系统不可用</string>
<string name="home_mount_system">模块系统</string>
<string name="home_magic_mount">Magic Mount</string>
<string name="home_overlayfs_mount">OverlayFS</string>
@@ -101,19 +101,19 @@
<string name="allowlist_backup_message">备份当前的超级用户列表</string>
<string name="module_backup">备份模块</string>
<string name="module_backup_message">备份当前已安装的模块。</string>
<string name="warning">警告</string>
<string name="warning">免责声明</string>
<string name="warning_message">此为尚在开发阶段的实验功能,继续操作前请确保模块已备份。使用此功能需要先了解其风险,在知晓后果的情况下谨慎操作。</string>
<string name="proceed">继续</string>
<string name="cancel">取消</string>
<string name="later">稍后</string>
<string name="lkm_warning_message">此 LKM 补丁依赖于闭源组件,你确定要继续吗?</string>
<string name="lkm_warning_message">此 LKM 补丁依赖于闭源组件,确认操作代表你知晓因继续使用该功能所导致的一切后果与开发团队无关。</string>
<string name="home_next_kernelsu">🔥 Next 构建</string>
<string name="home_next_kernelsu_repo">https://github.com/KernelSU-Next/KernelSU-Next</string>
<string name="home_next_kernelsu_body">Next 实验性分支。在 GitHub 上查看!</string>
<string name="home_experimental_kernelsu">⚠️ 实验性开发警告!</string>
<string name="home_experimental_kernelsu_repo">127.0.0.1</string>
<string name="home_experimental_kernelsu_body">KernelSU Next 属于第三方版本,保持着积极的实验开发。该版本所得即所用,不保证稳定性、性能和可靠性。</string>
<string name="home_experimental_kernelsu_body_point_1"> • 风险自担:可能会发生崩溃、意外行为或导致系统故障。</string>
<string name="home_experimental_kernelsu_body_point_1"> • 风险自担:可能会出现崩溃、意外行为或导致系统故障。</string>
<string name="home_experimental_kernelsu_body_point_2"> • 不做保证:开发者不对数据丢失、系统损坏等问题负责。</string>
<string name="home_experimental_kernelsu_body_point_3"> • 仅供测试:此版适合了解风险并能轻松解决问题的用户。</string>
<string name="about_source_code"><![CDATA[在 %1$s 查看源代码]]></string>
@@ -140,7 +140,8 @@
<string name="profile_selinux_domain"></string>
<string name="profile_selinux_rules">规则</string>
<string name="module_update">更新</string>
<string name="module_updated">更新</string>
<string name="module_update_available">更新可用</string>
<string name="module_updated">重启以应用更新</string>
<string name="module_downloading">正在下载模块: %s</string>
<string name="module_start_downloading">开始下载: %s</string>
<string name="new_version_available">发现新版本:%s点击升级。</string>
@@ -178,9 +179,9 @@
<string name="app_profile_template_import_empty">剪贴板为空!</string>
<string name="module_changelog_failed">获取更新日志失败: %s</string>
<string name="settings_check_update">检查更新</string>
<string name="settings_check_update_summary">在应用启动后自动检查是否有新。</string>
<string name="settings_check_update_summary">在应用启动后自动检查是否有新版本</string>
<string name="grant_root_failed">获取 root 失败!</string>
<string name="action">动作</string>
<string name="action">执行</string>
<string name="webui">WebUI</string>
<string name="open">打开</string>
<string name="enable_web_debugging">启用 WebView 调试</string>
@@ -208,16 +209,20 @@
<string name="save_log">保存日志</string>
<string name="log_saved">日志已保存</string>
<string name="send_log">分享日志</string>
<string name="settings_disable_su">禁用超级用户指令</string>
<string name="settings_disable_su">禁用 SU 兼容</string>
<string name="settings_disable_su_summary">临时禁止任何应用通过 su 命令获取 root 权限(正在运行的 root 进程不受影响)。</string>
<string name="settings_language">语言</string>
<string name="system_default">系统默认</string>
<string name="settings_legacyui">切换到传统 UI</string>
<string name="settings_legacyui_summary">使用早期的用户界面风格。</string>
<string name="settings_banner">启用横幅</string>
<string name="settings_banner_summary">针对模块启用背景横幅。</string>
<string name="use_webuix">使用 WebUI X</string>
<string name="use_webuix_summary">使用更完善的 WebUI X 取代旧有 WebUI请注意模块开发者可在模块信息中单独覆盖此项设置。</string>
<string name="use_webuix_summary">使用更完善的 WebUI X 取代旧有 WebUI请注意模块开发者可在模块信息中单独覆盖此项设置。</string>
<string name="use_webuix_eruda">对 WebUI X 注入 Eruda</string>
<string name="use_webuix_eruda_summary">使用 WebUI X 时注入控制台以便于调试,需要启用 WebView 调试功能。</string>
<string name="customization">自定义</string>
<string name="customization">界面设置</string>
<string name="developer">开发者选项</string>
<string name="sucompat_disabled">SU 兼容被禁用</string>
<string name="zygisk_required">需要 Zygisk</string>
</resources>

View File

@@ -47,14 +47,14 @@
<string name="module_install_prompt_with_name">是否要繼續安裝模組 %1$s </string>
<string name="module_sort_a_to_z">按 A - Z 排序</string>
<string name="module_sort_z_to_a">按 Z - A 排序</string>
<string name="module_size_low_to_high">按模組大小正序排序</string>
<string name="module_size_high_to_low">按模組大小倒序排序</string>
<string name="module_size_low_to_high">正序排序(按模組大小</string>
<string name="module_size_high_to_low">倒序排序(按模組大小</string>
<string name="uninstall">卸載</string>
<string name="restore">恢復</string>
<string name="module_install">安裝</string>
<string name="install">安裝</string>
<string name="reboot">重啟</string>
<string name="uninstalled">已經卸載</string>
<string name="uninstalled">重啓以完成卸載</string>
<string name="settings">設定</string>
<string name="reboot_userspace">軟重啟</string>
<string name="reboot_recovery">重啟到 Recovery</string>
@@ -140,7 +140,8 @@
<string name="profile_selinux_domain"></string>
<string name="profile_selinux_rules">規則</string>
<string name="module_update">更新</string>
<string name="module_updated">已經更新</string>
<string name="module_update_available">更新可用</string>
<string name="module_updated">重啓以完成更新</string>
<string name="module_downloading">正在下載模組: %s</string>
<string name="module_start_downloading">開始下載: %s</string>
<string name="new_version_available">發現新版本:%s點擊升級。</string>
@@ -178,9 +179,9 @@
<string name="app_profile_template_import_empty">剪貼簿為空!</string>
<string name="module_changelog_failed">獲取更新日誌失敗: %s</string>
<string name="settings_check_update">檢查更新</string>
<string name="settings_check_update_summary">在應用啟動後自動檢查是否有新版。</string>
<string name="settings_check_update_summary">在應用啟動後自動檢查是否有新版</string>
<string name="grant_root_failed">獲取 root 失敗!</string>
<string name="action">Action</string>
<string name="action">執行</string>
<string name="webui">WebUI</string>
<string name="open">打開</string>
<string name="enable_web_debugging">啟用 WebView 偵錯</string>
@@ -211,6 +212,7 @@
<string name="settings_disable_su">關閉 SU 相容</string>
<string name="settings_disable_su_summary">暫時禁止任何應用通過 su 命令獲取 root 權限(已運行的 root 進程不受影響)。</string>
<string name="settings_language">語言</string>
<string name="system_default">跟隨系統</string>
<string name="settings_legacyui">換回傳統 UI</string>
<string name="settings_legacyui_summary">使用早期版本 UI 風格。</string>
<string name="settings_banner">啓用橫幅</string>
@@ -219,5 +221,8 @@
<string name="use_webuix_summary">使用更爲完善的 WebUI X 而不是 WebUI請注意模組開發人員能在模組信息中覆寫這個設置。</string>
<string name="use_webuix_eruda">將 Eruda 注入 WebUI X</string>
<string name="use_webuix_eruda_summary">在使用 WebUI X 時注入控制台以便於偵錯,需要啟用 WebView 偵錯功能。</string>
<string name="customization">客制化</string>
<string name="customization">介面選項</string>
<string name="developer">開發人員設定</string>
<string name="sucompat_disabled">SU 兼容被禁用</string>
<string name="zygisk_required">需要 Zygisk</string>
</resources>

View File

@@ -16,8 +16,6 @@
<string name="lkm_alternative_suggestion">Install GKI kernel or integrate KernelSU Next to your device.</string>
<string name="home_working">Working</string>
<string name="home_working_version">Version: %d</string>
<string name="home_superuser_count">Superusers: %d</string>
<string name="home_module_count">Modules: %d</string>
<string name="home_module_update_count">Updates: %d</string>
<string name="home_failure">KernelSU Next v2 signature not found in kernel! [ !KSU_NEXT || != size/hash ]</string>
<string name="home_failure_tip">Ask your kernel developer to integrate KernelSU Next!</string>
@@ -44,7 +42,7 @@
<string name="module_failed_to_disable">Failed to disable module: %s</string>
<string name="module_empty">No module installed</string>
<string name="module">Module</string>
<string name="module_install_prompt_with_name">The following module(s) will be installed: %1$s</string>
<string name="module_install_prompt_with_name">The following modules will be installed: %1$s</string>
<string name="module_sort_a_to_z">Sort (A → Z)</string>
<string name="module_sort_z_to_a">Sort (Z → A)</string>
<string name="module_size_low_to_high">Sort (Low → High)</string>
@@ -56,7 +54,7 @@
<string name="reboot">Reboot</string>
<string name="uninstalled">Uninstalled</string>
<string name="settings">Settings</string>
<string name="reboot_userspace">Soft Reboot</string>
<string name="reboot_userspace">Soft reboot</string>
<string name="reboot_recovery">Reboot to Recovery</string>
<string name="reboot_bootloader">Reboot to Bootloader</string>
<string name="reboot_download">Reboot to Download</string>
@@ -76,7 +74,7 @@
<string name="module_update_json_empty">Empty</string>
<string name="enable_developer_options">Enable developer options</string>
<string name="enable_developer_options_summary">Show hidden settings and debug info relevant only for developers.</string>
<string name="module_overlay_fs_not_available">Modules are unavailable as OverlayFS is disabled by the kernel.</string>
<string name="module_overlay_fs_not_available">Modules are unavailable as OverlayFS is disabled by the kernel!</string>
<string name="refresh">Refresh</string>
<string name="show_system_apps">Show system apps</string>
<string name="hide_system_apps">Hide system apps</string>
@@ -112,7 +110,7 @@
<string name="home_next_kernelsu_body">Next experimental branch. Check it out on GitHub!</string>
<string name="home_experimental_kernelsu">⚠️ Experimental development warning!</string>
<string name="home_experimental_kernelsu_repo">127.0.0.1</string>
<string name="home_experimental_kernelsu_body">KernelSU Next is a non-official version that is always under active experimental development. It is provided as-is, with no guarantees of stability, performance, or reliability.</string>
<string name="home_experimental_kernelsu_body">KernelSU Next is a non-official version that is always under active experimental development. It\'s provided as-is, with no guarantees of stability, performance, or reliability.</string>
<string name="home_experimental_kernelsu_body_point_1"> • Use at your own risk: crashes, unexpected behavior, or system issues may occur.</string>
<string name="home_experimental_kernelsu_body_point_2"> • No warranty: the developers aren\'t responsible for any data loss, system damage, or other consequences resulting from its use.</string>
<string name="home_experimental_kernelsu_body_point_3"> • For testing purposes only: intended for users who understand the risks and are comfortable troubleshooting issues.</string>
@@ -140,6 +138,7 @@
<string name="profile_selinux_domain">Domain</string>
<string name="profile_selinux_rules">Rules</string>
<string name="module_update">Update</string>
<string name="module_update_available">Update</string>
<string name="module_updated">Updated</string>
<string name="module_downloading">Downloading module: %s</string>
<string name="module_start_downloading">Start downloading: %s</string>
@@ -152,10 +151,10 @@
<string name="settings_amoled_mode_summary">Enable a pure black theme useful for AMOLED screens to reduce eye strain and save battery.</string>
<string name="restart_required">Restart required</string>
<string name="restart_app_message">The app needs to restart for this change to take effect.</string>
<string name="failed_to_update_sepolicy">Failed to update SELinux rules for: %s</string>
<string name="su_not_allowed">Granting superuser isn\'t allowed for: %s</string>
<string name="failed_to_update_sepolicy">Failed to update SELinux rules for %s</string>
<string name="su_not_allowed">Couldn\'t grant Superuser access to %s</string>
<string name="module_changelog">Changelog</string>
<string name="settings_profile_template">App Profile Template</string>
<string name="settings_profile_template">App Profile template</string>
<string name="settings_profile_template_summary">Manage local and online template of App Profile</string>
<string name="app_profile_template_create">Create template</string>
<string name="app_profile_template_edit">Edit template</string>
@@ -177,8 +176,8 @@
<string name="app_profile_template_save_failed">Failed to save template</string>
<string name="app_profile_template_import_empty">Clipboard is empty!</string>
<string name="module_changelog_failed">Fetch changelog failed: %s</string>
<string name="settings_check_update">Check update</string>
<string name="settings_check_update_summary">Automatically check for updates when opening the app.</string>
<string name="settings_check_update">Check for updates</string>
<string name="settings_check_update_summary">Automatically check for updates when opening the app</string>
<string name="grant_root_failed">Failed to grant root!</string>
<string name="action">Action</string>
<string name="webui">WebUI</string>
@@ -209,9 +208,10 @@
<string name="log_saved">Logs saved</string>
<string name="send_log">Share logs</string>
<string name="settings_disable_su">Disable su compatibility</string>
<string name="settings_disable_su_summary">Temporarily disable the ability of any app to gain root privileges via the su command (existing root processes won\'t be affected).</string>
<string name="settings_disable_su_summary">Temporarily disable the ability of any app to gain root privileges via the su command (Existing root processes won\'t be affected).</string>
<string name="settings_language">Language</string>
<string name="settings_legacyui">Use Legacy UI</string>
<string name="system_default">System default</string>
<string name="settings_legacyui">Use legacy UI</string>
<string name="settings_legacyui_summary">Switch to the previous user interface style.</string>
<string name="settings_banner">Enable banners</string>
<string name="settings_banner_summary">Show background banners for modules.</string>
@@ -222,4 +222,14 @@
<string name="customization">Customization</string>
<string name="developer">Developer</string>
<string name="sucompat_disabled">SUCOMPAT DISABLED</string>
<string name="zygisk_required">Zygisk required</string>
<string name="zygisk_status">Zygisk injection</string>
<plurals name="home_superuser_count">
<item quantity="one">Superuser</item>
<item quantity="other">Superusers</item>
</plurals>
<plurals name="home_module_count">
<item quantity="one">Module</item>
<item quantity="other">Modules</item>
</plurals>
</resources>

View File

@@ -17,7 +17,7 @@ parcelablelist = "2.0.1"
libsu = "6.0.0"
apksign = "1.4"
cmaker = "1.2"
mmrl = "2bb00b3c2b"
mmrl = "v33890"
[plugins]
agp-app = { id = "com.android.application", version.ref = "agp" }
@@ -74,7 +74,4 @@ markdown = { group = "io.noties.markwon", name = "core", version.ref = "markdown
lsposed-cxx = { module = "org.lsposed.libcxx:libcxx", version = "28.1.13356709" }
mmrl-webui = { group = "com.github.MMRLApp.MMRL", name = "webui", version.ref = "mmrl" }
mmrl-platform = { group = "com.github.MMRLApp.MMRL", name = "platform", version.ref = "mmrl" }
mmrl-ui = { group = "com.github.MMRLApp.MMRL", name = "ui", version.ref = "mmrl" }
mmrl-hidden-api = { group = "com.github.MMRLApp.MMRL", name = "hidden-api", version.ref = "mmrl" }
mmrl-ui = { group = "com.github.MMRLApp.MMRL", name = "ui", version.ref = "mmrl" }

View File

@@ -167,6 +167,36 @@ fn do_cpio_cmd(magiskboot: &Path, workdir: &Path, cmd: &str) -> Result<()> {
Ok(())
}
fn do_vendor_init_boot_cpio_cmd(magiskboot: &Path, workdir: &Path, cmd: &str) -> Result<()> {
let vendor_init_boot_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_init_boot_cpio)
.arg(cmd)
.status()?;
ensure!(status.success(), "magiskboot cpio {} failed", cmd);
Ok(())
}
fn do_vendor_ramdisk_cpio_cmd(magiskboot: &Path, workdir: &Path, cmd: &str) -> Result<()> {
let vendor_ramdisk_cpio = workdir.join("vendor_ramdisk").join("ramdisk.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)
@@ -179,6 +209,32 @@ fn is_magisk_patched(magiskboot: &Path, workdir: &Path) -> Result<bool> {
Ok(status.code() == Some(1))
}
fn is_magisk_patched_vendor_init_boot(magiskboot: &Path, workdir: &Path) -> Result<bool> {
let vendor_init_boot_cpio = workdir.join("vendor_ramdisk").join("init_boot.cpio");
let status = Command::new(magiskboot)
.current_dir(workdir)
.stdout(Stdio::null())
.stderr(Stdio::null())
.args(["cpio", vendor_init_boot_cpio.to_str().unwrap(), "test"])
.status()?;
// 0: stock, 1: magisk
Ok(status.code() == Some(1))
}
fn is_magisk_patched_vendor_ramdisk(magiskboot: &Path, workdir: &Path) -> Result<bool> {
let vendor_ramdisk_cpio = workdir.join("vendor_ramdisk").join("ramdisk.cpio");
let status = Command::new(magiskboot)
.current_dir(workdir)
.stdout(Stdio::null())
.stderr(Stdio::null())
.args(["cpio", vendor_ramdisk_cpio.to_str().unwrap(), "test"])
.status()?;
// 0: stock, 1: magisk
Ok(status.code() == Some(1))
}
fn is_kernelsu_patched(magiskboot: &Path, workdir: &Path) -> Result<bool> {
let status = Command::new(magiskboot)
.current_dir(workdir)
@@ -190,6 +246,38 @@ fn is_kernelsu_patched(magiskboot: &Path, workdir: &Path) -> Result<bool> {
Ok(status.success())
}
fn is_kernelsu_patched_vendor_init_boot(magiskboot: &Path, workdir: &Path) -> Result<bool> {
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())
.args([
"cpio",
vendor_ramdisk_cpio.to_str().unwrap(),
"exists kernelsu.ko",
])
.status()?;
Ok(status.success())
}
fn is_kernelsu_patched_vendor_ramdisk(magiskboot: &Path, workdir: &Path) -> Result<bool> {
let vendor_ramdisk_cpio = workdir.join("vendor_ramdisk").join("ramdisk.cpio");
let status = Command::new(magiskboot)
.current_dir(workdir)
.stdout(Stdio::null())
.stderr(Stdio::null())
.args([
"cpio",
vendor_ramdisk_cpio.to_str().unwrap(),
"exists kernelsu.ko",
])
.status()?;
Ok(status.success())
}
fn dd<P: AsRef<Path>, Q: AsRef<Path>>(ifile: P, ofile: Q) -> Result<()> {
let status = Command::new("dd")
.stdout(Stdio::null())
@@ -234,9 +322,22 @@ pub fn restore(
.status()?;
ensure!(status.success(), "magiskboot unpack failed");
let no_ramdisk = !workdir.join("ramdisk.cpio").exists();
let no_vendor_init_boot = !workdir
.join("vendor_ramdisk")
.join("init_boot.cpio")
.exists();
let no_vendor_ramdisk = !workdir
.join("vendor_ramdisk")
.join("ramdisk.cpio")
.exists();
let is_kernelsu_patched = is_kernelsu_patched(&magiskboot, workdir)?;
let is_kernelsu_patched_vendor_init_boot =
is_kernelsu_patched_vendor_init_boot(&magiskboot, workdir)?;
let is_kernelsu_patched_vendor_ramdisk =
is_kernelsu_patched_vendor_ramdisk(&magiskboot, workdir)?;
ensure!(
is_kernelsu_patched,
is_kernelsu_patched || is_kernelsu_patched_vendor_init_boot || is_kernelsu_patched_vendor_ramdisk,
"boot image is not patched by KernelSU Next"
);
@@ -270,16 +371,44 @@ pub fn restore(
}
if new_boot.is_none() {
// remove kernelsu.ko
do_cpio_cmd(&magiskboot, workdir, "rm kernelsu.ko")?;
if no_ramdisk {
if !no_vendor_init_boot {
// vendor init_boot restore
do_vendor_init_boot_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_init_boot_cpio_cmd(&magiskboot, workdir, "exists init.real").is_ok();
if status {
do_vendor_init_boot_cpio_cmd(&magiskboot, workdir, "mv init.real init")?;
} else {
let vendor_init_boot = workdir.join("vendor_ramdisk").join("init_boot.cpio");
std::fs::remove_file(vendor_init_boot)?;
}
} else if !no_vendor_ramdisk {
// vendor ramdisk restore
do_vendor_ramdisk_cpio_cmd(&magiskboot, workdir, "rm kernelsu.ko")?;
let status =
do_vendor_ramdisk_cpio_cmd(&magiskboot, workdir, "exists init.real").is_ok();
if status {
do_vendor_ramdisk_cpio_cmd(&magiskboot, workdir, "mv init.real init")?;
} else {
let vendor_ramdisk = workdir.join("vendor_ramdisk").join("ramdisk.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 +570,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)
@@ -457,28 +581,73 @@ fn do_patch(
ensure!(status.success(), "magiskboot unpack failed");
let no_ramdisk = !workdir.join("ramdisk.cpio").exists();
let no_vendor_init_boot = !workdir
.join("vendor_ramdisk")
.join("init_boot.cpio")
.exists();
let no_vendor_ramdisk = !workdir
.join("vendor_ramdisk")
.join("ramdisk.cpio")
.exists();
if no_ramdisk && no_vendor_init_boot && no_vendor_ramdisk {
bail!("No compatible ramdisk found.");
}
let is_magisk_patched = is_magisk_patched(&magiskboot, workdir)?;
let is_magisk_patched_vendor_init_boot =
is_magisk_patched_vendor_init_boot(&magiskboot, workdir)?;
let is_magisk_patched_vendor_ramdisk =
is_magisk_patched_vendor_ramdisk(&magiskboot, workdir)?;
ensure!(
no_ramdisk || !is_magisk_patched,
!is_magisk_patched || !is_magisk_patched_vendor_init_boot || !is_magisk_patched_vendor_ramdisk,
"Cannot work with Magisk patched image"
);
println!("- Adding KernelSU Next LKM");
let is_kernelsu_patched = is_kernelsu_patched(&magiskboot, workdir)?;
let is_kernelsu_patched_vendor_init_boot =
is_kernelsu_patched_vendor_init_boot(&magiskboot, workdir)?;
let is_kernelsu_patched_vendor_ramdisk =
is_kernelsu_patched_vendor_ramdisk(&magiskboot, workdir)?;
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 !is_kernelsu_patched || (no_ramdisk && !is_kernelsu_patched_vendor_init_boot) || (no_ramdisk && no_vendor_init_boot && !is_kernelsu_patched_vendor_ramdisk)
{
if no_ramdisk {
if !no_vendor_init_boot {
// vendor init_boot patching
let status = do_vendor_init_boot_cpio_cmd(&magiskboot, workdir, "exists init");
if status.is_ok() {
do_vendor_init_boot_cpio_cmd(&magiskboot, workdir, "mv init init.real")?;
}
} else if !no_vendor_ramdisk {
// vendor ramdisk patching
let status = do_vendor_ramdisk_cpio_cmd(&magiskboot, workdir, "exists init");
if status.is_ok() {
do_vendor_ramdisk_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 {
if !no_vendor_init_boot {
do_vendor_init_boot_cpio_cmd(&magiskboot, workdir, "add 0755 init init")?;
do_vendor_init_boot_cpio_cmd(&magiskboot, workdir, "add 0755 kernelsu.ko kernelsu.ko")?;
} else if !no_vendor_ramdisk {
do_vendor_ramdisk_cpio_cmd(&magiskboot, workdir, "add 0750 init init")?;
do_vendor_ramdisk_cpio_cmd(&magiskboot, workdir, "add 0750 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 +827,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,36 @@ fn do_cpio_cmd(magiskboot: &Path, workdir: &Path, cmd: &str) -> Result<()> {
Ok(())
}
fn do_vendor_init_boot_cpio_cmd(magiskboot: &Path, workdir: &Path, cmd: &str) -> Result<()> {
let vendor_init_boot_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_init_boot_cpio)
.arg(cmd)
.status()?;
ensure!(status.success(), "magiskboot cpio {} failed", cmd);
Ok(())
}
fn do_vendor_ramdisk_cpio_cmd(magiskboot: &Path, workdir: &Path, cmd: &str) -> Result<()> {
let vendor_ramdisk_cpio = workdir.join("vendor_ramdisk").join("ramdisk.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)
@@ -179,6 +209,32 @@ fn is_magisk_patched(magiskboot: &Path, workdir: &Path) -> Result<bool> {
Ok(status.code() == Some(1))
}
fn is_magisk_patched_vendor_init_boot(magiskboot: &Path, workdir: &Path) -> Result<bool> {
let vendor_init_boot_cpio = workdir.join("vendor_ramdisk").join("init_boot.cpio");
let status = Command::new(magiskboot)
.current_dir(workdir)
.stdout(Stdio::null())
.stderr(Stdio::null())
.args(["cpio", vendor_init_boot_cpio.to_str().unwrap(), "test"])
.status()?;
// 0: stock, 1: magisk
Ok(status.code() == Some(1))
}
fn is_magisk_patched_vendor_ramdisk(magiskboot: &Path, workdir: &Path) -> Result<bool> {
let vendor_ramdisk_cpio = workdir.join("vendor_ramdisk").join("ramdisk.cpio");
let status = Command::new(magiskboot)
.current_dir(workdir)
.stdout(Stdio::null())
.stderr(Stdio::null())
.args(["cpio", vendor_ramdisk_cpio.to_str().unwrap(), "test"])
.status()?;
// 0: stock, 1: magisk
Ok(status.code() == Some(1))
}
fn is_kernelsu_patched(magiskboot: &Path, workdir: &Path) -> Result<bool> {
let status = Command::new(magiskboot)
.current_dir(workdir)
@@ -190,6 +246,38 @@ fn is_kernelsu_patched(magiskboot: &Path, workdir: &Path) -> Result<bool> {
Ok(status.success())
}
fn is_kernelsu_patched_vendor_init_boot(magiskboot: &Path, workdir: &Path) -> Result<bool> {
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())
.args([
"cpio",
vendor_ramdisk_cpio.to_str().unwrap(),
"exists kernelsu.ko",
])
.status()?;
Ok(status.success())
}
fn is_kernelsu_patched_vendor_ramdisk(magiskboot: &Path, workdir: &Path) -> Result<bool> {
let vendor_ramdisk_cpio = workdir.join("vendor_ramdisk").join("ramdisk.cpio");
let status = Command::new(magiskboot)
.current_dir(workdir)
.stdout(Stdio::null())
.stderr(Stdio::null())
.args([
"cpio",
vendor_ramdisk_cpio.to_str().unwrap(),
"exists kernelsu.ko",
])
.status()?;
Ok(status.success())
}
fn dd<P: AsRef<Path>, Q: AsRef<Path>>(ifile: P, ofile: Q) -> Result<()> {
let status = Command::new("dd")
.stdout(Stdio::null())
@@ -234,9 +322,22 @@ pub fn restore(
.status()?;
ensure!(status.success(), "magiskboot unpack failed");
let no_ramdisk = !workdir.join("ramdisk.cpio").exists();
let no_vendor_init_boot = !workdir
.join("vendor_ramdisk")
.join("init_boot.cpio")
.exists();
let no_vendor_ramdisk = !workdir
.join("vendor_ramdisk")
.join("ramdisk.cpio")
.exists();
let is_kernelsu_patched = is_kernelsu_patched(&magiskboot, workdir)?;
let is_kernelsu_patched_vendor_init_boot =
is_kernelsu_patched_vendor_init_boot(&magiskboot, workdir)?;
let is_kernelsu_patched_vendor_ramdisk =
is_kernelsu_patched_vendor_ramdisk(&magiskboot, workdir)?;
ensure!(
is_kernelsu_patched,
is_kernelsu_patched || is_kernelsu_patched_vendor_init_boot || is_kernelsu_patched_vendor_ramdisk,
"boot image is not patched by KernelSU Next"
);
@@ -270,16 +371,44 @@ pub fn restore(
}
if new_boot.is_none() {
// remove kernelsu.ko
do_cpio_cmd(&magiskboot, workdir, "rm kernelsu.ko")?;
if no_ramdisk {
if !no_vendor_init_boot {
// vendor init_boot restore
do_vendor_init_boot_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_init_boot_cpio_cmd(&magiskboot, workdir, "exists init.real").is_ok();
if status {
do_vendor_init_boot_cpio_cmd(&magiskboot, workdir, "mv init.real init")?;
} else {
let vendor_init_boot = workdir.join("vendor_ramdisk").join("init_boot.cpio");
std::fs::remove_file(vendor_init_boot)?;
}
} else if !no_vendor_ramdisk {
// vendor ramdisk restore
do_vendor_ramdisk_cpio_cmd(&magiskboot, workdir, "rm kernelsu.ko")?;
let status =
do_vendor_ramdisk_cpio_cmd(&magiskboot, workdir, "exists init.real").is_ok();
if status {
do_vendor_ramdisk_cpio_cmd(&magiskboot, workdir, "mv init.real init")?;
} else {
let vendor_ramdisk = workdir.join("vendor_ramdisk").join("ramdisk.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 +430,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 +570,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)
@@ -457,28 +581,73 @@ fn do_patch(
ensure!(status.success(), "magiskboot unpack failed");
let no_ramdisk = !workdir.join("ramdisk.cpio").exists();
let no_vendor_init_boot = !workdir
.join("vendor_ramdisk")
.join("init_boot.cpio")
.exists();
let no_vendor_ramdisk = !workdir
.join("vendor_ramdisk")
.join("ramdisk.cpio")
.exists();
if no_ramdisk && no_vendor_init_boot && no_vendor_ramdisk {
bail!("No compatible ramdisk found.");
}
let is_magisk_patched = is_magisk_patched(&magiskboot, workdir)?;
let is_magisk_patched_vendor_init_boot =
is_magisk_patched_vendor_init_boot(&magiskboot, workdir)?;
let is_magisk_patched_vendor_ramdisk =
is_magisk_patched_vendor_ramdisk(&magiskboot, workdir)?;
ensure!(
no_ramdisk || !is_magisk_patched,
!is_magisk_patched || !is_magisk_patched_vendor_init_boot || !is_magisk_patched_vendor_ramdisk,
"Cannot work with Magisk patched image"
);
println!("- Adding KernelSU Next LKM");
let is_kernelsu_patched = is_kernelsu_patched(&magiskboot, workdir)?;
let is_kernelsu_patched_vendor_init_boot =
is_kernelsu_patched_vendor_init_boot(&magiskboot, workdir)?;
let is_kernelsu_patched_vendor_ramdisk =
is_kernelsu_patched_vendor_ramdisk(&magiskboot, workdir)?;
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 !is_kernelsu_patched || (no_ramdisk && !is_kernelsu_patched_vendor_init_boot) || (no_ramdisk && no_vendor_init_boot && !is_kernelsu_patched_vendor_ramdisk)
{
if no_ramdisk {
if !no_vendor_init_boot {
// vendor init_boot patching
let status = do_vendor_init_boot_cpio_cmd(&magiskboot, workdir, "exists init");
if status.is_ok() {
do_vendor_init_boot_cpio_cmd(&magiskboot, workdir, "mv init init.real")?;
}
} else if !no_vendor_ramdisk {
// vendor ramdisk patching
let status = do_vendor_ramdisk_cpio_cmd(&magiskboot, workdir, "exists init");
if status.is_ok() {
do_vendor_ramdisk_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 {
if !no_vendor_init_boot {
do_vendor_init_boot_cpio_cmd(&magiskboot, workdir, "add 0755 init init")?;
do_vendor_init_boot_cpio_cmd(&magiskboot, workdir, "add 0755 kernelsu.ko kernelsu.ko")?;
} else if !no_vendor_ramdisk {
do_vendor_ramdisk_cpio_cmd(&magiskboot, workdir, "add 0750 init init")?;
do_vendor_ramdisk_cpio_cmd(&magiskboot, workdir, "add 0750 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 +827,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

@@ -119,50 +119,50 @@ int main(int argc, char *argv[]) {
ptr_buf += str_len;
}
if (enabled_features & (1 << 5)) {
str_len = strlen("CONFIG_KSU_SUSFS_SUS_OVERLAYFS\n");
strncpy(ptr_buf, "CONFIG_KSU_SUSFS_SUS_OVERLAYFS\n", str_len);
ptr_buf += str_len;
}
if (enabled_features & (1 << 6)) {
str_len = strlen("CONFIG_KSU_SUSFS_TRY_UMOUNT\n");
strncpy(ptr_buf, "CONFIG_KSU_SUSFS_TRY_UMOUNT\n", str_len);
ptr_buf += str_len;
}
if (enabled_features & (1 << 7)) {
if (enabled_features & (1 << 6)) {
str_len = strlen("CONFIG_KSU_SUSFS_AUTO_ADD_TRY_UMOUNT_FOR_BIND_MOUNT\n");
strncpy(ptr_buf, "CONFIG_KSU_SUSFS_AUTO_ADD_TRY_UMOUNT_FOR_BIND_MOUNT\n", str_len);
ptr_buf += str_len;
}
if (enabled_features & (1 << 8)) {
if (enabled_features & (1 << 7)) {
str_len = strlen("CONFIG_KSU_SUSFS_SPOOF_UNAME\n");
strncpy(ptr_buf, "CONFIG_KSU_SUSFS_SPOOF_UNAME\n", str_len);
ptr_buf += str_len;
}
if (enabled_features & (1 << 9)) {
if (enabled_features & (1 << 8)) {
str_len = strlen("CONFIG_KSU_SUSFS_ENABLE_LOG\n");
strncpy(ptr_buf, "CONFIG_KSU_SUSFS_ENABLE_LOG\n", str_len);
ptr_buf += str_len;
}
if (enabled_features & (1 << 10)) {
if (enabled_features & (1 << 9)) {
str_len = strlen("CONFIG_KSU_SUSFS_HIDE_KSU_SUSFS_SYMBOLS\n");
strncpy(ptr_buf, "CONFIG_KSU_SUSFS_HIDE_KSU_SUSFS_SYMBOLS\n", str_len);
ptr_buf += str_len;
}
if (enabled_features & (1 << 11)) {
if (enabled_features & (1 << 10)) {
str_len = strlen("CONFIG_KSU_SUSFS_SPOOF_BOOTCONFIG\n");
strncpy(ptr_buf, "CONFIG_KSU_SUSFS_SPOOF_BOOTCONFIG\n", str_len);
ptr_buf += str_len;
}
if (enabled_features & (1 << 12)) {
if (enabled_features & (1 << 11)) {
str_len = strlen("CONFIG_KSU_SUSFS_OPEN_REDIRECT\n");
strncpy(ptr_buf, "CONFIG_KSU_SUSFS_OPEN_REDIRECT\n", str_len);
ptr_buf += str_len;
}
if (enabled_features & (1 << 13)) {
if (enabled_features & (1 << 12)) {
str_len = strlen("CONFIG_KSU_SUSFS_SUS_SU\n");
strncpy(ptr_buf, "CONFIG_KSU_SUSFS_SUS_SU\n", str_len);
ptr_buf += str_len;
}
if (enabled_features & (1 << 13)) {
str_len = strlen("CONFIG_KSU_SUSFS_HAS_MAGIC_MOUNT\n");
strncpy(ptr_buf, "CONFIG_KSU_SUSFS_HAS_MAGIC_MOUNT\n", str_len);
ptr_buf += str_len;
}
printf("%s", enabled_features_buf);
free(enabled_features_buf);
} else {