You've already forked KernelSU-Next
mirror of
https://github.com/KernelSU-Next/KernelSU-Next.git
synced 2025-08-27 23:46:34 +00:00
Compare commits
51 Commits
66d42de599
...
d1aad01df3
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d1aad01df3 | ||
|
|
9733b92d30 | ||
|
|
aaf776f421 | ||
|
|
5e33aee99f | ||
|
|
4e3f06d405 | ||
|
|
98b9863041 | ||
|
|
29ae76d1fb | ||
|
|
2c3841558e | ||
|
|
c8b357e31b | ||
|
|
3ad02ff50b | ||
|
|
6a54b30a9d | ||
|
|
b0cb3bb4c2 | ||
|
|
7f957be99b | ||
|
|
a54c319d55 | ||
|
|
2c71531533 | ||
|
|
5c61a70e5a | ||
|
|
fdf1d61735 | ||
|
|
93dc61e113 | ||
|
|
d80a3ebcda | ||
|
|
4270fd8b1e | ||
|
|
764dbc3782 | ||
|
|
080ab9a952 | ||
|
|
a9cab5ccfd | ||
|
|
3d44602537 | ||
|
|
88eb2a2723 | ||
|
|
e272e557b0 | ||
|
|
2a4794e422 | ||
|
|
818bdbead6 | ||
|
|
76249fa67d | ||
|
|
ed50b57b57 | ||
|
|
f05f776a08 | ||
|
|
c9b79c3016 | ||
|
|
3ff10d6622 | ||
|
|
b95d2b69b6 | ||
|
|
2cd8453877 | ||
|
|
b7300b0525 | ||
|
|
3a278d560f | ||
|
|
03fa2eddb2 | ||
|
|
b74e953ad2 | ||
|
|
39717b0a3f | ||
|
|
7f0eccd3d5 | ||
|
|
78eb3b0b22 | ||
|
|
39f20bf573 | ||
|
|
092eb1b23d | ||
|
|
30e2ed5db5 | ||
|
|
dc7ae2db5f | ||
|
|
b3b7ef1cb3 | ||
|
|
4de4d1e091 | ||
|
|
0beea57ab7 | ||
|
|
49aee1ff4c | ||
|
|
f7a3699fe3 |
104
docs/README.md
104
docs/README.md
@@ -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>
|
||||
|
||||
[](https://github.com/KernelSU-Next/KernelSU-Next/releases/latest)
|
||||
[](https://nightly.link/KernelSU-Next/KernelSU-Next/workflows/build-manager-ci/next/Manager)
|
||||
[](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html)
|
||||
[](/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 you’d 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
|
||||
|
||||
@@ -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>
|
||||
|
||||
[](https://github.com/KernelSU-Next/KernelSU-Next/releases/latest)
|
||||
[](https://nightly.link/KernelSU-Next/KernelSU-Next/workflows/build-manager-ci/next/Manager)
|
||||
[](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html)
|
||||
[](/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.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** 的大多数安卓内核
|
||||
|
||||
## 用法
|
||||
| 内核版本 | 支持情况 |
|
||||
|----------------|---------------|
|
||||
| 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!
|
||||
|
||||
@@ -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>
|
||||
|
||||
[](https://github.com/KernelSU-Next/KernelSU-Next/releases/latest)
|
||||
[](https://nightly.link/KernelSU-Next/KernelSU-Next/workflows/build-manager-ci/next/Manager)
|
||||
[](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html)
|
||||
[](/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
|
||||
|
||||
@@ -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>
|
||||
|
||||
[](https://github.com/KernelSU-Next/KernelSU-Next/releases/latest)
|
||||
[](https://nightly.link/KernelSU-Next/KernelSU-Next/workflows/build-manager-ci/next/Manager)
|
||||
[](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html)
|
||||
[](/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!
|
||||
|
||||
@@ -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://github.com/KernelSU-Next/KernelSU-Next/releases/latest)
|
||||
[](https://nightly.link/KernelSU-Next/KernelSU-Next/workflows/build-manager-ci/next/Manager)
|
||||
[](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html)
|
||||
[](/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
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
10
manager/app/proguard-rules.pro
vendored
10
manager/app/proguard-rules.pro
vendored
@@ -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
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
77
manager/app/src/main/java/com/rifsxd/ksunext/KsuService.java
Normal file
77
manager/app/src/main/java/com/rifsxd/ksunext/KsuService.java
Normal 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<>();
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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)) },
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
)
|
||||
}
|
||||
},
|
||||
|
||||
@@ -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) },
|
||||
)
|
||||
|
||||
@@ -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) }
|
||||
|
||||
@@ -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) }
|
||||
|
||||
@@ -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) }
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 = {
|
||||
|
||||
@@ -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)) {
|
||||
|
||||
@@ -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) }
|
||||
|
||||
@@ -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, {})
|
||||
}
|
||||
|
||||
@@ -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
|
||||
)
|
||||
|
||||
@@ -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,
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 -> {
|
||||
|
||||
@@ -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("\"", "\\\"")
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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}")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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")
|
||||
|
||||
@@ -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()
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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())
|
||||
@@ -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
|
||||
|
||||
@@ -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 & 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 & 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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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 (A → Z)</string>
|
||||
<string name="module_sort_z_to_a">Sắp xếp (Z → A)</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">Hiện 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">Hiển 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">Hiện thị ứng dụng hệ thống</string>
|
||||
<string name="show_system_apps">Hiển 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 & 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 và 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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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" }
|
||||
@@ -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}")
|
||||
};
|
||||
|
||||
@@ -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}")
|
||||
};
|
||||
|
||||
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user