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
106 Commits
kpm
...
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 | ||
|
|
66d42de599 | ||
|
|
d562594ae1 | ||
|
|
02afc6710c | ||
|
|
9d9f9ed5d5 | ||
|
|
b3b6320946 | ||
|
|
d43b8320ad | ||
|
|
a2260ae330 | ||
|
|
aa221acad1 | ||
|
|
c41d35db62 | ||
|
|
e5f0c733b8 | ||
|
|
70b52f85e9 | ||
|
|
303b5192ec | ||
|
|
443d3a6abe | ||
|
|
a2a7383343 | ||
|
|
ed64f933f5 | ||
|
|
4c7ad8eac4 | ||
|
|
6a922febcd | ||
|
|
71eba5df8b | ||
|
|
f1ef0afc20 | ||
|
|
844c9f94e9 | ||
|
|
07a4d3457c | ||
|
|
8c9728df95 | ||
|
|
14ec1194e4 | ||
|
|
a37f398cc7 | ||
|
|
80bd797737 | ||
|
|
600d9ce5d2 | ||
|
|
f15d9b18e9 | ||
|
|
aa19e8c609 | ||
|
|
2b2320000c | ||
|
|
06135cc827 | ||
|
|
fc58bdf0e2 | ||
|
|
9b5e60912d | ||
|
|
c108a8ed32 | ||
|
|
adce657583 | ||
|
|
d6601e1e54 | ||
|
|
0d4efa649f | ||
|
|
85f4e6ac27 | ||
|
|
c91f9c18ec | ||
|
|
bf35f73430 | ||
|
|
ad290a51a0 | ||
|
|
d218346613 | ||
|
|
502e5599fe | ||
|
|
11fb52b929 | ||
|
|
057388ccef | ||
|
|
67759ad723 | ||
|
|
f231cbdba7 | ||
|
|
7abc9bc821 | ||
|
|
e7697d86fe | ||
|
|
68394fddd5 | ||
|
|
90d34bf511 | ||
|
|
236fbc7615 | ||
|
|
6871cbdba7 | ||
|
|
38a9949211 | ||
|
|
9fba0faa43 | ||
|
|
b1250b002e |
2
.github/workflows/avd-kernel.yml
vendored
2
.github/workflows/avd-kernel.yml
vendored
@@ -37,7 +37,7 @@ on:
|
||||
jobs:
|
||||
build:
|
||||
name: Build ${{ inputs.version_name }}
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Maximize build space
|
||||
uses: easimon/maximize-build-space@master
|
||||
|
||||
26
.github/workflows/build-debug-kernel.yml
vendored
26
.github/workflows/build-debug-kernel.yml
vendored
@@ -7,9 +7,9 @@ jobs:
|
||||
uses: ./.github/workflows/gki-kernel.yml
|
||||
with:
|
||||
version: android12-5.10
|
||||
version_name: android12-5.10.233
|
||||
tag: android12-5.10-2025-02
|
||||
os_patch_level: 2025-02
|
||||
version_name: android12-5.10.236
|
||||
tag: android12-5.10-2025-05
|
||||
os_patch_level: 2025-05
|
||||
patch_path: "5.10"
|
||||
debug: true
|
||||
build-debug-kernel-a13:
|
||||
@@ -17,11 +17,11 @@ jobs:
|
||||
matrix:
|
||||
include:
|
||||
- version: "5.10"
|
||||
sub_level: 234
|
||||
os_patch_level: 2025-03
|
||||
sub_level: 236
|
||||
os_patch_level: 2025-05
|
||||
- version: "5.15"
|
||||
sub_level: 178
|
||||
os_patch_level: 2025-03
|
||||
sub_level: 180
|
||||
os_patch_level: 2025-05
|
||||
uses: ./.github/workflows/gki-kernel.yml
|
||||
with:
|
||||
version: android13-${{ matrix.version }}
|
||||
@@ -34,11 +34,11 @@ jobs:
|
||||
matrix:
|
||||
include:
|
||||
- version: "5.15"
|
||||
sub_level: 178
|
||||
os_patch_level: 2025-03
|
||||
sub_level: 180
|
||||
os_patch_level: 2025-05
|
||||
- version: "6.1"
|
||||
sub_level: 129
|
||||
os_patch_level: 2025-04
|
||||
sub_level: 138
|
||||
os_patch_level: 2025-06
|
||||
uses: ./.github/workflows/gki-kernel.yml
|
||||
with:
|
||||
version: android14-${{ matrix.version }}
|
||||
@@ -51,8 +51,8 @@ jobs:
|
||||
matrix:
|
||||
include:
|
||||
- version: "6.6"
|
||||
sub_level: 82
|
||||
os_patch_level: 2025-04
|
||||
sub_level: 89
|
||||
os_patch_level: 2025-06
|
||||
uses: ./.github/workflows/gki-kernel.yml
|
||||
with:
|
||||
version: android15-${{ matrix.version }}
|
||||
|
||||
10
.github/workflows/build-kernel-a12.yml
vendored
10
.github/workflows/build-kernel-a12.yml
vendored
@@ -21,14 +21,14 @@ jobs:
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- sub_level: 209
|
||||
os_patch_level: 2024-05
|
||||
- sub_level: 218
|
||||
os_patch_level: 2024-08
|
||||
- sub_level: 226
|
||||
os_patch_level: 2024-11
|
||||
- sub_level: 233
|
||||
os_patch_level: 2025-02
|
||||
- sub_level: 236
|
||||
os_patch_level: 2025-05
|
||||
uses: ./.github/workflows/gki-kernel.yml
|
||||
secrets: inherit
|
||||
with:
|
||||
@@ -114,7 +114,7 @@ jobs:
|
||||
uses: ./.github/workflows/gki-kernel.yml
|
||||
with:
|
||||
version: android12-5.10
|
||||
version_name: android12-5.10.223
|
||||
tag: android12-5.10-2024-11
|
||||
os_patch_level: 2024-11
|
||||
version_name: android12-5.10.236
|
||||
tag: android12-5.10-2025-05
|
||||
os_patch_level: 2025-05
|
||||
patch_path: "5.10"
|
||||
20
.github/workflows/build-kernel-a13.yml
vendored
20
.github/workflows/build-kernel-a13.yml
vendored
@@ -21,9 +21,6 @@ jobs:
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- version: "5.10"
|
||||
sub_level: 209
|
||||
os_patch_level: 2024-05
|
||||
- version: "5.10"
|
||||
sub_level: 210
|
||||
os_patch_level: 2024-06
|
||||
@@ -42,9 +39,9 @@ jobs:
|
||||
- version: "5.10"
|
||||
sub_level: 234
|
||||
os_patch_level: 2025-03
|
||||
- version: "5.15"
|
||||
sub_level: 148
|
||||
os_patch_level: 2024-05
|
||||
- version: "5.10"
|
||||
sub_level: 236
|
||||
os_patch_level: 2025-05
|
||||
- version: "5.15"
|
||||
sub_level: 149
|
||||
os_patch_level: 2024-07
|
||||
@@ -63,6 +60,9 @@ jobs:
|
||||
- version: "5.15"
|
||||
sub_level: 178
|
||||
os_patch_level: 2025-03
|
||||
- version: "5.15"
|
||||
sub_level: 180
|
||||
os_patch_level: 2025-05
|
||||
uses: ./.github/workflows/gki-kernel.yml
|
||||
secrets: inherit
|
||||
with:
|
||||
@@ -149,11 +149,11 @@ jobs:
|
||||
matrix:
|
||||
include:
|
||||
- version: "5.10"
|
||||
sub_level: 234
|
||||
os_patch_level: 2025-03
|
||||
sub_level: 236
|
||||
os_patch_level: 2025-05
|
||||
- version: "5.15"
|
||||
sub_level: 178
|
||||
os_patch_level: 2025-03
|
||||
sub_level: 180
|
||||
os_patch_level: 2025-05
|
||||
uses: ./.github/workflows/gki-kernel.yml
|
||||
with:
|
||||
version: android13-${{ matrix.version }}
|
||||
|
||||
23
.github/workflows/build-kernel-a14.yml
vendored
23
.github/workflows/build-kernel-a14.yml
vendored
@@ -21,9 +21,6 @@ jobs:
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- version: "5.15"
|
||||
sub_level: 148
|
||||
os_patch_level: 2024-05
|
||||
- version: "5.15"
|
||||
sub_level: 149
|
||||
os_patch_level: 2024-06
|
||||
@@ -45,9 +42,9 @@ jobs:
|
||||
- version: "5.15"
|
||||
sub_level: 178
|
||||
os_patch_level: 2025-03
|
||||
- version: "6.1"
|
||||
sub_level: 75
|
||||
os_patch_level: 2024-05
|
||||
- version: "5.15"
|
||||
sub_level: 180
|
||||
os_patch_level: 2025-05
|
||||
- version: "6.1"
|
||||
sub_level: 78
|
||||
os_patch_level: 2024-06
|
||||
@@ -81,6 +78,12 @@ jobs:
|
||||
- version: "6.1"
|
||||
sub_level: 129
|
||||
os_patch_level: 2025-04
|
||||
- version: "6.1"
|
||||
sub_level: 134
|
||||
os_patch_level: 2025-05
|
||||
- version: "6.1"
|
||||
sub_level: 138
|
||||
os_patch_level: 2025-06
|
||||
uses: ./.github/workflows/gki-kernel.yml
|
||||
secrets: inherit
|
||||
with:
|
||||
@@ -167,11 +170,11 @@ jobs:
|
||||
matrix:
|
||||
include:
|
||||
- version: "5.15"
|
||||
sub_level: 178
|
||||
os_patch_level: 2025-03
|
||||
sub_level: 180
|
||||
os_patch_level: 2025-05
|
||||
- version: "6.1"
|
||||
sub_level: 129
|
||||
os_patch_level: 2025-04
|
||||
sub_level: 138
|
||||
os_patch_level: 2025-06
|
||||
uses: ./.github/workflows/gki-kernel.yml
|
||||
with:
|
||||
version: android14-${{ matrix.version }}
|
||||
|
||||
10
.github/workflows/build-kernel-a15.yml
vendored
10
.github/workflows/build-kernel-a15.yml
vendored
@@ -48,6 +48,12 @@ jobs:
|
||||
- version: "6.6"
|
||||
sub_level: 82
|
||||
os_patch_level: 2025-04
|
||||
- version: "6.6"
|
||||
sub_level: 87
|
||||
os_patch_level: 2025-05
|
||||
- version: "6.6"
|
||||
sub_level: 89
|
||||
os_patch_level: 2025-06
|
||||
uses: ./.github/workflows/gki-kernel.yml
|
||||
secrets: inherit
|
||||
with:
|
||||
@@ -134,8 +140,8 @@ jobs:
|
||||
matrix:
|
||||
include:
|
||||
- version: "6.6"
|
||||
sub_level: 82
|
||||
os_patch_level: 2025-04
|
||||
sub_level: 89
|
||||
os_patch_level: 2025-06
|
||||
uses: ./.github/workflows/gki-kernel.yml
|
||||
with:
|
||||
version: android15-${{ matrix.version }}
|
||||
|
||||
137
.github/workflows/build-kernel-arcvm.yml
vendored
Normal file
137
.github/workflows/build-kernel-arcvm.yml
vendored
Normal file
@@ -0,0 +1,137 @@
|
||||
name: Build Kernel - ChromeOS ARCVM
|
||||
on:
|
||||
push:
|
||||
branches: ["next"]
|
||||
paths:
|
||||
- ".github/workflows/build-kernel-arcvm.yml"
|
||||
- "kernel/**"
|
||||
pull_request:
|
||||
branches: ["next"]
|
||||
paths:
|
||||
- ".github/workflows/build-kernel-arcvm.yml"
|
||||
- "kernel/**"
|
||||
workflow_call:
|
||||
workflow_dispatch:
|
||||
|
||||
env:
|
||||
git_tag: chromeos-5.10-arcvm
|
||||
|
||||
jobs:
|
||||
build:
|
||||
if: github.event_name != 'pull_request' || (github.event_name == 'pull_request' && !github.event.pull_request.draft)
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- arch: x86_64
|
||||
kernel_image_name: bzImage
|
||||
build_config: build.config.gki.x86_64
|
||||
defconfig: x86_64_arcvm_defconfig
|
||||
- arch: arm64
|
||||
kernel_image_name: Image
|
||||
build_config: build.config.gki.aarch64
|
||||
defconfig: arm64_arcvm_defconfig
|
||||
|
||||
name: Build ChromeOS ARCVM kernel
|
||||
runs-on: ubuntu-22.04
|
||||
env:
|
||||
LTO: thin
|
||||
ROOT_DIR: /
|
||||
KERNEL_DIR: ${{ github.workspace }}/kernel
|
||||
|
||||
steps:
|
||||
- name: Install Build Tools
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y --no-install-recommends bc \
|
||||
bison build-essential ca-certificates flex git gnupg \
|
||||
libelf-dev libssl-dev lsb-release software-properties-common wget \
|
||||
libncurses-dev binutils-aarch64-linux-gnu gcc-aarch64-linux-gnu nuget gzip \
|
||||
rsync python3 device-tree-compiler
|
||||
|
||||
sudo ln -s --force python3 /usr/bin/python
|
||||
|
||||
export LLVM_VERSION=14
|
||||
wget https://apt.llvm.org/llvm.sh
|
||||
chmod +x llvm.sh
|
||||
sudo ./llvm.sh $LLVM_VERSION
|
||||
rm ./llvm.sh
|
||||
sudo ln -s --force /usr/bin/clang-$LLVM_VERSION /usr/bin/clang
|
||||
sudo ln -s --force /usr/bin/ld.lld-$LLVM_VERSION /usr/bin/ld.lld
|
||||
sudo ln -s --force /usr/bin/llvm-objdump-$LLVM_VERSION /usr/bin/llvm-objdump
|
||||
sudo ln -s --force /usr/bin/llvm-ar-$LLVM_VERSION /usr/bin/llvm-ar
|
||||
sudo ln -s --force /usr/bin/llvm-nm-$LLVM_VERSION /usr/bin/llvm-nm
|
||||
sudo ln -s --force /usr/bin/llvm-strip-$LLVM_VERSION /usr/bin/llvm-strip
|
||||
sudo ln -s --force /usr/bin/llvm-objcopy-$LLVM_VERSION /usr/bin/llvm-objcopy
|
||||
sudo ln -s --force /usr/bin/llvm-readelf-$LLVM_VERSION /usr/bin/llvm-readelf
|
||||
sudo ln -s --force /usr/bin/clang++-$LLVM_VERSION /usr/bin/clang++
|
||||
|
||||
- name: Checkout KernelSU-Next
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
path: KernelSU-Next
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Setup kernel source
|
||||
run: git clone https://chromium.googlesource.com/chromiumos/third_party/kernel.git -b ${{ env.git_tag }} --depth=1
|
||||
|
||||
- name: Extract version from Makefile
|
||||
working-directory: kernel
|
||||
run: |
|
||||
VERSION=$(grep -E '^VERSION = ' Makefile | awk '{print $3}')
|
||||
PATCHLEVEL=$(grep -E '^PATCHLEVEL = ' Makefile | awk '{print $3}')
|
||||
SUBLEVEL=$(grep -E '^SUBLEVEL = ' Makefile | awk '{print $3}')
|
||||
echo "ChromeOS ARCVM Linux kernel version: $VERSION.$PATCHLEVEL.$SUBLEVEL"
|
||||
echo "version=$VERSION.$PATCHLEVEL.$SUBLEVEL" >> $GITHUB_ENV
|
||||
|
||||
- name: Setup KernelSU-Next
|
||||
working-directory: kernel
|
||||
run: |
|
||||
echo "[+] KernelSU-Next setup"
|
||||
KERNEL_ROOT=$GITHUB_WORKSPACE/kernel
|
||||
echo "[+] KERNEL_ROOT: $KERNEL_ROOT"
|
||||
echo "[+] Copy KernelSU-Next driver to $KERNEL_ROOT/drivers"
|
||||
ln -sf $GITHUB_WORKSPACE/KernelSU-Next/kernel $KERNEL_ROOT/drivers/kernelsu-next
|
||||
|
||||
echo "[+] Add KernelSU-Next driver to Makefile"
|
||||
DRIVER_MAKEFILE=$KERNEL_ROOT/drivers/Makefile
|
||||
DRIVER_KCONFIG=$KERNEL_ROOT/drivers/Kconfig
|
||||
grep -q "kernelsu-next" "$DRIVER_MAKEFILE" || printf "\nobj-\$(CONFIG_KSU) += kernelsu-next/\n" >> "$DRIVER_MAKEFILE"
|
||||
grep -q "kernelsu-next" "$DRIVER_KCONFIG" || sed -i "/endmenu/i\\source \"drivers/kernelsu-next/Kconfig\"" "$DRIVER_KCONFIG"
|
||||
|
||||
echo "[+] Apply KernelSU patches"
|
||||
cd $KERNEL_ROOT && git apply $GITHUB_WORKSPACE/KernelSU-Next/.github/patches/5.10/*.patch || echo "[-] No patch found"
|
||||
|
||||
echo "[+] Patch script/setlocalversion"
|
||||
sed -i 's/-dirty//g' $KERNEL_ROOT/scripts/setlocalversion
|
||||
|
||||
echo "[+] KernelSU-Next setup done."
|
||||
cd $GITHUB_WORKSPACE/KernelSU-Next
|
||||
KSU_VERSION=$(($(git rev-list --count HEAD) + 10200))
|
||||
echo "KernelSU-Next version: $KSU_VERSION"
|
||||
echo "kernelsu-next_version=$KSU_VERSION" >> $GITHUB_ENV
|
||||
|
||||
- name: Build Kernel
|
||||
working-directory: kernel
|
||||
env:
|
||||
KERNEL_IMAGE_NAME: ${{ matrix.kernel_image_name }}
|
||||
ARCH: ${{ matrix.arch }}
|
||||
run: |
|
||||
set -a && . ${{ matrix.build_config }}; set +a
|
||||
export DEFCONFIG=${{ matrix.defconfig }}
|
||||
if [ ! -z ${{ vars.EXPECTED_SIZE }} ] && [ ! -z ${{ vars.EXPECTED_HASH }} ]; then
|
||||
export KSU_EXPECTED_SIZE=${{ vars.EXPECTED_SIZE }}
|
||||
export KSU_EXPECTED_HASH=${{ vars.EXPECTED_HASH }}
|
||||
fi
|
||||
|
||||
make LLVM=1 LLVM_IAS=1 DEPMOD=depmod DTC=dtc O=${PWD} mrproper
|
||||
make LLVM=1 LLVM_IAS=1 DEPMOD=depmod DTC=dtc O=${PWD} ${DEFCONFIG} < /dev/null
|
||||
scripts/config --file .config -e LTO_CLANG -d LTO_NONE -e LTO_CLANG_THIN -d LTO_CLANG_FULL -e THINLTO
|
||||
make LLVM=1 LLVM_IAS=1 DEPMOD=depmod DTC=dtc O=${PWD} -j$(nproc) ${KERNEL_IMAGE_NAME} modules prepare-objtool
|
||||
ls -l -h ${PWD}/arch/${ARCH}/boot
|
||||
echo "file_path=${PWD}/arch/${ARCH}/boot/${KERNEL_IMAGE_NAME}" >> $GITHUB_ENV
|
||||
|
||||
- name: Upload kernel-ARCVM-${{ matrix.arch }}-${{ env.version }}
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: kernel-ARCVM-${{ matrix.arch }}-${{ env.version }}
|
||||
path: "${{ env.file_path }}"
|
||||
39
.github/workflows/build-kernel-avd.yml
vendored
Normal file
39
.github/workflows/build-kernel-avd.yml
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
name: Build Kernel - AVD
|
||||
on:
|
||||
push:
|
||||
branches: ["next"]
|
||||
paths:
|
||||
- ".github/workflows/build-kernel-avd.yml"
|
||||
- ".github/workflows/avd-kernel.yml"
|
||||
- "kernel/**"
|
||||
pull_request:
|
||||
branches: ["next"]
|
||||
paths:
|
||||
- ".github/workflows/build-kernel-avd.yml"
|
||||
- ".github/workflows/avd-kernel.yml"
|
||||
- "kernel/**"
|
||||
workflow_call:
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
build-kernel:
|
||||
if: github.event_name != 'pull_request' && github.ref != 'refs/heads/checkci'
|
||||
uses: ./.github/workflows/avd-kernel.yml
|
||||
secrets: inherit
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- version: "android-14-avd_x86_64"
|
||||
manifest: "android-14-avd_x86_64.xml"
|
||||
arch: "x86_64"
|
||||
- version: "android-15-avd_aarch64"
|
||||
manifest: "android-15-avd_aarch64.xml"
|
||||
arch: "aarch64"
|
||||
- version: "android-15-avd_x86_64"
|
||||
manifest: "android-15-avd_x86_64.xml"
|
||||
arch: "x86_64"
|
||||
with:
|
||||
version_name: ${{ matrix.version }}
|
||||
manifest_name: ${{ matrix.manifest }}
|
||||
arch: ${{ matrix.arch }}
|
||||
debug: true
|
||||
24
.github/workflows/build-lkm.yml
vendored
24
.github/workflows/build-lkm.yml
vendored
@@ -15,23 +15,23 @@ jobs:
|
||||
matrix:
|
||||
include:
|
||||
- version: "android12-5.10"
|
||||
sub_level: 233
|
||||
os_patch_level: 2025-02
|
||||
sub_level: 236
|
||||
os_patch_level: 2025-05
|
||||
- version: "android13-5.10"
|
||||
sub_level: 234
|
||||
os_patch_level: 2025-03
|
||||
sub_level: 236
|
||||
os_patch_level: 2025-05
|
||||
- version: "android13-5.15"
|
||||
sub_level: 178
|
||||
os_patch_level: 2025-03
|
||||
sub_level: 180
|
||||
os_patch_level: 2025-05
|
||||
- version: "android14-5.15"
|
||||
sub_level: 178
|
||||
os_patch_level: 2025-03
|
||||
sub_level: 180
|
||||
os_patch_level: 2025-05
|
||||
- version: "android14-6.1"
|
||||
sub_level: 129
|
||||
os_patch_level: 2025-04
|
||||
sub_level: 138
|
||||
os_patch_level: 2025-06
|
||||
- version: "android15-6.6"
|
||||
sub_level: 82
|
||||
os_patch_level: 2025-04
|
||||
sub_level: 89
|
||||
os_patch_level: 2025-06
|
||||
uses: ./.github/workflows/gki-kernel.yml
|
||||
with:
|
||||
version: ${{ matrix.version }}
|
||||
|
||||
4
.github/workflows/clippy.yml
vendored
4
.github/workflows/clippy.yml
vendored
@@ -3,14 +3,14 @@ name: Clippy check
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- x86
|
||||
- next
|
||||
paths:
|
||||
- '.github/workflows/clippy.yml'
|
||||
- 'userspace/ksud_magic/**'
|
||||
- 'userspace/ksud_overlayfs/**'
|
||||
pull_request:
|
||||
branches:
|
||||
- x86
|
||||
- next
|
||||
paths:
|
||||
- '.github/workflows/clippy.yml'
|
||||
- 'userspace/ksud_magic/**'
|
||||
|
||||
28
.github/workflows/release.yml
vendored
28
.github/workflows/release.yml
vendored
@@ -21,6 +21,12 @@ jobs:
|
||||
build-a15-kernel:
|
||||
uses: ./.github/workflows/build-kernel-a15.yml
|
||||
secrets: inherit
|
||||
build-wsa-kernel:
|
||||
uses: ./.github/workflows/build-kernel-wsa.yml
|
||||
secrets: inherit
|
||||
build-arcvm-kernel:
|
||||
uses: ./.github/workflows/build-kernel-arcvm.yml
|
||||
secrets: inherit
|
||||
release:
|
||||
needs:
|
||||
- build-manager
|
||||
@@ -29,6 +35,7 @@ jobs:
|
||||
- build-a14-kernel
|
||||
- build-a15-kernel
|
||||
- build-wsa-kernel
|
||||
- build-arcvm-kernel
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Download artifacts
|
||||
@@ -42,6 +49,24 @@ jobs:
|
||||
fi
|
||||
done
|
||||
|
||||
- name: Zip WSA kernel
|
||||
run: |
|
||||
for dir in kernel-WSA-*; do
|
||||
if [ -d "$dir" ]; then
|
||||
echo "------ Zip $dir ----------"
|
||||
(cd $dir && zip -r9 "$dir".zip ./* -x .git .gitignore ./*.zip && mv *.zip ..)
|
||||
fi
|
||||
done
|
||||
|
||||
- name: Zip ChromeOS ARCVM kernel
|
||||
run: |
|
||||
for dir in kernel-ARCVM-*; do
|
||||
if [ -d "$dir" ]; then
|
||||
echo "------ Zip $dir ----------"
|
||||
(cd $dir && zip -r9 "$dir".zip ./* -x .git .gitignore ./*.zip && mv *.zip ..)
|
||||
fi
|
||||
done
|
||||
|
||||
- name: Display structure of downloaded files
|
||||
run: ls -R
|
||||
|
||||
@@ -54,6 +79,7 @@ jobs:
|
||||
AnyKernel3-*.zip
|
||||
boot-images-*/Image-*/*.img.gz
|
||||
kernel-WSA*.zip
|
||||
kernel-ARCVM*.zip
|
||||
ksud_magic*.zip
|
||||
ksud_overlayfs*.zip
|
||||
susfsd*.zip
|
||||
susfsd*.zip
|
||||
24
.github/workflows/wsa-kernel.yml
vendored
24
.github/workflows/wsa-kernel.yml
vendored
@@ -43,10 +43,10 @@ jobs:
|
||||
ubuntu-version: "16.04"
|
||||
cached: ${{ steps.cache-llvm.outputs.cache-hit }}
|
||||
|
||||
- name: Checkout KernelSU
|
||||
- name: Checkout KernelSU-Next
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
path: KernelSU
|
||||
path: KernelSU-Next
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Setup kernel source
|
||||
@@ -63,23 +63,23 @@ jobs:
|
||||
save: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }}
|
||||
max-size: 2G
|
||||
|
||||
- name: Setup KernelSU
|
||||
- name: Setup KernelSU-Next
|
||||
working-directory: WSA-Linux-Kernel
|
||||
run: |
|
||||
echo "[+] KernelSU setup"
|
||||
echo "[+] KernelSU-Next setup"
|
||||
KERNEL_ROOT=$GITHUB_WORKSPACE/WSA-Linux-Kernel
|
||||
echo "[+] KERNEL_ROOT: $KERNEL_ROOT"
|
||||
echo "[+] Copy KernelSU driver to $KERNEL_ROOT/drivers"
|
||||
ln -sf $GITHUB_WORKSPACE/KernelSU/kernel $KERNEL_ROOT/drivers/kernelsu
|
||||
echo "[+] Add KernelSU driver to Makefile"
|
||||
echo "[+] Copy KernelSU-Next driver to $KERNEL_ROOT/drivers"
|
||||
ln -sf $GITHUB_WORKSPACE/KernelSU-Next/kernel $KERNEL_ROOT/drivers/kernelsu
|
||||
echo "[+] Add KernelSU-Next driver to Makefile"
|
||||
DRIVER_MAKEFILE=$KERNEL_ROOT/drivers/Makefile
|
||||
DRIVER_KCONFIG=$KERNEL_ROOT/drivers/Kconfig
|
||||
grep -q "kernelsu" "$DRIVER_MAKEFILE" || printf "\nobj-\$(CONFIG_KSU) += kernelsu/\n" >> "$DRIVER_MAKEFILE"
|
||||
grep -q "kernelsu" "$DRIVER_KCONFIG" || sed -i "/endmenu/i\\source \"drivers/kernelsu/Kconfig\"" "$DRIVER_KCONFIG"
|
||||
echo "[+] Apply KernelSU patches"
|
||||
cd $KERNEL_ROOT && git apply $GITHUB_WORKSPACE/KernelSU/.github/patches/5.15/*.patch || echo "[-] No patch found"
|
||||
echo "[+] KernelSU setup done."
|
||||
cd $GITHUB_WORKSPACE/KernelSU
|
||||
echo "[+] Apply KernelSU-Next patches"
|
||||
cd $KERNEL_ROOT && git apply $GITHUB_WORKSPACE/KernelSU-Next/.github/patches/5.15/*.patch || echo "[-] No patch found"
|
||||
echo "[+] KernelSU-Next setup done."
|
||||
cd $GITHUB_WORKSPACE/KernelSU-Next
|
||||
VERSION=$(($(git rev-list --count HEAD) + 10200))
|
||||
echo "VERSION: $VERSION"
|
||||
echo "kernelsu_version=$VERSION" >> $GITHUB_ENV
|
||||
@@ -103,4 +103,4 @@ jobs:
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: kernel-WSA-${{ inputs.arch }}-${{ inputs.version }}
|
||||
path: "${{ env.file_path }}"
|
||||
path: "${{ env.file_path }}"
|
||||
|
||||
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` architecture is supported.
|
||||
## ✅ Compatibility
|
||||
|
||||
## Usage
|
||||
KernelSU Next supports Android kernels from **4.4 up to 6.6**:
|
||||
|
||||
- [Installation instruction](https://ksunext.org/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 a arquitetura `arm64-v8a`, `armeabi-v7a` & `x86_64` é compatível.
|
||||
## ✅ 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
|
||||
|
||||
@@ -16,18 +16,32 @@ ccflags-y += -I$(objtree)/security/selinux -include $(srctree)/include/uapi/asm-
|
||||
|
||||
obj-$(CONFIG_KSU) += kernelsu.o
|
||||
|
||||
# .git is a text file while the module is imported by 'git submodule add'.
|
||||
ifeq ($(shell test -e $(srctree)/$(src)/../.git; echo $$?),0)
|
||||
$(shell cd $(srctree)/$(src); /usr/bin/env PATH="$$PATH":/usr/bin:/usr/local/bin [ -f ../.git/shallow ] && git fetch --unshallow)
|
||||
KSU_GIT_VERSION := $(shell cd $(srctree)/$(src); /usr/bin/env PATH="$$PATH":/usr/bin:/usr/local/bin git rev-list --count HEAD)
|
||||
# ksu_version: major * 10000 + git version + 200 for historical reasons
|
||||
$(eval KSU_VERSION=$(shell expr 10000 + $(KSU_GIT_VERSION) + 200))
|
||||
REPO_OWNER := KernelSU-Next
|
||||
REPO_NAME := KernelSU-Next
|
||||
REPO_BRANCH := next
|
||||
|
||||
GIT_BIN := /usr/bin/env PATH="$$PATH":/usr/bin:/usr/local/bin git
|
||||
CURL_BIN := /usr/bin/env PATH="$$PATH":/usr/bin:/usr/local/bin curl
|
||||
|
||||
KSU_GITHUB_VERSION := $(shell $(CURL_BIN) -sI "https://api.github.com/repos/$(REPO_OWNER)/$(REPO_NAME)/commits?sha=$(REPO_BRANCH)&per_page=1" | grep -i "link:" | sed -n 's/.*page=\([0-9]*\)>; rel="last".*/\1/p')
|
||||
|
||||
ifeq ($(KSU_GITHUB_VERSION),)
|
||||
ifeq ($(shell test -e $(srctree)/$(src)/../.git; echo $$?),0)
|
||||
$(shell cd $(srctree)/$(src); [ -f ../.git/shallow ] && $(GIT_BIN) fetch --unshallow)
|
||||
KSU_LOCAL_VERSION := $(shell cd $(srctree)/$(src); $(GIT_BIN) rev-list --count HEAD)
|
||||
$(eval KSU_VERSION := $(shell expr 10000 + $(KSU_LOCAL_VERSION) + 200))
|
||||
$(info -- KernelSU-Next version (local .git): $(KSU_VERSION))
|
||||
else
|
||||
$(eval KSU_VERSION := 11998)
|
||||
$(warning -- Could not fetch version online or via local .git! Using fallback version: $(KSU_VERSION))
|
||||
endif
|
||||
else
|
||||
$(eval KSU_VERSION := $(shell expr 10000 + $(KSU_GITHUB_VERSION) + 200))
|
||||
$(info -- KernelSU-Next version (GitHub): $(KSU_VERSION))
|
||||
endif
|
||||
|
||||
$(info -- KernelSU-Next version: $(KSU_VERSION))
|
||||
ccflags-y += -DKSU_VERSION=$(KSU_VERSION)
|
||||
else # If there is no .git file, the default version will be passed.
|
||||
$(warning "KSU_GIT_VERSION not defined! It is better to make KernelSU-Next a git submodule!")
|
||||
ccflags-y += -DKSU_VERSION=11998
|
||||
endif
|
||||
|
||||
ifeq ($(shell grep -q " current_sid(void)" $(srctree)/security/selinux/include/objsec.h; echo $$?),0)
|
||||
ccflags-y += -DKSU_COMPAT_HAS_CURRENT_SID
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#include "apk_sign.h"
|
||||
#include "klog.h" // IWYU pragma: keep
|
||||
#include "kernel_compat.h"
|
||||
#include "throne_tracker.h"
|
||||
|
||||
|
||||
struct sdesc {
|
||||
@@ -316,5 +317,21 @@ module_param_cb(ksu_debug_manager_uid, &expected_size_ops,
|
||||
|
||||
bool is_manager_apk(char *path)
|
||||
{
|
||||
int tries = 0;
|
||||
|
||||
while (tries++ < 10) {
|
||||
if (!is_lock_held(path))
|
||||
break;
|
||||
|
||||
pr_info("%s: waiting for %s\n", __func__, path);
|
||||
msleep(100);
|
||||
}
|
||||
|
||||
// let it go, if retry fails, check_v2_signature will fail to open it anyway
|
||||
if (tries == 10) {
|
||||
pr_info("%s: timeout for %s\n", __func__, path);
|
||||
return false;
|
||||
}
|
||||
|
||||
return check_v2_signature(path, EXPECTED_NEXT_SIZE, EXPECTED_NEXT_HASH);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -118,6 +118,7 @@ static void setup_groups(struct root_profile *profile, struct cred *cred)
|
||||
|
||||
groups_sort(group_info);
|
||||
set_groups(cred, group_info);
|
||||
put_group_info(group_info);
|
||||
}
|
||||
|
||||
static void disable_seccomp(void)
|
||||
@@ -142,27 +143,17 @@ void escape_to_root(void)
|
||||
{
|
||||
struct cred *cred;
|
||||
|
||||
#ifdef KSU_GET_CRED_RCU
|
||||
rcu_read_lock();
|
||||
|
||||
do {
|
||||
cred = (struct cred *)__task_cred((current));
|
||||
BUG_ON(!cred);
|
||||
} while (!get_cred_rcu(cred));
|
||||
cred = prepare_creds();
|
||||
if (!cred) {
|
||||
pr_warn("prepare_creds failed!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (cred->euid.val == 0) {
|
||||
pr_warn("Already root, don't escape!\n");
|
||||
rcu_read_unlock();
|
||||
abort_creds(cred);
|
||||
return;
|
||||
}
|
||||
#else
|
||||
cred = (struct cred *)__task_cred(current);
|
||||
|
||||
if (cred->euid.val == 0) {
|
||||
pr_warn("Already root, don't escape!\n");
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
struct root_profile *profile = ksu_get_root_profile(cred->uid.val);
|
||||
|
||||
@@ -198,9 +189,7 @@ void escape_to_root(void)
|
||||
|
||||
setup_groups(profile, cred);
|
||||
|
||||
#ifdef KSU_GET_CRED_RCU
|
||||
rcu_read_unlock();
|
||||
#endif
|
||||
commit_creds(cred);
|
||||
|
||||
// Refer to kernel/seccomp.c: seccomp_set_mode_strict
|
||||
// When disabling Seccomp, ensure that current->sighand->siglock is held during the operation.
|
||||
@@ -267,7 +256,7 @@ static void nuke_ext4_sysfs() {
|
||||
}
|
||||
|
||||
ext4_unregister_sysfs(sb);
|
||||
path_put(&path);
|
||||
path_put(&path);
|
||||
}
|
||||
|
||||
int ksu_handle_prctl(int option, unsigned long arg2, unsigned long arg3,
|
||||
@@ -339,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";
|
||||
@@ -580,11 +580,13 @@ static void try_umount(const char *mnt, bool check_mnt, int flags)
|
||||
|
||||
if (path.dentry != path.mnt->mnt_root) {
|
||||
// it is not root mountpoint, maybe umounted by others already.
|
||||
path_put(&path);
|
||||
return;
|
||||
}
|
||||
|
||||
// we are only interest in some specific mounts
|
||||
if (check_mnt && !should_umount(&path)) {
|
||||
path_put(&path);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -396,10 +396,12 @@ int ksu_handle_vfs_read(struct file **file_ptr, char __user **buf_ptr,
|
||||
if (orig_read) {
|
||||
fops_proxy.read = read_proxy;
|
||||
}
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 16, 0)
|
||||
orig_read_iter = file->f_op->read_iter;
|
||||
if (orig_read_iter) {
|
||||
fops_proxy.read_iter = read_iter_proxy;
|
||||
}
|
||||
#endif
|
||||
// replace the file_operations
|
||||
file->f_op = &fops_proxy;
|
||||
read_count_append = rc_count;
|
||||
@@ -632,6 +634,29 @@ static void do_stop_input_hook(struct work_struct *work)
|
||||
}
|
||||
#endif
|
||||
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 0)
|
||||
#include "objsec.h" // task_security_struct
|
||||
bool is_ksu_transition(const struct task_security_struct *old_tsec,
|
||||
const struct task_security_struct *new_tsec)
|
||||
{
|
||||
static u32 ksu_sid;
|
||||
char *secdata;
|
||||
u32 seclen;
|
||||
bool allowed = false;
|
||||
|
||||
if (!ksu_sid)
|
||||
security_secctx_to_secid("u:r:su:s0", strlen("u:r:su:s0"), &ksu_sid);
|
||||
|
||||
if (security_secid_to_secctx(old_tsec->sid, &secdata, &seclen))
|
||||
return false;
|
||||
|
||||
allowed = (!strcmp("u:r:init:s0", secdata) && new_tsec->sid == ksu_sid);
|
||||
security_release_secctx(secdata, seclen);
|
||||
|
||||
return allowed;
|
||||
}
|
||||
#endif
|
||||
|
||||
static void stop_vfs_read_hook()
|
||||
{
|
||||
#ifdef CONFIG_KSU_KPROBES_HOOK
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
#include <linux/string.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/version.h>
|
||||
#include <linux/namei.h>
|
||||
|
||||
#include "allowlist.h"
|
||||
#include "klog.h" // IWYU pragma: keep
|
||||
@@ -14,13 +13,9 @@
|
||||
#include "throne_tracker.h"
|
||||
#include "kernel_compat.h"
|
||||
|
||||
#include <linux/kthread.h>
|
||||
#include <linux/sched.h>
|
||||
|
||||
uid_t ksu_manager_uid = KSU_INVALID_UID;
|
||||
|
||||
static struct task_struct *throne_thread;
|
||||
#define SYSTEM_PACKAGES_LIST_PATH "/data/system/packages.list"
|
||||
#define SYSTEM_PACKAGES_LIST_PATH "/data/system/packages.list.tmp"
|
||||
|
||||
struct uid_data {
|
||||
struct list_head list;
|
||||
@@ -120,7 +115,6 @@ struct my_dir_context {
|
||||
void *private_data;
|
||||
int depth;
|
||||
int *stop;
|
||||
struct super_block* root_sb;
|
||||
};
|
||||
// https://docs.kernel.org/filesystems/porting.html
|
||||
// filldir_t (readdir callbacks) calling conventions have changed. Instead of returning 0 or -E... it returns bool now. false means "no more" (as -E... used to) and true - "keep going" (as 0 in old calling conventions). Rationale: callers never looked at specific -E... values anyway. -> iterate_shared() instances require no changes at all, all filldir_t ones in the tree converted.
|
||||
@@ -141,8 +135,6 @@ FILLDIR_RETURN_TYPE my_actor(struct dir_context *ctx, const char *name,
|
||||
struct my_dir_context *my_ctx =
|
||||
container_of(ctx, struct my_dir_context, ctx);
|
||||
char dirpath[DATA_PATH_LEN];
|
||||
int err;
|
||||
struct path path;
|
||||
|
||||
if (!my_ctx) {
|
||||
pr_err("Invalid context\n");
|
||||
@@ -169,18 +161,6 @@ FILLDIR_RETURN_TYPE my_actor(struct dir_context *ctx, const char *name,
|
||||
return FILLDIR_ACTOR_CONTINUE;
|
||||
}
|
||||
|
||||
err = kern_path(dirpath, 0, &path);
|
||||
|
||||
if (err) {
|
||||
pr_err("get dirpath %s err: %d\n", dirpath, err);
|
||||
return FILLDIR_ACTOR_CONTINUE;
|
||||
}
|
||||
|
||||
if (my_ctx->root_sb != path.dentry->d_inode->i_sb) {
|
||||
pr_info("skip cross fs: %s", dirpath);
|
||||
return FILLDIR_ACTOR_CONTINUE;
|
||||
}
|
||||
|
||||
if (d_type == DT_DIR && my_ctx->depth > 0 &&
|
||||
(my_ctx->stop && !*my_ctx->stop)) {
|
||||
struct data_path *data = kmalloc(sizeof(struct data_path), GFP_ATOMIC);
|
||||
@@ -236,21 +216,53 @@ FILLDIR_RETURN_TYPE my_actor(struct dir_context *ctx, const char *name,
|
||||
return FILLDIR_ACTOR_CONTINUE;
|
||||
}
|
||||
|
||||
void search_manager(const char *path, int depth, struct list_head *uid_data)
|
||||
/*
|
||||
* small helper to check if lock is held
|
||||
* false - file is stable
|
||||
* true - file is being deleted/renamed
|
||||
* possibly optional
|
||||
*
|
||||
*/
|
||||
bool is_lock_held(const char *path)
|
||||
{
|
||||
int i, stop = 0, err;
|
||||
struct list_head data_path_list;
|
||||
struct path kpath;
|
||||
struct super_block* root_sb;
|
||||
INIT_LIST_HEAD(&data_path_list);
|
||||
|
||||
err = kern_path(path, 0, &kpath);
|
||||
// kern_path returns 0 on success
|
||||
if (kern_path(path, 0, &kpath))
|
||||
return true;
|
||||
|
||||
if (err) {
|
||||
pr_err("get search root %s err: %d\n", path, err);
|
||||
return;
|
||||
// just being defensive
|
||||
if (!kpath.dentry) {
|
||||
path_put(&kpath);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!spin_trylock(&kpath.dentry->d_lock)) {
|
||||
pr_info("%s: lock held, bail out!\n", __func__);
|
||||
path_put(&kpath);
|
||||
return true;
|
||||
}
|
||||
// we hold it ourselves here!
|
||||
|
||||
spin_unlock(&kpath.dentry->d_lock);
|
||||
path_put(&kpath);
|
||||
return false;
|
||||
}
|
||||
|
||||
// compat: https://elixir.bootlin.com/linux/v3.9/source/include/linux/fs.h#L771
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,9,0)
|
||||
#define S_MAGIC_COMPAT(x) ((x)->f_inode->i_sb->s_magic)
|
||||
#else
|
||||
#define S_MAGIC_COMPAT(x) ((x)->f_path.dentry->d_inode->i_sb->s_magic)
|
||||
#endif
|
||||
|
||||
void search_manager(const char *path, int depth, struct list_head *uid_data)
|
||||
{
|
||||
int i, stop = 0;
|
||||
struct list_head data_path_list;
|
||||
INIT_LIST_HEAD(&data_path_list);
|
||||
unsigned long data_app_magic = 0;
|
||||
|
||||
// Initialize APK cache list
|
||||
struct apk_path_hash *pos, *n;
|
||||
list_for_each_entry(pos, &apk_path_hash_list, list) {
|
||||
@@ -267,8 +279,6 @@ void search_manager(const char *path, int depth, struct list_head *uid_data)
|
||||
data.depth = depth;
|
||||
list_add_tail(&data.list, &data_path_list);
|
||||
|
||||
root_sb = kpath.dentry->d_inode->i_sb;
|
||||
|
||||
for (i = depth; i >= 0; i--) {
|
||||
struct data_path *pos, *n;
|
||||
|
||||
@@ -278,8 +288,7 @@ void search_manager(const char *path, int depth, struct list_head *uid_data)
|
||||
.parent_dir = pos->dirpath,
|
||||
.private_data = uid_data,
|
||||
.depth = pos->depth,
|
||||
.stop = &stop,
|
||||
.root_sb = root_sb };
|
||||
.stop = &stop };
|
||||
struct file *file;
|
||||
|
||||
if (!stop) {
|
||||
@@ -288,6 +297,24 @@ void search_manager(const char *path, int depth, struct list_head *uid_data)
|
||||
pr_err("Failed to open directory: %s, err: %ld\n", pos->dirpath, PTR_ERR(file));
|
||||
goto skip_iterate;
|
||||
}
|
||||
|
||||
// grab magic on first folder, which is /data/app
|
||||
if (!data_app_magic) {
|
||||
if (S_MAGIC_COMPAT(file)) {
|
||||
data_app_magic = S_MAGIC_COMPAT(file);
|
||||
pr_info("%s: dir: %s got magic! 0x%lx\n", __func__, pos->dirpath, data_app_magic);
|
||||
} else {
|
||||
filp_close(file, NULL);
|
||||
goto skip_iterate;
|
||||
}
|
||||
}
|
||||
|
||||
if (S_MAGIC_COMPAT(file) != data_app_magic) {
|
||||
pr_info("%s: skip: %s magic: 0x%lx expected: 0x%lx\n", __func__, pos->dirpath,
|
||||
S_MAGIC_COMPAT(file), data_app_magic);
|
||||
filp_close(file, NULL);
|
||||
goto skip_iterate;
|
||||
}
|
||||
|
||||
iterate_dir(file, &ctx.ctx);
|
||||
filp_close(file, NULL);
|
||||
@@ -324,15 +351,27 @@ static bool is_uid_exist(uid_t uid, char *package, void *data)
|
||||
return exist;
|
||||
}
|
||||
|
||||
static void track_throne_function()
|
||||
void track_throne()
|
||||
{
|
||||
struct file *fp =
|
||||
ksu_filp_open_compat(SYSTEM_PACKAGES_LIST_PATH, O_RDONLY, 0);
|
||||
struct file *fp;
|
||||
int tries = 0;
|
||||
|
||||
while (tries++ < 10) {
|
||||
if (!is_lock_held(SYSTEM_PACKAGES_LIST_PATH)) {
|
||||
fp = ksu_filp_open_compat(SYSTEM_PACKAGES_LIST_PATH, O_RDONLY, 0);
|
||||
if (!IS_ERR(fp))
|
||||
break;
|
||||
}
|
||||
|
||||
pr_info("%s: waiting for %s\n", __func__, SYSTEM_PACKAGES_LIST_PATH);
|
||||
msleep(100); // migth as well add a delay
|
||||
};
|
||||
|
||||
if (IS_ERR(fp)) {
|
||||
pr_err("%s: open " SYSTEM_PACKAGES_LIST_PATH " failed: %ld\n",
|
||||
__func__, PTR_ERR(fp));
|
||||
pr_err("%s: open " SYSTEM_PACKAGES_LIST_PATH " failed: %ld\n", __func__, PTR_ERR(fp));
|
||||
return;
|
||||
}
|
||||
} else
|
||||
pr_info("%s: %s found!\n", __func__, SYSTEM_PACKAGES_LIST_PATH);
|
||||
|
||||
struct list_head uid_list;
|
||||
INIT_LIST_HEAD(&uid_list);
|
||||
@@ -419,23 +458,6 @@ out:
|
||||
}
|
||||
}
|
||||
|
||||
static int throne_tracker_thread(void *data)
|
||||
{
|
||||
pr_info("%s: pid: %d started\n", __func__, current->pid);
|
||||
track_throne_function();
|
||||
throne_thread = NULL;
|
||||
pr_info("%s: pid: %d exit!\n", __func__, current->pid);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void track_throne()
|
||||
{
|
||||
throne_thread = kthread_run(throne_tracker_thread, NULL, "throne_tracker");
|
||||
if (IS_ERR(throne_thread)) {
|
||||
throne_thread = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void ksu_throne_tracker_init()
|
||||
{
|
||||
// nothing to do
|
||||
|
||||
@@ -7,4 +7,6 @@ void ksu_throne_tracker_exit();
|
||||
|
||||
void track_throne();
|
||||
|
||||
bool is_lock_held(const char *path);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -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
|
||||
@@ -24,6 +24,15 @@
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.BROWSABLE" />
|
||||
<data android:mimeType="application/zip" />
|
||||
<data android:scheme="file" />
|
||||
<data android:scheme="content" />
|
||||
<data android:pathPattern=".*\\.zip" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package com.rifsxd.ksunext.ui
|
||||
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.content.Context
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
@@ -22,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
|
||||
@@ -36,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
|
||||
@@ -51,12 +56,22 @@ import com.rifsxd.ksunext.Natives
|
||||
import com.rifsxd.ksunext.ksuApp
|
||||
import com.rifsxd.ksunext.ui.screen.BottomBarDestination
|
||||
import com.rifsxd.ksunext.ui.theme.KernelSUTheme
|
||||
import com.rifsxd.ksunext.ui.util.*
|
||||
import com.rifsxd.ksunext.ui.util.LocalSnackbarHost
|
||||
import com.rifsxd.ksunext.ui.util.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
|
||||
@@ -70,11 +85,41 @@ class MainActivity : ComponentActivity() {
|
||||
val isManager = Natives.becomeManager(ksuApp.packageName)
|
||||
if (isManager) install()
|
||||
|
||||
val zipUri: Uri? = when (intent?.action) {
|
||||
Intent.ACTION_VIEW, Intent.ACTION_SEND -> {
|
||||
val uri = intent.data ?: intent.getParcelableExtra<Uri>(Intent.EXTRA_STREAM)
|
||||
uri?.let {
|
||||
val name = when (it.scheme) {
|
||||
"file" -> it.lastPathSegment ?: ""
|
||||
"content" -> {
|
||||
contentResolver.query(it, null, null, null, null)?.use { cursor ->
|
||||
val nameIndex = cursor.getColumnIndex(android.provider.OpenableColumns.DISPLAY_NAME)
|
||||
if (cursor.moveToFirst() && nameIndex != -1) {
|
||||
cursor.getString(nameIndex)
|
||||
} else {
|
||||
it.lastPathSegment ?: ""
|
||||
}
|
||||
} ?: (it.lastPathSegment ?: "")
|
||||
}
|
||||
else -> it.lastPathSegment ?: ""
|
||||
}
|
||||
if (name.lowercase().endsWith(".zip")) it else null
|
||||
}
|
||||
}
|
||||
else -> null
|
||||
}
|
||||
|
||||
setContent {
|
||||
// Read AMOLED mode preference
|
||||
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
|
||||
) {
|
||||
@@ -82,6 +127,29 @@ class MainActivity : ComponentActivity() {
|
||||
val snackBarHostState = remember { SnackbarHostState() }
|
||||
val currentDestination = navController.currentBackStackEntryAsState()?.value?.destination
|
||||
|
||||
val navigator = navController.rememberDestinationsNavigator()
|
||||
|
||||
LaunchedEffect(zipUri) {
|
||||
if (zipUri != null) {
|
||||
navigator.navigate(
|
||||
FlashScreenDestination(
|
||||
FlashIt.FlashModules(listOf(zipUri)),
|
||||
finishIntent = true
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
@@ -95,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)
|
||||
@@ -122,43 +190,59 @@ class MainActivity : ComponentActivity() {
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun BottomBar(navController: NavHostController) {
|
||||
private fun BottomBar(navController: NavHostController, moduleUpdateCount: Int) {
|
||||
val navigator = navController.rememberDestinationsNavigator()
|
||||
val isManager = Natives.becomeManager(ksuApp.packageName)
|
||||
val fullFeatured = isManager && !Natives.requireNewKernel() && rootAvailable()
|
||||
val suCompatDisabled = isSuCompatDisabled()
|
||||
val suSFS = getSuSFS()
|
||||
val susSUMode = susfsSUS_SU_Mode()
|
||||
|
||||
NavigationBar(
|
||||
tonalElevation = 8.dp,
|
||||
windowInsets = WindowInsets.systemBars.union(WindowInsets.displayCutout).only(
|
||||
WindowInsetsSides.Horizontal + WindowInsetsSides.Bottom
|
||||
)
|
||||
) {
|
||||
BottomBarDestination.entries.forEach { destination ->
|
||||
if (!fullFeatured && destination.rootRequired) return@forEach
|
||||
val isCurrentDestOnBackStack by navController.isRouteOnBackStackAsState(destination.direction)
|
||||
NavigationBarItem(
|
||||
selected = isCurrentDestOnBackStack,
|
||||
onClick = {
|
||||
if (isCurrentDestOnBackStack) {
|
||||
navigator.popBackStack(destination.direction, false)
|
||||
}
|
||||
navigator.navigate(destination.direction) {
|
||||
popUpTo(NavGraphs.root) {
|
||||
saveState = true
|
||||
BottomBarDestination.entries
|
||||
.forEach { destination ->
|
||||
if (!fullFeatured && destination.rootRequired) return@forEach
|
||||
val isCurrentDestOnBackStack by navController.isRouteOnBackStackAsState(destination.direction)
|
||||
NavigationBarItem(
|
||||
selected = isCurrentDestOnBackStack,
|
||||
onClick = {
|
||||
if (isCurrentDestOnBackStack) {
|
||||
navigator.popBackStack(destination.direction, false)
|
||||
}
|
||||
launchSingleTop = true
|
||||
restoreState = true
|
||||
}
|
||||
},
|
||||
icon = {
|
||||
if (isCurrentDestOnBackStack) {
|
||||
Icon(destination.iconSelected, stringResource(destination.label))
|
||||
} else {
|
||||
Icon(destination.iconNotSelected, stringResource(destination.label))
|
||||
}
|
||||
},
|
||||
label = { Text(stringResource(destination.label)) },
|
||||
alwaysShowLabel = true
|
||||
)
|
||||
}
|
||||
navigator.navigate(destination.direction) {
|
||||
popUpTo(NavGraphs.root) {
|
||||
saveState = true
|
||||
}
|
||||
launchSingleTop = true
|
||||
restoreState = true
|
||||
}
|
||||
},
|
||||
icon = {
|
||||
// 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 {
|
||||
if (isCurrentDestOnBackStack) {
|
||||
Icon(destination.iconSelected, stringResource(destination.label))
|
||||
} else {
|
||||
Icon(destination.iconNotSelected, stringResource(destination.label))
|
||||
}
|
||||
}
|
||||
},
|
||||
label = { Text(stringResource(destination.label)) },
|
||||
alwaysShowLabel = true
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -56,6 +58,7 @@ import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.DpOffset
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.compose.dropUnlessResumed
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import coil.compose.AsyncImage
|
||||
import coil.request.ImageRequest
|
||||
import com.ramcosta.composedestinations.annotation.Destination
|
||||
@@ -94,6 +97,7 @@ fun AppProfileScreen(
|
||||
val snackBarHost = LocalSnackbarHost.current
|
||||
val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior()
|
||||
val scope = rememberCoroutineScope()
|
||||
val viewModel: SuperUserViewModel = viewModel()
|
||||
val failToUpdateAppProfile = stringResource(R.string.failed_to_update_app_profile).format(appInfo.label)
|
||||
val failToUpdateSepolicy = stringResource(R.string.failed_to_update_sepolicy).format(appInfo.label)
|
||||
val suNotAllowed = stringResource(R.string.su_not_allowed).format(appInfo.label)
|
||||
@@ -160,6 +164,7 @@ fun AppProfileScreen(
|
||||
snackBarHost.showSnackbar(failToUpdateAppProfile.format(appInfo.uid))
|
||||
} else {
|
||||
profile = it
|
||||
viewModel.updateAppProfile(packageName, it)
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -183,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,
|
||||
)
|
||||
@@ -269,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(
|
||||
@@ -288,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 = {
|
||||
@@ -165,9 +179,7 @@ fun BackupRestoreScreen(navigator: DestinationsNavigator) {
|
||||
val prefs = context.getSharedPreferences("settings", Context.MODE_PRIVATE)
|
||||
|
||||
var useOverlayFs by rememberSaveable {
|
||||
mutableStateOf(
|
||||
prefs.getBoolean("use_overlay_fs", false)
|
||||
)
|
||||
mutableStateOf(readMountSystemFile())
|
||||
}
|
||||
|
||||
val moduleRestore = stringResource(id = R.string.module_restore)
|
||||
@@ -178,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(
|
||||
@@ -214,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)
|
||||
@@ -236,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)
|
||||
@@ -259,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,35 +103,181 @@ fun CustomizationScreen(navigator: DestinationsNavigator) {
|
||||
|
||||
val prefs = context.getSharedPreferences("settings", Context.MODE_PRIVATE)
|
||||
|
||||
var useLagacyUI by rememberSaveable {
|
||||
mutableStateOf(
|
||||
prefs.getBoolean("use_legacyui", false)
|
||||
)
|
||||
// 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)
|
||||
}
|
||||
SwitchItem(
|
||||
icon = Icons.Filled.Dashboard,
|
||||
title = stringResource(id = R.string.settings_legacyui),
|
||||
summary = stringResource(id = R.string.settings_legacyui_summary),
|
||||
checked = useLagacyUI
|
||||
) {
|
||||
prefs.edit().putBoolean("use_legacyui", it).apply()
|
||||
useLagacyUI = it
|
||||
|
||||
// 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(
|
||||
enabled = !useLagacyUI,
|
||||
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 {
|
||||
@@ -141,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 = {
|
||||
@@ -175,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) }
|
||||
|
||||
@@ -0,0 +1,152 @@
|
||||
package com.rifsxd.ksunext.ui.screen
|
||||
|
||||
import android.content.Context
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.WindowInsets
|
||||
import androidx.compose.foundation.layout.WindowInsetsSides
|
||||
import androidx.compose.foundation.layout.only
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.safeDrawing
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.filled.*
|
||||
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.Scaffold
|
||||
import androidx.compose.material3.SnackbarHost
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TopAppBar
|
||||
import androidx.compose.material3.TopAppBarDefaults
|
||||
import androidx.compose.material3.TopAppBarScrollBehavior
|
||||
import androidx.compose.material3.rememberTopAppBarState
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.runtime.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.ramcosta.composedestinations.annotation.Destination
|
||||
import com.ramcosta.composedestinations.annotation.RootGraph
|
||||
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
|
||||
import com.ramcosta.composedestinations.navigation.EmptyDestinationsNavigator
|
||||
import kotlinx.coroutines.launch
|
||||
import com.rifsxd.ksunext.Natives
|
||||
import com.rifsxd.ksunext.ksuApp
|
||||
import com.rifsxd.ksunext.R
|
||||
import com.rifsxd.ksunext.ui.component.SwitchItem
|
||||
import com.rifsxd.ksunext.ui.util.LocalSnackbarHost
|
||||
import com.rifsxd.ksunext.ui.util.*
|
||||
|
||||
/**
|
||||
* @author rifsxd
|
||||
* @date 2025/6/15.
|
||||
*/
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Destination<RootGraph>
|
||||
@Composable
|
||||
fun DeveloperScreen(navigator: DestinationsNavigator) {
|
||||
val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState())
|
||||
val snackBarHost = LocalSnackbarHost.current
|
||||
|
||||
val isManager = Natives.becomeManager(ksuApp.packageName)
|
||||
val ksuVersion = if (isManager) Natives.version else null
|
||||
|
||||
Scaffold(
|
||||
topBar = {
|
||||
TopBar(
|
||||
onBack = { navigator.popBackStack() },
|
||||
scrollBehavior = scrollBehavior
|
||||
)
|
||||
},
|
||||
snackbarHost = { SnackbarHost(snackBarHost) },
|
||||
contentWindowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal)
|
||||
) { paddingValues ->
|
||||
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.padding(paddingValues)
|
||||
.nestedScroll(scrollBehavior.nestedScrollConnection)
|
||||
.verticalScroll(rememberScrollState())
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
val scope = rememberCoroutineScope()
|
||||
val prefs = context.getSharedPreferences("settings", Context.MODE_PRIVATE)
|
||||
|
||||
// --- Developer Options Switch ---
|
||||
var developerOptionsEnabled by rememberSaveable {
|
||||
mutableStateOf(
|
||||
prefs.getBoolean("enable_developer_options", false)
|
||||
)
|
||||
}
|
||||
if (ksuVersion != null) {
|
||||
SwitchItem(
|
||||
icon = Icons.Filled.DeveloperMode,
|
||||
title = stringResource(id = R.string.enable_developer_options),
|
||||
summary = stringResource(id = R.string.enable_developer_options_summary),
|
||||
checked = developerOptionsEnabled
|
||||
) {
|
||||
prefs.edit().putBoolean("enable_developer_options", it).apply()
|
||||
developerOptionsEnabled = it
|
||||
}
|
||||
}
|
||||
|
||||
var enableWebDebugging by rememberSaveable {
|
||||
mutableStateOf(
|
||||
prefs.getBoolean("enable_web_debugging", false)
|
||||
)
|
||||
}
|
||||
if (ksuVersion != null) {
|
||||
SwitchItem(
|
||||
enabled = developerOptionsEnabled,
|
||||
icon = Icons.Filled.Web,
|
||||
title = stringResource(id = R.string.enable_web_debugging),
|
||||
summary = stringResource(id = R.string.enable_web_debugging_summary),
|
||||
checked = enableWebDebugging
|
||||
) {
|
||||
prefs.edit().putBoolean("enable_web_debugging", it).apply()
|
||||
enableWebDebugging = it
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
private fun TopBar(
|
||||
onBack: () -> Unit = {},
|
||||
scrollBehavior: TopAppBarScrollBehavior? = null
|
||||
) {
|
||||
TopAppBar(
|
||||
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) }
|
||||
},
|
||||
windowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal),
|
||||
scrollBehavior = scrollBehavior
|
||||
)
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
private fun DeveloperPreview() {
|
||||
DeveloperScreen(EmptyDestinationsNavigator)
|
||||
}
|
||||
@@ -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
|
||||
@@ -63,6 +64,8 @@ import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import kotlinx.parcelize.Parcelize
|
||||
import com.rifsxd.ksunext.R
|
||||
import com.rifsxd.ksunext.ui.component.rememberConfirmDialog
|
||||
import com.rifsxd.ksunext.ui.component.ConfirmResult
|
||||
import com.rifsxd.ksunext.ui.component.KeyEventBlocker
|
||||
import com.rifsxd.ksunext.ui.util.FlashResult
|
||||
import com.rifsxd.ksunext.ui.util.LkmSelection
|
||||
@@ -77,12 +80,21 @@ import java.text.SimpleDateFormat
|
||||
import java.util.Date
|
||||
import java.util.Locale
|
||||
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import android.app.Activity
|
||||
|
||||
enum class FlashingStatus {
|
||||
FLASHING,
|
||||
SUCCESS,
|
||||
FAILED
|
||||
}
|
||||
|
||||
fun Context.findActivity(): Activity? = when (this) {
|
||||
is Activity -> this
|
||||
is android.content.ContextWrapper -> baseContext.findActivity()
|
||||
else -> null
|
||||
}
|
||||
|
||||
// Lets you flash modules sequentially when mutiple zipUris are selected
|
||||
fun flashModulesSequentially(
|
||||
uris: List<Uri>,
|
||||
@@ -106,7 +118,11 @@ fun flashModulesSequentially(
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
@Destination<RootGraph>
|
||||
fun FlashScreen(navigator: DestinationsNavigator, flashIt: FlashIt) {
|
||||
fun FlashScreen(
|
||||
navigator: DestinationsNavigator,
|
||||
flashIt: FlashIt,
|
||||
finishIntent: Boolean = false
|
||||
) {
|
||||
|
||||
var text by rememberSaveable { mutableStateOf("") }
|
||||
var tempText: String
|
||||
@@ -126,6 +142,8 @@ fun FlashScreen(navigator: DestinationsNavigator, flashIt: FlashIt) {
|
||||
val prefs = context.getSharedPreferences("settings", Context.MODE_PRIVATE)
|
||||
val developerOptionsEnabled = prefs.getBoolean("enable_developer_options", false)
|
||||
|
||||
val activity = context.findActivity()
|
||||
|
||||
val view = LocalView.current
|
||||
DisposableEffect(flashing) {
|
||||
view.keepScreenOn = flashing == FlashingStatus.FLASHING
|
||||
@@ -138,12 +156,49 @@ fun FlashScreen(navigator: DestinationsNavigator, flashIt: FlashIt) {
|
||||
// Disable back button if flashing is running
|
||||
}
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
if (text.isNotEmpty()) {
|
||||
return@LaunchedEffect
|
||||
BackHandler(enabled = flashing != FlashingStatus.FLASHING) {
|
||||
navigator.popBackStack()
|
||||
if (finishIntent) activity?.finish()
|
||||
}
|
||||
|
||||
val confirmDialog = rememberConfirmDialog()
|
||||
var confirmed by rememberSaveable { mutableStateOf(flashIt !is FlashIt.FlashModules) }
|
||||
var pendingFlashIt by rememberSaveable { mutableStateOf<FlashIt?>(null) }
|
||||
var hasFlashed by rememberSaveable { mutableStateOf(false) }
|
||||
|
||||
LaunchedEffect(flashIt) {
|
||||
if (flashIt is FlashIt.FlashModules && !confirmed) {
|
||||
val uris = flashIt.uris
|
||||
val moduleNames =
|
||||
uris.mapIndexed { index, uri -> "\n${index + 1}. ${uri.getFileName(context)}" }
|
||||
.joinToString("")
|
||||
val confirmContent =
|
||||
context.getString(R.string.module_install_prompt_with_name, moduleNames)
|
||||
val confirmTitle = context.getString(R.string.module)
|
||||
val result = confirmDialog.awaitConfirm(
|
||||
title = confirmTitle,
|
||||
content = confirmContent,
|
||||
markdown = true
|
||||
)
|
||||
if (result == ConfirmResult.Confirmed) {
|
||||
confirmed = true
|
||||
pendingFlashIt = flashIt
|
||||
} else {
|
||||
// User cancelled, go back
|
||||
navigator.popBackStack()
|
||||
if (finishIntent) activity?.finish()
|
||||
}
|
||||
} else {
|
||||
confirmed = true
|
||||
pendingFlashIt = flashIt
|
||||
}
|
||||
}
|
||||
|
||||
LaunchedEffect(confirmed, pendingFlashIt) {
|
||||
if (!confirmed || pendingFlashIt == null || text.isNotEmpty() || hasFlashed) return@LaunchedEffect
|
||||
hasFlashed = true
|
||||
withContext(Dispatchers.IO) {
|
||||
flashIt(flashIt, onStdout = {
|
||||
flashIt(pendingFlashIt!!, onStdout = {
|
||||
tempText = "$it\n"
|
||||
if (tempText.startsWith("[H[J")) { // clear command
|
||||
text = tempText.substring(6)
|
||||
@@ -172,6 +227,7 @@ fun FlashScreen(navigator: DestinationsNavigator, flashIt: FlashIt) {
|
||||
flashing,
|
||||
onBack = dropUnlessResumed {
|
||||
navigator.popBackStack()
|
||||
if (finishIntent) activity?.finish()
|
||||
},
|
||||
onSave = {
|
||||
scope.launch {
|
||||
@@ -211,6 +267,7 @@ fun FlashScreen(navigator: DestinationsNavigator, flashIt: FlashIt) {
|
||||
icon = { Icon(Icons.Filled.Close, contentDescription = null) },
|
||||
onClick = {
|
||||
navigator.popBackStack()
|
||||
if (finishIntent) activity?.finish()
|
||||
}
|
||||
)
|
||||
}
|
||||
@@ -282,6 +339,19 @@ fun FlashScreen(navigator: DestinationsNavigator, flashIt: FlashIt) {
|
||||
}
|
||||
}
|
||||
|
||||
fun Uri.getFileName(context: Context): String {
|
||||
val contentResolver = context.contentResolver
|
||||
val cursor = contentResolver.query(this, null, null, null, null)
|
||||
return cursor?.use {
|
||||
val nameIndex = it.getColumnIndex(android.provider.OpenableColumns.DISPLAY_NAME)
|
||||
if (it.moveToFirst() && nameIndex != -1) {
|
||||
it.getString(nameIndex)
|
||||
} else {
|
||||
this.lastPathSegment ?: "unknown.zip"
|
||||
}
|
||||
} ?: (this.lastPathSegment ?: "unknown.zip")
|
||||
}
|
||||
|
||||
@Parcelize
|
||||
sealed class FlashIt : Parcelable {
|
||||
data class FlashBoot(val boot: Uri? = null, val lkm: LkmSelection, val ota: Boolean) :
|
||||
@@ -335,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 = {
|
||||
|
||||
@@ -8,8 +8,13 @@ import android.os.Looper
|
||||
import android.system.Os
|
||||
import android.widget.Toast
|
||||
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
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
@@ -29,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
|
||||
@@ -50,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)
|
||||
@@ -92,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(
|
||||
@@ -141,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
|
||||
@@ -193,6 +256,19 @@ fun RebootDropdownItem(@StringRes id: Int, reason: String = "") {
|
||||
})
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun getSeasonalIcon(): ImageVector {
|
||||
val month = Calendar.getInstance().get(Calendar.MONTH) // 0-11 for January-December
|
||||
return when (month) {
|
||||
Calendar.DECEMBER, Calendar.JANUARY, Calendar.FEBRUARY -> Icons.Filled.AcUnit // Winter
|
||||
Calendar.MARCH, Calendar.APRIL, Calendar.MAY -> Icons.Filled.Spa // Spring
|
||||
Calendar.JUNE, Calendar.JULY, Calendar.AUGUST -> Icons.Filled.WbSunny // Summer
|
||||
Calendar.SEPTEMBER, Calendar.OCTOBER, Calendar.NOVEMBER -> Icons.Filled.Forest // Fall
|
||||
else -> Icons.Filled.Whatshot // Fallback icon
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
private fun TopBar(
|
||||
@@ -201,8 +277,46 @@ private fun TopBar(
|
||||
onInstallClick: () -> Unit,
|
||||
scrollBehavior: TopAppBarScrollBehavior? = null
|
||||
) {
|
||||
var isSpinning by remember { mutableStateOf(false) }
|
||||
val rotation by animateFloatAsState(
|
||||
targetValue = if (isSpinning) 360f else 0f,
|
||||
animationSpec = tween(durationMillis = 800),
|
||||
finishedListener = {
|
||||
isSpinning = false
|
||||
}
|
||||
)
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
isSpinning = true
|
||||
}
|
||||
|
||||
TopAppBar(
|
||||
title = { Text(stringResource(R.string.app_name)) },
|
||||
title = {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
modifier = Modifier.clickable(
|
||||
indication = null,
|
||||
interactionSource = remember { MutableInteractionSource() }
|
||||
) {
|
||||
if (!isSpinning) isSpinning = true
|
||||
}
|
||||
) {
|
||||
Icon(
|
||||
imageVector = getSeasonalIcon(),
|
||||
contentDescription = null,
|
||||
modifier = Modifier
|
||||
.padding(end = 8.dp)
|
||||
.graphicsLayer {
|
||||
rotationZ = rotation
|
||||
}
|
||||
)
|
||||
Text(
|
||||
text = stringResource(R.string.app_name),
|
||||
style = MaterialTheme.typography.titleLarge,
|
||||
fontWeight = FontWeight.Black,
|
||||
)
|
||||
}
|
||||
},
|
||||
actions = {
|
||||
if (ksuVersion != null) {
|
||||
if (kernelVersion.isGKI()) {
|
||||
@@ -249,17 +363,6 @@ private fun TopBar(
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun getSeasonalIcon(): ImageVector {
|
||||
val month = Calendar.getInstance().get(Calendar.MONTH) // 0-11 for January-December
|
||||
return when (month) {
|
||||
Calendar.DECEMBER, Calendar.JANUARY, Calendar.FEBRUARY -> Icons.Filled.AcUnit // Winter
|
||||
Calendar.MARCH, Calendar.APRIL, Calendar.MAY -> Icons.Filled.Spa // Spring
|
||||
Calendar.JUNE, Calendar.JULY, Calendar.AUGUST -> Icons.Filled.WbSunny // Summer
|
||||
Calendar.SEPTEMBER, Calendar.OCTOBER, Calendar.NOVEMBER -> Icons.Filled.Forest // Fall
|
||||
else -> Icons.Filled.Whatshot // Fallback icon
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun StatusCard(
|
||||
@@ -274,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
|
||||
})
|
||||
) {
|
||||
@@ -292,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()
|
||||
@@ -312,7 +417,7 @@ private fun StatusCard(
|
||||
}
|
||||
|
||||
Icon(
|
||||
getSeasonalIcon(), // Use dynamic seasonal icon
|
||||
imageVector = Icons.Filled.CheckCircle,
|
||||
contentDescription = stringResource(R.string.home_working)
|
||||
)
|
||||
Column(
|
||||
@@ -322,69 +427,67 @@ private fun StatusCard(
|
||||
val labelStyle = LabelItemDefaults.style
|
||||
TextRow(
|
||||
trailingContent = {
|
||||
LabelItem(
|
||||
icon = if (Natives.isSafeMode) {
|
||||
{
|
||||
Icon(
|
||||
tint = labelStyle.contentColor,
|
||||
imageVector = Icons.Filled.Security,
|
||||
contentDescription = null
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(6.dp)
|
||||
) {
|
||||
LabelItem(
|
||||
icon = if (Natives.isSafeMode) {
|
||||
{
|
||||
Icon(
|
||||
tint = labelStyle.contentColor,
|
||||
imageVector = Icons.Filled.Security,
|
||||
contentDescription = null
|
||||
)
|
||||
}
|
||||
} else {
|
||||
null
|
||||
},
|
||||
text = {
|
||||
Text(
|
||||
text = workingMode,
|
||||
style = labelStyle.textStyle.copy(color = labelStyle.contentColor),
|
||||
)
|
||||
}
|
||||
} else {
|
||||
null
|
||||
},
|
||||
text = {
|
||||
Text(
|
||||
text = workingMode,
|
||||
style = labelStyle.textStyle.copy(color = labelStyle.contentColor),
|
||||
)
|
||||
if (isSuCompatDisabled()) {
|
||||
LabelItem(
|
||||
icon = {
|
||||
Icon(
|
||||
tint = labelStyle.contentColor,
|
||||
imageVector = Icons.Filled.Warning,
|
||||
contentDescription = null
|
||||
)
|
||||
},
|
||||
text = {
|
||||
Text(
|
||||
text = stringResource(R.string.sucompat_disabled),
|
||||
style = labelStyle.textStyle.copy(
|
||||
color = labelStyle.contentColor,
|
||||
)
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
) {
|
||||
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
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
kernelVersion.isGKI() -> {
|
||||
Icon(Icons.Filled.Report, stringResource(R.string.home_not_installed))
|
||||
Icon(Icons.Filled.NewReleases, stringResource(R.string.home_not_installed))
|
||||
Column(Modifier.padding(start = 20.dp)) {
|
||||
Text(
|
||||
text = stringResource(R.string.home_not_installed),
|
||||
@@ -399,7 +502,7 @@ private fun StatusCard(
|
||||
}
|
||||
|
||||
else -> {
|
||||
Icon(Icons.Filled.Dangerous, stringResource(R.string.home_failure))
|
||||
Icon(Icons.Filled.Cancel, stringResource(R.string.home_failure))
|
||||
Column(Modifier.padding(start = 20.dp)) {
|
||||
Text(
|
||||
text = stringResource(R.string.home_failure),
|
||||
@@ -450,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
|
||||
@@ -460,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) {
|
||||
@@ -482,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)
|
||||
)
|
||||
}
|
||||
@@ -497,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),
|
||||
)
|
||||
|
||||
@@ -531,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(),
|
||||
@@ -685,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) }
|
||||
|
||||
@@ -9,10 +9,14 @@ import android.util.Log
|
||||
import android.widget.Toast
|
||||
import androidx.activity.compose.rememberLauncherForActivityResult
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.animation.expandVertically
|
||||
import androidx.compose.animation.shrinkVertically
|
||||
import androidx.compose.animation.fadeIn
|
||||
import androidx.compose.animation.fadeOut
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.LocalIndication
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
@@ -73,6 +77,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.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
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
@@ -84,14 +95,12 @@ import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.semantics.Role
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.text.style.TextDecoration
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
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
|
||||
@@ -119,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)
|
||||
@@ -161,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 = "" },
|
||||
@@ -286,58 +324,58 @@ fun ModuleScreen(navigator: DestinationsNavigator) {
|
||||
},
|
||||
floatingActionButton = {
|
||||
if (!hideInstallButton) {
|
||||
val moduleInstall = stringResource(id = R.string.module_install)
|
||||
val confirmTitle = stringResource(R.string.module)
|
||||
var zipUris by remember { mutableStateOf<List<Uri>>(emptyList()) }
|
||||
val confirmDialog = rememberConfirmDialog(onConfirm = {
|
||||
navigator.navigate(FlashScreenDestination(FlashIt.FlashModules(zipUris)))
|
||||
viewModel.markNeedRefresh()
|
||||
})
|
||||
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()
|
||||
}
|
||||
|
||||
// Show confirm dialog with selected zip file(s) name(s)
|
||||
val moduleNames =
|
||||
uris.mapIndexed { index, uri -> "\n${index + 1}. ${uri.getFileName(context)}" }
|
||||
.joinToString("")
|
||||
val confirmContent =
|
||||
context.getString(R.string.module_install_prompt_with_name, moduleNames)
|
||||
zipUris = uris
|
||||
confirmDialog.showConfirm(
|
||||
title = confirmTitle,
|
||||
content = confirmContent,
|
||||
markdown = true
|
||||
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),
|
||||
@@ -370,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
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -408,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)
|
||||
@@ -419,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)
|
||||
@@ -437,6 +468,8 @@ private fun ModuleList(
|
||||
val loadingDialog = rememberLoadingDialog()
|
||||
val confirmDialog = rememberConfirmDialog()
|
||||
|
||||
var expandedModuleId by rememberSaveable { mutableStateOf<String?>(null) }
|
||||
|
||||
suspend fun onModuleUpdate(
|
||||
module: ModuleViewModel.ModuleInfo,
|
||||
changelogUrl: String,
|
||||
@@ -570,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() -> {
|
||||
@@ -601,7 +637,6 @@ private fun ModuleList(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
else -> {
|
||||
items(viewModel.moduleList) { module ->
|
||||
val scope = rememberCoroutineScope()
|
||||
@@ -659,6 +694,10 @@ private fun ModuleList(
|
||||
},
|
||||
onClick = {
|
||||
onClickModule(it.dirId, it.name, it.hasWebUi)
|
||||
},
|
||||
expanded = expandedModuleId == module.id,
|
||||
onExpandToggle = {
|
||||
expandedModuleId = if (expandedModuleId == module.id) null else module.id
|
||||
}
|
||||
)
|
||||
|
||||
@@ -670,7 +709,6 @@ private fun ModuleList(
|
||||
}
|
||||
|
||||
DownloadListener(context, onInstallModule)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -684,9 +722,18 @@ fun ModuleItem(
|
||||
onCheckChanged: (Boolean) -> Unit,
|
||||
onUpdate: (ModuleViewModel.ModuleInfo) -> Unit,
|
||||
onClick: (ModuleViewModel.ModuleInfo) -> Unit,
|
||||
expanded: Boolean,
|
||||
onExpandToggle: () -> Unit,
|
||||
) {
|
||||
val viewModel = viewModel<ModuleViewModel>()
|
||||
|
||||
ElevatedCard(
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.clip(MaterialTheme.shapes.medium)
|
||||
.clickable(
|
||||
onClick = onExpandToggle
|
||||
)
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
@@ -694,11 +741,10 @@ fun ModuleItem(
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
val prefs = context.getSharedPreferences("settings", Context.MODE_PRIVATE)
|
||||
val useLagacyUI = prefs.getBoolean("use_legacyui", false)
|
||||
|
||||
val useBanner = prefs.getBoolean("use_banner", true)
|
||||
|
||||
if (useBanner && !useLagacyUI && module.banner.isNotEmpty()) {
|
||||
if (useBanner && module.banner.isNotEmpty()) {
|
||||
val isDark = isSystemInDarkTheme()
|
||||
val colorScheme = MaterialTheme.colorScheme
|
||||
val context = LocalContext.current
|
||||
@@ -769,10 +815,7 @@ fun ModuleItem(
|
||||
}
|
||||
}
|
||||
Column {
|
||||
val textDecoration = if (!module.remove) null else TextDecoration.LineThrough
|
||||
val interactionSource = remember { MutableInteractionSource() }
|
||||
val indication = LocalIndication.current
|
||||
val viewModel = viewModel<ModuleViewModel>()
|
||||
|
||||
var developerOptionsEnabled by rememberSaveable {
|
||||
mutableStateOf(
|
||||
@@ -780,116 +823,188 @@ fun ModuleItem(
|
||||
)
|
||||
}
|
||||
|
||||
val filterZygiskModules = Natives.isZygiskEnabled() || !module.zygiskRequired
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
developerOptionsEnabled = prefs.getBoolean("enable_developer_options", false)
|
||||
}
|
||||
|
||||
if (useLagacyUI) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.padding(22.dp, 18.dp, 22.dp, 12.dp)
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.padding(22.dp, 18.dp, 22.dp, 12.dp)
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
val moduleVersion = stringResource(id = R.string.module_version)
|
||||
val moduleAuthor = stringResource(id = R.string.module_author)
|
||||
val moduleId = stringResource(id = R.string.module_id)
|
||||
val moduleVersionCode = stringResource(id = R.string.module_version_code)
|
||||
val moduleUpdateJson = stringResource(id = R.string.module_update_json)
|
||||
val moduleUpdateJsonEmpty = stringResource(id = R.string.module_update_json_empty)
|
||||
|
||||
Column(
|
||||
modifier = Modifier.fillMaxWidth(0.8f)
|
||||
) {
|
||||
val moduleVersion = stringResource(id = R.string.module_version)
|
||||
val moduleAuthor = stringResource(id = R.string.module_author)
|
||||
val moduleId = stringResource(id = R.string.module_id)
|
||||
val moduleVersionCode = stringResource(id = R.string.module_version_code)
|
||||
val moduleUpdateJson = stringResource(id = R.string.module_update_json)
|
||||
val moduleUpdateJsonEmpty = stringResource(id = R.string.module_update_json_empty)
|
||||
|
||||
Column(
|
||||
modifier = Modifier.fillMaxWidth(0.8f)
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(6.dp)
|
||||
) {
|
||||
Text(
|
||||
text = module.name,
|
||||
fontSize = MaterialTheme.typography.titleMedium.fontSize,
|
||||
fontWeight = FontWeight.SemiBold,
|
||||
lineHeight = MaterialTheme.typography.bodySmall.lineHeight,
|
||||
fontFamily = MaterialTheme.typography.titleMedium.fontFamily,
|
||||
textDecoration = textDecoration,
|
||||
)
|
||||
|
||||
Text(
|
||||
text = "$moduleVersion: ${module.version}",
|
||||
fontSize = MaterialTheme.typography.bodySmall.fontSize,
|
||||
lineHeight = MaterialTheme.typography.bodySmall.lineHeight,
|
||||
fontFamily = MaterialTheme.typography.bodySmall.fontFamily,
|
||||
textDecoration = textDecoration
|
||||
)
|
||||
|
||||
Text(
|
||||
text = "$moduleAuthor: ${module.author}",
|
||||
fontSize = MaterialTheme.typography.bodySmall.fontSize,
|
||||
lineHeight = MaterialTheme.typography.bodySmall.lineHeight,
|
||||
fontFamily = MaterialTheme.typography.bodySmall.fontFamily,
|
||||
textDecoration = textDecoration
|
||||
)
|
||||
|
||||
if (developerOptionsEnabled) {
|
||||
|
||||
Text(
|
||||
text = "$moduleId: ${module.id}",
|
||||
fontSize = MaterialTheme.typography.bodySmall.fontSize,
|
||||
lineHeight = MaterialTheme.typography.bodySmall.lineHeight,
|
||||
fontFamily = MaterialTheme.typography.bodySmall.fontFamily,
|
||||
textDecoration = textDecoration
|
||||
LabelItem(
|
||||
text = formatSize(module.size),
|
||||
style = LabelItemDefaults.style.copy(
|
||||
containerColor = MaterialTheme.colorScheme.secondaryContainer,
|
||||
contentColor = MaterialTheme.colorScheme.onSecondaryContainer
|
||||
)
|
||||
|
||||
Text(
|
||||
text = "$moduleVersionCode: ${module.versionCode}",
|
||||
fontSize = MaterialTheme.typography.bodySmall.fontSize,
|
||||
lineHeight = MaterialTheme.typography.bodySmall.lineHeight,
|
||||
fontFamily = MaterialTheme.typography.bodySmall.fontFamily,
|
||||
textDecoration = textDecoration
|
||||
)
|
||||
if (module.remove) {
|
||||
LabelItem(
|
||||
text = stringResource(R.string.uninstalled),
|
||||
style = LabelItemDefaults.style.copy(
|
||||
containerColor = MaterialTheme.colorScheme.errorContainer,
|
||||
contentColor = MaterialTheme.colorScheme.onErrorContainer
|
||||
)
|
||||
)
|
||||
|
||||
Text(
|
||||
text = if (module.updateJson.isNotEmpty()) "$moduleUpdateJson: ${module.updateJson}" else "$moduleUpdateJson: $moduleUpdateJsonEmpty",
|
||||
fontSize = MaterialTheme.typography.bodySmall.fontSize,
|
||||
lineHeight = MaterialTheme.typography.bodySmall.lineHeight,
|
||||
fontFamily = MaterialTheme.typography.bodySmall.fontFamily,
|
||||
textDecoration = textDecoration
|
||||
}
|
||||
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
|
||||
)
|
||||
)
|
||||
}
|
||||
if (updateUrl.isNotEmpty() && !module.remove && !module.update) {
|
||||
LabelItem(
|
||||
text = stringResource(R.string.module_update_available),
|
||||
style = LabelItemDefaults.style.copy(
|
||||
containerColor = MaterialTheme.colorScheme.tertiaryContainer,
|
||||
contentColor = MaterialTheme.colorScheme.onTertiaryContainer
|
||||
)
|
||||
)
|
||||
}
|
||||
if (!module.remove) {
|
||||
if (module.update) {
|
||||
LabelItem(
|
||||
text = stringResource(R.string.module_updated),
|
||||
style = LabelItemDefaults.style.copy(
|
||||
containerColor = MaterialTheme.colorScheme.tertiaryContainer,
|
||||
contentColor = MaterialTheme.colorScheme.onTertiaryContainer
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
if (module.enabled && !module.remove) {
|
||||
if (module.hasWebUi && filterZygiskModules) {
|
||||
LabelItem(
|
||||
text = stringResource(R.string.webui),
|
||||
style = LabelItemDefaults.style.copy(
|
||||
containerColor = MaterialTheme.colorScheme.primaryContainer,
|
||||
contentColor = MaterialTheme.colorScheme.onPrimaryContainer
|
||||
)
|
||||
)
|
||||
}
|
||||
if (module.hasActionScript && filterZygiskModules) {
|
||||
LabelItem(
|
||||
text = stringResource(R.string.action),
|
||||
style = LabelItemDefaults.style.copy(
|
||||
containerColor = MaterialTheme.colorScheme.secondaryContainer,
|
||||
contentColor = MaterialTheme.colorScheme.onSecondaryContainer
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.weight(1f))
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.End,
|
||||
) {
|
||||
Switch(
|
||||
enabled = !module.update,
|
||||
checked = module.enabled,
|
||||
onCheckedChange = onCheckChanged,
|
||||
interactionSource = if (!module.hasWebUi) interactionSource else null
|
||||
Text(
|
||||
text = module.name,
|
||||
fontSize = MaterialTheme.typography.titleMedium.fontSize,
|
||||
fontWeight = FontWeight.SemiBold,
|
||||
lineHeight = MaterialTheme.typography.bodySmall.lineHeight,
|
||||
fontFamily = MaterialTheme.typography.titleMedium.fontFamily
|
||||
)
|
||||
|
||||
Text(
|
||||
text = "$moduleVersion: ${module.version}",
|
||||
fontSize = MaterialTheme.typography.bodySmall.fontSize,
|
||||
lineHeight = MaterialTheme.typography.bodySmall.lineHeight,
|
||||
fontFamily = MaterialTheme.typography.bodySmall.fontFamily
|
||||
)
|
||||
|
||||
Text(
|
||||
text = "$moduleAuthor: ${module.author}",
|
||||
fontSize = MaterialTheme.typography.bodySmall.fontSize,
|
||||
lineHeight = MaterialTheme.typography.bodySmall.lineHeight,
|
||||
fontFamily = MaterialTheme.typography.bodySmall.fontFamily
|
||||
)
|
||||
|
||||
if (developerOptionsEnabled) {
|
||||
|
||||
Text(
|
||||
text = "$moduleId: ${module.id}",
|
||||
fontSize = MaterialTheme.typography.bodySmall.fontSize,
|
||||
lineHeight = MaterialTheme.typography.bodySmall.lineHeight,
|
||||
fontFamily = MaterialTheme.typography.bodySmall.fontFamily
|
||||
)
|
||||
|
||||
Text(
|
||||
text = "$moduleVersionCode: ${module.versionCode}",
|
||||
fontSize = MaterialTheme.typography.bodySmall.fontSize,
|
||||
lineHeight = MaterialTheme.typography.bodySmall.lineHeight,
|
||||
fontFamily = MaterialTheme.typography.bodySmall.fontFamily
|
||||
)
|
||||
|
||||
Text(
|
||||
text = if (module.updateJson.isNotEmpty()) "$moduleUpdateJson: ${module.updateJson}" else "$moduleUpdateJson: $moduleUpdateJsonEmpty",
|
||||
fontSize = MaterialTheme.typography.bodySmall.fontSize,
|
||||
lineHeight = MaterialTheme.typography.bodySmall.lineHeight,
|
||||
fontFamily = MaterialTheme.typography.bodySmall.fontFamily
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(12.dp))
|
||||
Spacer(modifier = Modifier.weight(1f))
|
||||
|
||||
Text(
|
||||
text = module.description,
|
||||
fontSize = MaterialTheme.typography.bodySmall.fontSize,
|
||||
fontFamily = MaterialTheme.typography.bodySmall.fontFamily,
|
||||
lineHeight = MaterialTheme.typography.bodySmall.lineHeight,
|
||||
fontWeight = MaterialTheme.typography.bodySmall.fontWeight,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
maxLines = 4,
|
||||
textDecoration = textDecoration
|
||||
)
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.End,
|
||||
) {
|
||||
Switch(
|
||||
enabled = !module.update,
|
||||
checked = module.enabled,
|
||||
onCheckedChange = onCheckChanged,
|
||||
interactionSource = if (!module.hasWebUi) interactionSource else null
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
Spacer(modifier = Modifier.height(12.dp))
|
||||
|
||||
HorizontalDivider(thickness = Dp.Hairline)
|
||||
Text(
|
||||
text = module.description,
|
||||
fontSize = MaterialTheme.typography.bodySmall.fontSize,
|
||||
fontFamily = MaterialTheme.typography.bodySmall.fontFamily,
|
||||
lineHeight = MaterialTheme.typography.bodySmall.lineHeight,
|
||||
fontWeight = MaterialTheme.typography.bodySmall.fontWeight,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
maxLines = 4
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(4.dp))
|
||||
Spacer(modifier = Modifier.height(2.dp))
|
||||
|
||||
if (expanded) {
|
||||
Spacer(modifier = Modifier.height(10.dp))
|
||||
}
|
||||
|
||||
AnimatedVisibility(
|
||||
visible = expanded,
|
||||
enter = fadeIn() + expandVertically(),
|
||||
exit = shrinkVertically() + fadeOut()
|
||||
) {
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
@@ -897,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()
|
||||
@@ -906,7 +1021,7 @@ fun ModuleItem(
|
||||
) {
|
||||
Icon(
|
||||
modifier = Modifier.size(20.dp),
|
||||
imageVector = Icons.Outlined.PlayArrow,
|
||||
imageVector = Icons.Outlined.Terminal,
|
||||
contentDescription = null
|
||||
)
|
||||
if (!module.hasWebUi && updateUrl.isEmpty()) {
|
||||
@@ -925,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
|
||||
@@ -948,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,
|
||||
@@ -1018,254 +1133,6 @@ fun ModuleItem(
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.padding(22.dp, 18.dp, 22.dp, 12.dp)
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.SpaceBetween
|
||||
) {
|
||||
val moduleVersion = stringResource(id = R.string.module_version)
|
||||
val moduleAuthor = stringResource(id = R.string.module_author)
|
||||
val moduleId = stringResource(id = R.string.module_id)
|
||||
val moduleVersionCode = stringResource(id = R.string.module_version_code)
|
||||
val moduleUpdateJson = stringResource(id = R.string.module_update_json)
|
||||
val moduleUpdateJsonEmpty = stringResource(id = R.string.module_update_json_empty)
|
||||
|
||||
Column(
|
||||
modifier = Modifier.fillMaxWidth(0.8f)
|
||||
) {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(6.dp)
|
||||
) {
|
||||
LabelItem(
|
||||
text = formatSize(module.size),
|
||||
style = com.dergoogler.mmrl.ui.component.LabelItemDefaults.style.copy(
|
||||
containerColor = MaterialTheme.colorScheme.secondaryContainer,
|
||||
contentColor = MaterialTheme.colorScheme.onSecondaryContainer
|
||||
)
|
||||
)
|
||||
LabelItem(
|
||||
text = if (module.enabled) stringResource(R.string.enabled) else stringResource(R.string.disabled),
|
||||
style = if (module.enabled)
|
||||
com.dergoogler.mmrl.ui.component.LabelItemDefaults.style.copy()
|
||||
else
|
||||
com.dergoogler.mmrl.ui.component.LabelItemDefaults.style.copy(
|
||||
containerColor = MaterialTheme.colorScheme.errorContainer,
|
||||
contentColor = MaterialTheme.colorScheme.onErrorContainer
|
||||
)
|
||||
)
|
||||
if (module.remove) {
|
||||
LabelItem(
|
||||
text = stringResource(R.string.uninstalled),
|
||||
style = com.dergoogler.mmrl.ui.component.LabelItemDefaults.style.copy(
|
||||
containerColor = MaterialTheme.colorScheme.errorContainer,
|
||||
contentColor = MaterialTheme.colorScheme.onErrorContainer
|
||||
)
|
||||
)
|
||||
}
|
||||
if (updateUrl.isNotEmpty() && !module.remove && !module.update) {
|
||||
LabelItem(
|
||||
text = stringResource(R.string.module_update),
|
||||
style = com.dergoogler.mmrl.ui.component.LabelItemDefaults.style.copy(
|
||||
containerColor = MaterialTheme.colorScheme.onTertiary,
|
||||
contentColor = MaterialTheme.colorScheme.onTertiaryContainer
|
||||
)
|
||||
)
|
||||
}
|
||||
if (!module.remove) {
|
||||
if (module.update) {
|
||||
LabelItem(
|
||||
text = stringResource(R.string.module_updated),
|
||||
style = com.dergoogler.mmrl.ui.component.LabelItemDefaults.style.copy(
|
||||
containerColor = MaterialTheme.colorScheme.tertiaryContainer,
|
||||
contentColor = MaterialTheme.colorScheme.onTertiaryContainer
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
if (!module.remove) {
|
||||
if (module.hasWebUi) {
|
||||
LabelItem(
|
||||
text = stringResource(R.string.webui),
|
||||
style = com.dergoogler.mmrl.ui.component.LabelItemDefaults.style.copy(
|
||||
containerColor = MaterialTheme.colorScheme.primaryContainer,
|
||||
contentColor = MaterialTheme.colorScheme.onPrimaryContainer
|
||||
)
|
||||
)
|
||||
}
|
||||
if (module.hasActionScript) {
|
||||
LabelItem(
|
||||
text = stringResource(R.string.action),
|
||||
style = com.dergoogler.mmrl.ui.component.LabelItemDefaults.style.copy(
|
||||
containerColor = MaterialTheme.colorScheme.secondaryContainer,
|
||||
contentColor = MaterialTheme.colorScheme.onSecondaryContainer
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
|
||||
Text(
|
||||
text = module.name,
|
||||
fontSize = MaterialTheme.typography.titleMedium.fontSize,
|
||||
fontWeight = FontWeight.SemiBold,
|
||||
lineHeight = MaterialTheme.typography.bodySmall.lineHeight,
|
||||
fontFamily = MaterialTheme.typography.titleMedium.fontFamily,
|
||||
textDecoration = textDecoration,
|
||||
)
|
||||
|
||||
Text(
|
||||
text = "$moduleVersion: ${module.version}",
|
||||
fontSize = MaterialTheme.typography.bodySmall.fontSize,
|
||||
lineHeight = MaterialTheme.typography.bodySmall.lineHeight,
|
||||
fontFamily = MaterialTheme.typography.bodySmall.fontFamily,
|
||||
textDecoration = textDecoration
|
||||
)
|
||||
|
||||
Text(
|
||||
text = "$moduleAuthor: ${module.author}",
|
||||
fontSize = MaterialTheme.typography.bodySmall.fontSize,
|
||||
lineHeight = MaterialTheme.typography.bodySmall.lineHeight,
|
||||
fontFamily = MaterialTheme.typography.bodySmall.fontFamily,
|
||||
textDecoration = textDecoration
|
||||
)
|
||||
|
||||
if (developerOptionsEnabled) {
|
||||
|
||||
Text(
|
||||
text = "$moduleId: ${module.id}",
|
||||
fontSize = MaterialTheme.typography.bodySmall.fontSize,
|
||||
lineHeight = MaterialTheme.typography.bodySmall.lineHeight,
|
||||
fontFamily = MaterialTheme.typography.bodySmall.fontFamily,
|
||||
textDecoration = textDecoration
|
||||
)
|
||||
|
||||
Text(
|
||||
text = "$moduleVersionCode: ${module.versionCode}",
|
||||
fontSize = MaterialTheme.typography.bodySmall.fontSize,
|
||||
lineHeight = MaterialTheme.typography.bodySmall.lineHeight,
|
||||
fontFamily = MaterialTheme.typography.bodySmall.fontFamily,
|
||||
textDecoration = textDecoration
|
||||
)
|
||||
|
||||
Text(
|
||||
text = if (module.updateJson.isNotEmpty()) "$moduleUpdateJson: ${module.updateJson}" else "$moduleUpdateJson: $moduleUpdateJsonEmpty",
|
||||
fontSize = MaterialTheme.typography.bodySmall.fontSize,
|
||||
lineHeight = MaterialTheme.typography.bodySmall.lineHeight,
|
||||
fontFamily = MaterialTheme.typography.bodySmall.fontFamily,
|
||||
textDecoration = textDecoration
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.End,
|
||||
) {
|
||||
var expanded by remember { mutableStateOf(false) }
|
||||
IconButton(onClick = { expanded = true }) {
|
||||
Icon(
|
||||
Icons.Filled.MoreVert,
|
||||
contentDescription = "Module actions"
|
||||
)
|
||||
}
|
||||
DropdownMenu(
|
||||
expanded = expanded,
|
||||
onDismissRequest = { expanded = false }
|
||||
) {
|
||||
if (updateUrl.isNotEmpty() && !module.remove) {
|
||||
DropdownMenuItem(
|
||||
text = { Text(stringResource(R.string.module_update)) },
|
||||
onClick = {
|
||||
expanded = false
|
||||
onUpdate(module)
|
||||
}
|
||||
)
|
||||
HorizontalDivider()
|
||||
}
|
||||
|
||||
if (!module.remove) {
|
||||
if (module.hasWebUi) {
|
||||
DropdownMenuItem(
|
||||
text = { Text(stringResource(R.string.webui)) },
|
||||
onClick = {
|
||||
expanded = false
|
||||
onClick(module)
|
||||
}
|
||||
)
|
||||
}
|
||||
if (module.hasActionScript) {
|
||||
DropdownMenuItem(
|
||||
text = { Text(stringResource(R.string.action)) },
|
||||
onClick = {
|
||||
expanded = false
|
||||
navigator.navigate(ExecuteModuleActionScreenDestination(module.dirId))
|
||||
viewModel.markNeedRefresh()
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
if (module.hasWebUi || module.hasActionScript ) {
|
||||
HorizontalDivider()
|
||||
}
|
||||
}
|
||||
|
||||
if (!module.remove) {
|
||||
DropdownMenuItem(
|
||||
text = {
|
||||
Text(
|
||||
if (module.enabled) stringResource(R.string.disable)
|
||||
else stringResource(R.string.enable)
|
||||
)
|
||||
},
|
||||
onClick = {
|
||||
expanded = false
|
||||
onCheckChanged(!module.enabled)
|
||||
}
|
||||
)
|
||||
}
|
||||
if (module.remove) {
|
||||
DropdownMenuItem(
|
||||
text = { Text(stringResource(R.string.restore)) },
|
||||
onClick = {
|
||||
expanded = false
|
||||
onRestore(module)
|
||||
}
|
||||
)
|
||||
} else {
|
||||
DropdownMenuItem(
|
||||
text = { Text(stringResource(R.string.uninstall)) },
|
||||
onClick = {
|
||||
expanded = false
|
||||
onUninstall(module)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(12.dp))
|
||||
|
||||
Text(
|
||||
text = module.description,
|
||||
fontSize = MaterialTheme.typography.bodySmall.fontSize,
|
||||
fontFamily = MaterialTheme.typography.bodySmall.fontFamily,
|
||||
lineHeight = MaterialTheme.typography.bodySmall.lineHeight,
|
||||
fontWeight = MaterialTheme.typography.bodySmall.fontWeight,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
maxLines = 4,
|
||||
textDecoration = textDecoration
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(6.dp))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1273,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
|
||||
@@ -1302,7 +1170,8 @@ fun ModuleItemPreview() {
|
||||
hasActionScript = false,
|
||||
dirId = "dirId",
|
||||
size = 12345678L,
|
||||
banner = ""
|
||||
banner = "",
|
||||
zygiskRequired = false
|
||||
)
|
||||
ModuleItem(EmptyDestinationsNavigator, module, "", {}, {}, {}, {}, {})
|
||||
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
|
||||
@@ -67,6 +68,7 @@ import com.ramcosta.composedestinations.generated.destinations.AppProfileTemplat
|
||||
import com.ramcosta.composedestinations.generated.destinations.FlashScreenDestination
|
||||
import com.ramcosta.composedestinations.generated.destinations.BackupRestoreScreenDestination
|
||||
import com.ramcosta.composedestinations.generated.destinations.CustomizationScreenDestination
|
||||
import com.ramcosta.composedestinations.generated.destinations.DeveloperScreenDestination
|
||||
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
|
||||
import com.ramcosta.composedestinations.navigation.EmptyDestinationsNavigator
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
@@ -148,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)
|
||||
@@ -224,9 +230,11 @@ fun SettingScreen(navigator: DestinationsNavigator) {
|
||||
}
|
||||
|
||||
var useOverlayFs by rememberSaveable {
|
||||
mutableStateOf(
|
||||
prefs.getBoolean("use_overlay_fs", false)
|
||||
)
|
||||
mutableStateOf(readMountSystemFile())
|
||||
}
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
useOverlayFs = readMountSystemFile()
|
||||
}
|
||||
|
||||
var showRebootDialog by remember { mutableStateOf(false) }
|
||||
@@ -244,8 +252,10 @@ fun SettingScreen(navigator: DestinationsNavigator) {
|
||||
useOverlayFs = it
|
||||
if (useOverlayFs) {
|
||||
moduleBackup()
|
||||
updateMountSystemFile(true)
|
||||
} else {
|
||||
moduleMigration()
|
||||
updateMountSystemFile(false)
|
||||
}
|
||||
if (isManager) install()
|
||||
showRebootDialog = true
|
||||
@@ -255,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 = {
|
||||
@@ -289,78 +303,6 @@ fun SettingScreen(navigator: DestinationsNavigator) {
|
||||
checkUpdate = it
|
||||
}
|
||||
|
||||
var enableWebDebugging by rememberSaveable {
|
||||
mutableStateOf(
|
||||
prefs.getBoolean("enable_web_debugging", false)
|
||||
)
|
||||
}
|
||||
|
||||
if (ksuVersion != null) {
|
||||
SwitchItem(
|
||||
icon = Icons.Filled.Web,
|
||||
title = stringResource(id = R.string.enable_web_debugging),
|
||||
summary = stringResource(id = R.string.enable_web_debugging_summary),
|
||||
checked = enableWebDebugging
|
||||
) {
|
||||
prefs.edit().putBoolean("enable_web_debugging", it).apply()
|
||||
enableWebDebugging = it
|
||||
}
|
||||
}
|
||||
|
||||
var developerOptionsEnabled by rememberSaveable {
|
||||
mutableStateOf(
|
||||
prefs.getBoolean("enable_developer_options", false)
|
||||
)
|
||||
}
|
||||
if (ksuVersion != null) {
|
||||
SwitchItem(
|
||||
icon = Icons.Filled.DeveloperMode,
|
||||
title = stringResource(id = R.string.enable_developer_options),
|
||||
summary = stringResource(id = R.string.enable_developer_options_summary),
|
||||
checked = developerOptionsEnabled
|
||||
) {
|
||||
prefs.edit().putBoolean("enable_developer_options", it).apply()
|
||||
developerOptionsEnabled = it
|
||||
}
|
||||
}
|
||||
|
||||
var useWebUIX by rememberSaveable {
|
||||
mutableStateOf(
|
||||
prefs.getBoolean("use_webuix", true)
|
||||
)
|
||||
}
|
||||
if (ksuVersion != null) {
|
||||
SwitchItem(
|
||||
beta = false,
|
||||
enabled = Platform.isAlive,
|
||||
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 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
|
||||
}
|
||||
}
|
||||
|
||||
if (isOverlayAvailable && useOverlayFs) {
|
||||
val shrink = stringResource(id = R.string.shrink_sparse_image)
|
||||
val shrinkMessage = stringResource(id = R.string.shrink_sparse_image_message)
|
||||
@@ -371,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)
|
||||
@@ -393,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)
|
||||
}
|
||||
@@ -408,13 +358,37 @@ 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)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
val developer = stringResource(id = R.string.developer)
|
||||
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) {
|
||||
UninstallItem(navigator) {
|
||||
@@ -431,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
|
||||
}
|
||||
@@ -539,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()
|
||||
}
|
||||
@@ -589,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()
|
||||
}
|
||||
@@ -656,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
|
||||
@@ -54,23 +54,14 @@ fun SuperUserScreen(navigator: DestinationsNavigator) {
|
||||
}
|
||||
}
|
||||
|
||||
LaunchedEffect(viewModel.search) {
|
||||
if (viewModel.search.isEmpty()) {
|
||||
listState.scrollToItem(0)
|
||||
}
|
||||
}
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
if (viewModel.refreshOnReturn) {
|
||||
viewModel.fetchAppList()
|
||||
viewModel.refreshOnReturn = false
|
||||
}
|
||||
}
|
||||
|
||||
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 = "" },
|
||||
@@ -105,7 +96,7 @@ fun SuperUserScreen(navigator: DestinationsNavigator) {
|
||||
}
|
||||
)
|
||||
}, onClick = {
|
||||
viewModel.showSystemApps = !viewModel.showSystemApps
|
||||
viewModel.updateShowSystemApps(!viewModel.showSystemApps)
|
||||
showDropdown = false
|
||||
})
|
||||
}
|
||||
@@ -131,7 +122,6 @@ fun SuperUserScreen(navigator: DestinationsNavigator) {
|
||||
) {
|
||||
items(viewModel.appList, key = { it.packageName + it.uid }) { app ->
|
||||
AppItem(app) {
|
||||
viewModel.refreshOnReturn = true
|
||||
navigator.navigate(AppProfileScreenDestination(app))
|
||||
}
|
||||
}
|
||||
@@ -148,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))
|
||||
|
||||
@@ -177,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"
|
||||
@@ -37,10 +38,16 @@ private fun ksuDaemonOverlayfsPath(): String {
|
||||
return ksuApp.applicationInfo.nativeLibraryDir + File.separator + "libksud_overlayfs.so"
|
||||
}
|
||||
|
||||
fun readMountSystemFile(): Boolean {
|
||||
val shell = getRootShell()
|
||||
val filePath = "/data/adb/ksu/mount_system"
|
||||
val result = ShellUtils.fastCmd(shell, "cat $filePath").trim()
|
||||
return result == "OVERLAYFS"
|
||||
}
|
||||
|
||||
// Get the path based on the user's choice
|
||||
fun getKsuDaemonPath(): String {
|
||||
val prefs = ksuApp.getSharedPreferences("settings", Context.MODE_PRIVATE)
|
||||
val useOverlayFs = prefs.getBoolean("use_overlay_fs", false)
|
||||
val useOverlayFs = readMountSystemFile()
|
||||
|
||||
return if (useOverlayFs) {
|
||||
ksuDaemonOverlayfsPath()
|
||||
@@ -49,6 +56,16 @@ fun getKsuDaemonPath(): String {
|
||||
}
|
||||
}
|
||||
|
||||
fun updateMountSystemFile(useOverlayFs: Boolean) {
|
||||
val shell = getRootShell()
|
||||
val filePath = "/data/adb/ksu/mount_system"
|
||||
if (useOverlayFs) {
|
||||
ShellUtils.fastCmd(shell, "echo -n OVERLAYFS > $filePath")
|
||||
} else {
|
||||
ShellUtils.fastCmd(shell, "echo -n MAGIC_MOUNT > $filePath")
|
||||
}
|
||||
}
|
||||
|
||||
data class FlashResult(val code: Int, val err: String, val showReboot: Boolean) {
|
||||
constructor(result: Shell.Result, showReboot: Boolean) : this(result.code, result.err.joinToString("\n"), showReboot)
|
||||
constructor(result: Shell.Result) : this(result, result.isSuccess)
|
||||
@@ -89,9 +106,9 @@ fun createRootShell(globalMnt: Boolean = false): Shell {
|
||||
val builder = Shell.Builder.create()
|
||||
return try {
|
||||
if (globalMnt) {
|
||||
builder.build(getKsuDaemonPath(), "debug", "su", "-g")
|
||||
builder.build(ksuDaemonMagicPath(), "debug", "su", "-g")
|
||||
} else {
|
||||
builder.build(getKsuDaemonPath(), "debug", "su")
|
||||
builder.build(ksuDaemonMagicPath(), "debug", "su")
|
||||
}
|
||||
} catch (e: Throwable) {
|
||||
Log.w(TAG, "ksu failed: ", e)
|
||||
@@ -474,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
|
||||
|
||||
@@ -495,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()
|
||||
}
|
||||
@@ -517,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
|
||||
|
||||
@@ -540,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()
|
||||
}
|
||||
@@ -607,11 +624,23 @@ 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
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.rifsxd.ksunext.ui.viewmodel
|
||||
|
||||
import android.net.Uri
|
||||
import android.os.SystemClock
|
||||
import android.util.Log
|
||||
import androidx.compose.runtime.derivedStateOf
|
||||
@@ -8,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
|
||||
@@ -22,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
|
||||
|
||||
@@ -47,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(
|
||||
@@ -93,25 +94,23 @@ class ModuleViewModel : ViewModel() {
|
||||
isNeedRefresh = true
|
||||
}
|
||||
|
||||
var zipUris by mutableStateOf<List<Uri>>(emptyList())
|
||||
|
||||
fun updateZipUris(uris: List<Uri>) {
|
||||
zipUris = uris
|
||||
}
|
||||
|
||||
fun clearZipUris() {
|
||||
zipUris = emptyList()
|
||||
}
|
||||
|
||||
fun fetchModuleList() {
|
||||
|
||||
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
|
||||
|
||||
@@ -128,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,
|
||||
@@ -144,7 +144,8 @@ class ModuleViewModel : ViewModel() {
|
||||
obj.optBoolean("action"),
|
||||
dirId,
|
||||
size,
|
||||
obj.optString("banner")
|
||||
obj.optString("banner"),
|
||||
zygiskRequired
|
||||
)
|
||||
}.toList()
|
||||
isNeedRefresh = false
|
||||
|
||||
@@ -1,7 +1,12 @@
|
||||
package com.rifsxd.ksunext.ui.viewmodel
|
||||
|
||||
import android.content.ComponentName
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.ServiceConnection
|
||||
import android.content.pm.ApplicationInfo
|
||||
import android.content.pm.PackageInfo
|
||||
import android.os.IBinder
|
||||
import android.os.Parcelable
|
||||
import android.os.SystemClock
|
||||
import android.util.Log
|
||||
@@ -11,29 +16,30 @@ 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
|
||||
import java.util.*
|
||||
import kotlin.coroutines.resume
|
||||
import kotlin.coroutines.suspendCoroutine
|
||||
import androidx.core.content.edit
|
||||
|
||||
class SuperUserViewModel : ViewModel() {
|
||||
val isPlatformAlive get() = Platform.isAlive
|
||||
|
||||
var refreshOnReturn by mutableStateOf(false)
|
||||
public set
|
||||
|
||||
companion object {
|
||||
private const val TAG = "SuperUserViewModel"
|
||||
private var apps by mutableStateOf<List<AppInfo>>(emptyList())
|
||||
private var profileOverrides by mutableStateOf<Map<String, Natives.Profile>>(emptyMap())
|
||||
}
|
||||
|
||||
@Parcelize
|
||||
@@ -63,16 +69,26 @@ class SuperUserViewModel : ViewModel() {
|
||||
}
|
||||
}
|
||||
|
||||
private val prefs = ksuApp.getSharedPreferences("settings", Context.MODE_PRIVATE)!!
|
||||
|
||||
var search by mutableStateOf("")
|
||||
var showSystemApps by mutableStateOf(false)
|
||||
var showSystemApps by mutableStateOf(prefs.getBoolean("show_system_apps", false))
|
||||
private set
|
||||
var isRefreshing by mutableStateOf(false)
|
||||
private set
|
||||
|
||||
fun updateShowSystemApps(newValue: Boolean) {
|
||||
showSystemApps = newValue
|
||||
prefs.edit { putBoolean("show_system_apps", newValue) }
|
||||
}
|
||||
|
||||
private val sortedList by derivedStateOf {
|
||||
val comparator = compareBy<AppInfo> {
|
||||
when {
|
||||
it.allowSu -> 0
|
||||
it.hasCustomProfile -> 1
|
||||
it.profile != null && it.profile.allowSu -> 0
|
||||
it.profile != null && (
|
||||
if (it.profile.allowSu) !it.profile.rootUseDefault else !it.profile.nonRootUseDefault
|
||||
) -> 1
|
||||
else -> 2
|
||||
}
|
||||
}.then(compareBy(Collator.getInstance(Locale.getDefault()), AppInfo::label))
|
||||
@@ -82,7 +98,9 @@ class SuperUserViewModel : ViewModel() {
|
||||
}
|
||||
|
||||
val appList by derivedStateOf {
|
||||
sortedList.filter {
|
||||
sortedList.map { app ->
|
||||
profileOverrides[app.packageName]?.let { app.copy(profile = it) } ?: app
|
||||
}.filter {
|
||||
it.label.contains(search, true) || it.packageName.contains(
|
||||
search,
|
||||
true
|
||||
@@ -94,26 +112,62 @@ class SuperUserViewModel : ViewModel() {
|
||||
}
|
||||
}
|
||||
|
||||
fun updateAppProfile(packageName: String, newProfile: Natives.Profile) {
|
||||
profileOverrides = profileOverrides.toMutableMap().apply {
|
||||
put(packageName, newProfile)
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
val result = connectKsuService {
|
||||
Log.w(TAG, "KsuService disconnected")
|
||||
}
|
||||
|
||||
withContext(Dispatchers.IO) {
|
||||
withTimeoutOrNull(TIMEOUT_MILLIS) {
|
||||
while (!isPlatformAlive) {
|
||||
delay(500)
|
||||
}
|
||||
} ?: return@withContext // Exit early if timeout
|
||||
|
||||
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
|
||||
|
||||
@@ -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,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="issue_report_title">Problémája van?</string>
|
||||
<string name="issue_report_body">Hibát észlelt, vagy van visszajelzése?</string>
|
||||
<string name="issue_report_body">Hibát észlelt, vagy visszajelzése van?</string>
|
||||
<string name="issue_report_body_2">Jelentse mielőbb!</string>
|
||||
<string name="issue_report_github">Report on GitHub</string>
|
||||
<string name="issue_report_telegram">Kapcsolatfelvétel Telegramon keresztül</string>
|
||||
@@ -12,13 +12,16 @@
|
||||
<string name="home">Kezdőlap</string>
|
||||
<string name="home_not_installed">Nincs telepítve</string>
|
||||
<string name="home_click_to_install">Kattintson a telepítéshez</string>
|
||||
<string name="lkm_alternative_suggestion">Telepíts GKI kernelt, vagy integráld a KernelSU Next-et az eszközödbe.</string>
|
||||
<string name="home_working">Működik</string>
|
||||
<string name="home_working_version">Verzió: %d</string>
|
||||
<string name="home_superuser_count">Superuserek: %d</string>
|
||||
<string name="home_module_count">Modulok: %d</string>
|
||||
<string name="home_module_update_count">Frissítések: %d</string>
|
||||
<string name="home_failure">KernelSU Next v2 aláírás nem található a kernelben! [ !KSU_NEXT || != size/hash ]</string>
|
||||
<string name="home_failure_tip">Kérje meg kernel fejlesztőjét, hogy integrálja a KernelSU Next-et!</string>
|
||||
<string name="home_kernel">Kernel verzió</string>
|
||||
<string name="disable">Kikapcsolva</string>
|
||||
<string name="enabled">Bekapcsolva</string>
|
||||
<string name="disabled">Kikapcsolva</string>
|
||||
<string name="susfs_supported">Támogatott</string>
|
||||
@@ -38,13 +41,15 @@
|
||||
<string name="module_empty">Nincs telepített modul</string>
|
||||
<string name="module">Modul</string>
|
||||
<string name="module_install_prompt_with_name">A következő modul(ok) lesznek telepítve: %1$s</string>
|
||||
<string name="module_sort_a_to_z">Rendezés (A-Z)</string>
|
||||
<string name="module_sort_z_to_a">Rendezés (Z-A)</string>
|
||||
<string name="module_sort_a_to_z">Rendezés (A → Z)</string>
|
||||
<string name="module_sort_z_to_a">Rendezés (Z → A)</string>
|
||||
<string name="module_size_high_to_low">Rendezés (Magas → Alacsony)</string>
|
||||
<string name="uninstall">Eltávolítás</string>
|
||||
<string name="restore">Visszaállítás</string>
|
||||
<string name="module_install">Telepítés</string>
|
||||
<string name="install">Telepítés</string>
|
||||
<string name="reboot">Újraindítás</string>
|
||||
<string name="uninstalled">Eltávolítva</string>
|
||||
<string name="settings">Beállítások</string>
|
||||
<string name="reboot_userspace">Lágy újraindítás</string>
|
||||
<string name="reboot_recovery">Újraindítás Recovery módba</string>
|
||||
@@ -96,6 +101,7 @@
|
||||
<string name="proceed">Folytatás</string>
|
||||
<string name="cancel">Mégse</string>
|
||||
<string name="later">Később</string>
|
||||
<string name="lkm_warning_message">Az LKM patch zárt forráskódú komponenseken alapul. Folytatja?</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">Next kísérleti branch. Nézze meg a GitHubon!</string>
|
||||
@@ -129,6 +135,7 @@
|
||||
<string name="profile_selinux_domain">Domain</string>
|
||||
<string name="profile_selinux_rules">Szabályok</string>
|
||||
<string name="module_update">Frissítés</string>
|
||||
<string name="module_updated">Frissítve</string>
|
||||
<string name="module_downloading">%s modul letöltése</string>
|
||||
<string name="module_start_downloading">Letöltés indítása: %s</string>
|
||||
<string name="new_version_available">Új verzió (%s) elérhető, kattintson a frissítéshez.</string>
|
||||
@@ -136,6 +143,7 @@
|
||||
<string name="close">Bezár</string>
|
||||
<string name="force_stop_app">Kényszer leállítás</string>
|
||||
<string name="restart_app">Újraindítás</string>
|
||||
<string name="restart_app_message">A módosítás érvénybe lépéséhez újra kell indítani az alkalmazást.</string>
|
||||
<string name="failed_to_update_sepolicy">Nem sikerült frissíteni a SELinux szabályait a következőhöz: %s</string>
|
||||
<string name="su_not_allowed">Superuser megadása nem engedélyezett a következőhöz: %s</string>
|
||||
<string name="module_changelog">Változásnapló</string>
|
||||
@@ -194,4 +202,20 @@
|
||||
<string name="settings_disable_su">Kapcsolja ki a su kompatibilitást</string>
|
||||
<string name="settings_disable_su_summary">Ideiglenesen tiltsa le bármely alkalmazás azon képességét, hogy root jogosultságokat szerezzen a su paranccsal (a már meglévő folyamatokat ez nem érinti).</string>
|
||||
<string name="settings_language">Nyelv</string>
|
||||
<string name="lkm_mode_deprecated">Az LKM mód elavult!</string>
|
||||
<string name="hook_mode">Hook módszer</string>
|
||||
<string name="enable">Bekapcsolva</string>
|
||||
<string name="module_size_low_to_high">Rendezés (Alacsony → Magas)</string>
|
||||
<string name="settings_amoled_mode">AMOLED mód</string>
|
||||
<string name="settings_amoled_mode_summary">Engedélyezzen egy teljesen fekete témát, amely leginkább AMOLED képernyőkön hasznos. Csökkenti a szem megerőltetését és az akkumulátort is kíméli.</string>
|
||||
<string name="restart_required">Újraindítás szükséges</string>
|
||||
<string name="settings_legacyui">Legacy UI használata</string>
|
||||
<string name="settings_legacyui_summary">Váltás az előző felhasználói felületre.</string>
|
||||
<string name="settings_banner">Bannerek engedélyezése</string>
|
||||
<string name="settings_banner_summary">Mutassa a modulok hátterének bannereit.</string>
|
||||
<string name="use_webuix">WebUI X használata</string>
|
||||
<string name="use_webuix_summary">A WebUI X több API-t támogat, mint a WebUI.</string>
|
||||
<string name="use_webuix_eruda">Eruda injektálása a WebUI X-be</string>
|
||||
<string name="use_webuix_eruda_summary">Hibakeresési konzol injektálása a WebUI X-be a hibakeresés megkönnyítése érdekében. Ehhez a webes hibakeresést be kell kapcsolni.</string>
|
||||
<string name="customization">Testreszabás</string>
|
||||
</resources>
|
||||
|
||||
@@ -1,205 +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_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="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">Ordinare (A-Z)</string>
|
||||
<string name="module_sort_z_to_a">Ordinare (Z-A)</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="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="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_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="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="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="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="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,16 +9,22 @@
|
||||
<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>
|
||||
<string name="lkm_alternative_suggestion">Zainstaluj jądro GKI lub zintegruj KernelSU Next ze swoim urządzeniem.</string>
|
||||
<string name="home_working">Uruchomiony</string>
|
||||
<string name="home_working_version">Wersja: %d</string>
|
||||
<string name="home_superuser_count">Superuserów: %d</string>
|
||||
<string name="home_module_count">Modułów: %d</string>
|
||||
<string name="home_module_update_count">Aktualizacji: %d</string>
|
||||
<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">Rodzaj hooków</string>
|
||||
<string name="enable">Aktywuj</string>
|
||||
<string name="disable">Dezaktywuj</string>
|
||||
<string name="enabled">Aktywny</string>
|
||||
<string name="disabled">Nieaktywny</string>
|
||||
<string name="susfs_supported">Obsługiwany</string>
|
||||
@@ -27,6 +33,7 @@
|
||||
<string name="home_susfs_sus_su">SuS SU</string>
|
||||
<string name="home_android">Wersja Androida</string>
|
||||
<string name="home_manager_version">Wersja managera</string>
|
||||
<string name="home_abi">ABI</string>
|
||||
<string name="home_selinux_status">Status SELinux</string>
|
||||
<string name="selinux_status_disabled">Wyłączony</string>
|
||||
<string name="selinux_status_enforcing">Enforcing</string>
|
||||
@@ -38,20 +45,23 @@
|
||||
<string name="module_empty">Brak zainstalowanych modułów</string>
|
||||
<string name="module">Moduły</string>
|
||||
<string name="module_install_prompt_with_name">Następujące moduły zostaną zainstalowane: %1$s</string>
|
||||
<string name="module_sort_a_to_z">Sortuj (A-Z)</string>
|
||||
<string name="module_sort_z_to_a">Sortuj (Z-A)</string>
|
||||
<string name="module_sort_a_to_z">Sortuj (A → Z)</string>
|
||||
<string name="module_sort_z_to_a">Sortuj (Z → A)</string>
|
||||
<string name="module_size_low_to_high">Sortuj (Najmniejszy → Największy)</string>
|
||||
<string name="module_size_high_to_low">Sortuj (Największy → Najmniejszy)</string>
|
||||
<string name="uninstall">Odinstaluj</string>
|
||||
<string name="restore">Przywróć</string>
|
||||
<string name="module_install">Zainstaluj</string>
|
||||
<string name="install">Zainstaluj</string>
|
||||
<string name="reboot">Reboot (uruchom ponownie)</string>
|
||||
<string name="uninstalled">Odinstalowany</string>
|
||||
<string name="settings">Ustawienia</string>
|
||||
<string name="reboot_userspace">Miękki reboot</string>
|
||||
<string name="reboot_recovery">Reboot do trybu Recovery</string>
|
||||
<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>
|
||||
@@ -84,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>
|
||||
@@ -96,6 +106,7 @@
|
||||
<string name="proceed">Dalej</string>
|
||||
<string name="cancel">Anuluj</string>
|
||||
<string name="later">Później</string>
|
||||
<string name="lkm_warning_message">Łatka LKM opiera się na komponentach o zamkniętym kodzie źródłowym. Czy chcesz kontynuować?</string>
|
||||
<string name="home_next_kernelsu">🔥 Kompilacja Next</string>
|
||||
<string name="home_next_kernelsu_repo">https://github.com/KernelSU-Next/KernelSU-Next</string>
|
||||
<string name="home_next_kernelsu_body">Eksperymentalna gałąź Next. Sprawdź na GitHub!</string>
|
||||
@@ -129,6 +140,7 @@
|
||||
<string name="profile_selinux_domain">Domena</string>
|
||||
<string name="profile_selinux_rules">Reguły</string>
|
||||
<string name="module_update">Zaktualizuj</string>
|
||||
<string name="module_updated">Zaktualizowany</string>
|
||||
<string name="module_downloading">Pobieranie modułu: %s</string>
|
||||
<string name="module_start_downloading">Rozpocznij pobieranie: %s</string>
|
||||
<string name="new_version_available">Nowa wersja %s jest dostępna, kliknij, aby zaktualizować.</string>
|
||||
@@ -136,6 +148,10 @@
|
||||
<string name="close">Zamknij</string>
|
||||
<string name="force_stop_app">Wymuś zatrzymanie</string>
|
||||
<string name="restart_app">Uruchom ponownie</string>
|
||||
<string name="settings_amoled_mode">Tryb AMOLED</string>
|
||||
<string name="settings_amoled_mode_summary">Włącz czysto czarny motyw, przydatny dla ekranów AMOLED, aby zmniejszyć zmęczenie oczu oraz zużycie baterii.</string>
|
||||
<string name="restart_required">Wymagany restart</string>
|
||||
<string name="restart_app_message">Aplikacja wymaga ponownego uruchomienia, aby zmiana została uwzględniona.</string>
|
||||
<string name="failed_to_update_sepolicy">Nie udało się zaktualizować reguł SELinux dla: %s</string>
|
||||
<string name="su_not_allowed">Przyznanie uprawnień superusera nie jest dozwolone dla: %s</string>
|
||||
<string name="module_changelog">Dziennik zmian</string>
|
||||
@@ -165,6 +181,7 @@
|
||||
<string name="settings_check_update_summary">Automatycznie sprawdzaj dostępność aktualizacji przy otwieraniu aplikacji.</string>
|
||||
<string name="grant_root_failed">Nie udało się przyznać uprawnień roota!</string>
|
||||
<string name="action">Akcja</string>
|
||||
<string name="webui">WebUI</string>
|
||||
<string name="open">Otwórz</string>
|
||||
<string name="enable_web_debugging">Włącz debugowanie WebView</string>
|
||||
<string name="enable_web_debugging_summary">Możesz użyć tej opcji w celu debugowania WebUI. Włącz tylko wtedy, gdy jest to potrzebne.</string>
|
||||
@@ -194,4 +211,15 @@
|
||||
<string name="settings_disable_su">Wyłącz funkcjonalność su</string>
|
||||
<string name="settings_disable_su_summary">Tymczasowo wyłącz możliwość uzyskania uprawnień roota przez dowolną aplikację za pomocą polecenia su (nie będzie to miało wpływu na istniejące procesy roota).</string>
|
||||
<string name="settings_language">Język</string>
|
||||
<string name="settings_legacyui">Używaj starszego interfejsu użytkownika</string>
|
||||
<string name="settings_legacyui_summary">Przełącz na poprzedni styl interfejsu użytkownika.</string>
|
||||
<string name="settings_banner">Włącz banery</string>
|
||||
<string name="settings_banner_summary">Pokazuj tła-banery dla modułów.</string>
|
||||
<string name="use_webuix">Używaj WebUI X</string>
|
||||
<string name="use_webuix_summary">Używaj WebUI X zamiast WebUI, który obsługuje więcej interfejsów API.</string>
|
||||
<string name="use_webuix_eruda">Wstrzykuj Eruda do WebUI X</string>
|
||||
<string name="use_webuix_eruda_summary">Wstrzyknij konsolę debugowania do WebUI X, aby ułatwić debugowanie. Wymagane jest aktywne debugowanie WebView.</string>
|
||||
<string name="customization">Personalizacja</string>
|
||||
<string name="developer">Programista</string>
|
||||
<string name="sucompat_disabled">SUCOMPAT NIEAKTYWNY</string>
|
||||
</resources>
|
||||
|
||||
@@ -16,12 +16,15 @@
|
||||
<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>
|
||||
<string name="home_kernel">Versão do kernel</string>
|
||||
<string name="hook_mode">Modo do hook</string>
|
||||
<string name="enable">Ativar</string>
|
||||
<string name="disable">Desativar</string>
|
||||
<string name="enabled">Ativado</string>
|
||||
<string name="disabled">Desativado</string>
|
||||
<string name="susfs_supported">Suportado</string>
|
||||
@@ -37,18 +40,21 @@
|
||||
<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>
|
||||
<string name="module_sort_a_to_z">Ordenar (A-Z)</string>
|
||||
<string name="module_sort_z_to_a">Ordenar (Z-A)</string>
|
||||
<string name="module_sort_a_to_z">Ordenar (A → Z)</string>
|
||||
<string name="module_sort_z_to_a">Ordenar (Z → A)</string>
|
||||
<string name="module_size_low_to_high">Ordenar (Menor → Maior)</string>
|
||||
<string name="module_size_high_to_low">Ordenar (Maior → Menor)</string>
|
||||
<string name="uninstall">Desinstalar</string>
|
||||
<string name="restore">Restaurar</string>
|
||||
<string name="module_install">Instalar</string>
|
||||
<string name="install">Instalar</string>
|
||||
<string name="reboot">Reiniciar</string>
|
||||
<string name="uninstalled">Desinstalado</string>
|
||||
<string name="settings">Configurações</string>
|
||||
<string name="reboot_userspace">Reinicialização suave</string>
|
||||
<string name="reboot_recovery">Reiniciar em modo Recovery</string>
|
||||
@@ -134,6 +140,8 @@
|
||||
<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>
|
||||
<string name="new_version_available">Nova versão %s está disponível, clique para atualizar.</string>
|
||||
@@ -141,8 +149,12 @@
|
||||
<string name="close">Fechar</string>
|
||||
<string name="force_stop_app">Forçar parada</string>
|
||||
<string name="restart_app">Reiniciar</string>
|
||||
<string name="settings_amoled_mode">Modo AMOLED</string>
|
||||
<string name="settings_amoled_mode_summary">Ative um tema preto puro útil para telas AMOLED para reduzir o cansaço visual e economizar bateria.</string>
|
||||
<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>
|
||||
@@ -166,29 +178,30 @@
|
||||
<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>
|
||||
<string name="webui">WebUI</string>
|
||||
<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>
|
||||
@@ -197,10 +210,19 @@
|
||||
<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>
|
||||
<string name="settings_banner_summary">Mostre banners de fundo para módulos.</string>
|
||||
<string name="use_webuix">Usar WebUI X</string>
|
||||
<string name="use_webuix_summary">Use o WebUI X em vez do WebUI, que oferece suporte a mais APIs.</string>
|
||||
<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,16 +15,19 @@
|
||||
<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>
|
||||
<string name="susfs_supported">Доступно</string>
|
||||
<string name="home_susfs">SuSFS: %s</string>
|
||||
<string name="home_susfs_version">Версия SuSFS</string>
|
||||
<string name="home_susfs_sus_su">SuS SU</string>
|
||||
@@ -41,14 +44,17 @@
|
||||
<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_sort_a_to_z">Сортировать (А-Я)</string>
|
||||
<string name="module_sort_z_to_a">Сортировать (Я-А)</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="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="reboot_userspace">Мягкая перезагрузка</string>
|
||||
<string name="reboot_recovery">Перезагрузка в Recovery</string>
|
||||
@@ -100,6 +106,7 @@
|
||||
<string name="proceed">Продолжить</string>
|
||||
<string name="cancel">Отмена</string>
|
||||
<string name="later">Позже</string>
|
||||
<string name="lkm_warning_message">Патч LKM использует компоненты с закрытым исходным кодом. Хотите продолжить?</string>
|
||||
<string name="home_next_kernelsu">🔥 Следующий билд</string>
|
||||
<string name="home_next_kernelsu_repo">https://github.com/KernelSU-Next/KernelSU-Next</string>
|
||||
<string name="home_next_kernelsu_body">Следующая экспериментальная ветка. Посмотрите на GitHub!</string>
|
||||
@@ -109,7 +116,7 @@
|
||||
<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>
|
||||
<string name="about_source_code"><![CDATA[Исходный код на %1$s]]></string>
|
||||
<string name="profile" translatable="false">Профиль Приложения</string>
|
||||
<string name="profile_default">По умолчанию</string>
|
||||
<string name="profile_template">Шаблон</string>
|
||||
@@ -133,6 +140,8 @@
|
||||
<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>
|
||||
<string name="new_version_available">Доступна новая версия %s, нажмите для обновления.</string>
|
||||
@@ -140,6 +149,10 @@
|
||||
<string name="close">Закрыть</string>
|
||||
<string name="force_stop_app">Принудительно остановить</string>
|
||||
<string name="restart_app">Перезапустить</string>
|
||||
<string name="settings_amoled_mode">Режим AMOLED</string>
|
||||
<string name="settings_amoled_mode_summary">Включить полностью тёмную тему, оптимизированную для дисплеев AMOLED, чтобы снизить зрительную нагрузку и сэкономить заряд аккумулятора.</string>
|
||||
<string name="restart_required">Требуется перезапуск</string>
|
||||
<string name="restart_app_message">Чтобы изменения вступили в силу, необходимо перезапустить приложение.</string>
|
||||
<string name="failed_to_update_sepolicy">Не удалось обновить правила SELinux для: %s</string>
|
||||
<string name="su_not_allowed">Предоставление прав суперпользователя запрещено для: %s</string>
|
||||
<string name="module_changelog">Журнал изменений</string>
|
||||
@@ -168,8 +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="open">Открыть</string>
|
||||
<string name="action">Скрипт</string>
|
||||
<string name="webui">WebUI</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>
|
||||
@@ -192,14 +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>
|
||||
|
||||
@@ -18,10 +18,13 @@
|
||||
<string name="home_working_version">Sürüm: %d</string>
|
||||
<string name="home_superuser_count">Süper kullanıcılar: %d</string>
|
||||
<string name="home_module_count">Modüller: %d</string>
|
||||
<string name="home_module_update_count">Güncellemeler: %d</string>
|
||||
<string name="home_failure">Kernel\'de KernelSU Next v2 imzası bulunamadı! [ !KSU_NEXT || != boyut/hash ]</string>
|
||||
<string name="home_failure_tip">Kernel geliştiricinizden KernelSU Next\'i entegre etmesini isteyin!</string>
|
||||
<string name="home_kernel">Kernel sürümü</string>
|
||||
<string name="hook_mode">Kanca Modu</string>
|
||||
<string name="enable">Etkinleştir</string>
|
||||
<string name="disable">Devre Dışı Bırak</string>
|
||||
<string name="enabled">Etkin</string>
|
||||
<string name="disabled">Devre dışı</string>
|
||||
<string name="susfs_supported">Destekleniyor</string>
|
||||
@@ -49,6 +52,7 @@
|
||||
<string name="module_install">Yükle</string>
|
||||
<string name="install">Yükle</string>
|
||||
<string name="reboot">Yeniden Başlat</string>
|
||||
<string name="uninstalled">Kaldırıldı</string>
|
||||
<string name="settings">Ayarlar</string>
|
||||
<string name="reboot_userspace">Yumuşak Yeniden Başlat</string>
|
||||
<string name="reboot_recovery">Kurtarma Moduna Yeniden Başlat</string>
|
||||
@@ -100,6 +104,7 @@
|
||||
<string name="proceed">Devam Et</string>
|
||||
<string name="cancel">İptal</string>
|
||||
<string name="later">Daha Sonra</string>
|
||||
<string name="lkm_warning_message">LKM yaması, kapalı kaynak bileşenlere dayanır. Devam etmek istiyor musunuz?</string>
|
||||
<string name="home_next_kernelsu">🔥 Yeni Sürüm</string>
|
||||
<string name="home_next_kernelsu_repo">https://github.com/KernelSU-Next/KernelSU-Next</string>
|
||||
<string name="home_next_kernelsu_body">Son deneysel dal. GitHub\'da kontrol edin!</string>
|
||||
@@ -133,6 +138,7 @@
|
||||
<string name="profile_selinux_domain">Etki Alanı</string>
|
||||
<string name="profile_selinux_rules">Kurallar</string>
|
||||
<string name="module_update">Güncelle</string>
|
||||
<string name="module_updated">Güncellendi</string>
|
||||
<string name="module_downloading">Modül indiriliyor: %s</string>
|
||||
<string name="module_start_downloading">İndirmeye başla: %s</string>
|
||||
<string name="new_version_available">%s sürümü mevcut, yükseltmek için tıklayın.</string>
|
||||
@@ -140,6 +146,10 @@
|
||||
<string name="close">Kapat</string>
|
||||
<string name="force_stop_app">Zorla durdur</string>
|
||||
<string name="restart_app">Yeniden Başlat</string>
|
||||
<string name="settings_amoled_mode">AMOLED modu</string>
|
||||
<string name="settings_amoled_mode_summary">Göz yorgunluğunu azaltmak ve pil tasarrufu sağlamak için AMOLED ekranlara uygun saf siyah tema etkinleştirilir.</string>
|
||||
<string name="restart_required">Yeniden Başlatma Gerekli</string>
|
||||
<string name="restart_app_message">Bu değişikliğin etkili olması için uygulamanın yeniden başlatılması gerekiyor.</string>
|
||||
<string name="failed_to_update_sepolicy">%s için SELinux kuralları güncellenemedi.</string>
|
||||
<string name="su_not_allowed">SüperKullanıcı yetkisi verilemez: %s</string>
|
||||
<string name="module_changelog">Değişiklik Günlüğü</string>
|
||||
@@ -169,6 +179,7 @@
|
||||
<string name="settings_check_update_summary">Uygulama açıldığında otomatik olarak güncellemeleri kontrol et.</string>
|
||||
<string name="grant_root_failed">Root yetkisi verilemedi!</string>
|
||||
<string name="action">Eylem</string>
|
||||
<string name="webui">WebUI</string>
|
||||
<string name="open">Aç</string>
|
||||
<string name="enable_web_debugging">WebView hata ayıklamasını etkinleştir</string>
|
||||
<string name="enable_web_debugging_summary">WebUI hata ayıklaması için kullanılabilir. Lütfen yalnızca gerekli olduğunda etkinleştirin.</string>
|
||||
@@ -198,8 +209,11 @@
|
||||
<string name="settings_disable_su">Su uyumluluğunu devre dışı bırak</string>
|
||||
<string name="settings_disable_su_summary">Geçici olarak herhangi bir uygulamanın su komutu aracılığıyla kök ayrıcalıkları elde etme yeteneğini devre dışı bırakır (mevcut kök süreçleri etkilenmez).</string>
|
||||
<string name="settings_language">Dil</string>
|
||||
<string name="settings_legacyui">Eski Arayüzü Kullan</string>
|
||||
<string name="settings_legacyui_summary">Önceki kullanıcı arayüzü stiline geç.</string>
|
||||
<string name="use_webuix">WebUI X\'i kullanın</string>
|
||||
<string name="use_webuix_summary">Daha fazla API\'yi destekleyen WebUI yerine WebUI X kullanın</string>
|
||||
<string name="use_webuix_eruda">WebUI X\'e Eruda Enjekte Et</string>
|
||||
<string name="use_webuix_eruda_summary">Hata ayıklamayı kolaylaştırmak için WebUI X\'e bir debug konsolu enjekte edin. Web hata ayıklamanın açık olması gerekir.</string>
|
||||
<string name="customization">Özelleştirme</string>
|
||||
</resources>
|
||||
|
||||
@@ -15,16 +15,19 @@
|
||||
<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>
|
||||
<string name="susfs_supported">Доступно</string>
|
||||
<string name="home_susfs">SuSFS: %s</string>
|
||||
<string name="home_susfs_version">Версія SuSFS</string>
|
||||
<string name="home_susfs_sus_su">SuS SU</string>
|
||||
@@ -41,15 +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="uninstall">Видалити</string>
|
||||
<string name="restore">Відновити</string>
|
||||
<string name="module_install">Встановити</string>
|
||||
<string name="install">Встановити</string>
|
||||
<string name="reboot">Перезавантаження</string>
|
||||
<string name="settings">Налаштування</string>
|
||||
<string name="uninstalled">Видалено</string>
|
||||
<string name="settings">Параметри</string>
|
||||
<string name="reboot_userspace">М’яка перезавантаження</string>
|
||||
<string name="reboot_recovery">Перезавантаження в Recovery</string>
|
||||
<string name="reboot_bootloader">Перезавантаження в Bootloader</string>
|
||||
@@ -87,19 +93,20 @@
|
||||
<string name="reboot_required">Потрібно перезавантаження</string>
|
||||
<string name="reboot_message">Зміни вступлять в силу після перезавантаження системи. Перезавантажити зараз?</string>
|
||||
<string name="module_restore">Відновити модуль</string>
|
||||
<string name="module_restore_message">Відновлення модулів із останньої резервної копії.</string>
|
||||
<string name="backup_restore">Резервне копіювання і відновлення</string>
|
||||
<string name="module_restore_message">Відновлення модулів із останнього бекапу.</string>
|
||||
<string name="backup_restore">Бекап і відновлення</string>
|
||||
<string name="module_backup">Бекап модуля</string>
|
||||
<string name="module_backup_message">Бекап поточних встановлених модулів.</string>
|
||||
<string name="allowlist_restore">Відновити список дозволів</string>
|
||||
<string name="allowlist_restore_message">Відновити список дозволів із останнього резервного копіювання.</string>
|
||||
<string name="allowlist_restore_message">Відновити список дозволів із останнього бекапу.</string>
|
||||
<string name="allowlist_backup">Бекап списку дозволів</string>
|
||||
<string name="allowlist_backup_message">Бекап поточного списку дозволів.</string>
|
||||
<string name="warning">Попередження</string>
|
||||
<string name="warning_message">Ця функція все ще перебуває у стадії бета-тестування. Будь ласка, переконайтеся, що ви створили резервні копії модулів перед використанням. Використовуйте це лише якщо розумієте можливі ризики. Будьте обережні.</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="home_next_kernelsu">🔥 Наступна збірка</string>
|
||||
<string name="home_next_kernelsu_repo">https://github.com/KernelSU-Next/KernelSU-Next</string>
|
||||
<string name="home_next_kernelsu_body">Наступна експериментальна гілка. Подивіться на GitHub!</string>
|
||||
@@ -109,9 +116,9 @@
|
||||
<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>
|
||||
<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>
|
||||
@@ -133,6 +140,8 @@
|
||||
<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>
|
||||
<string name="new_version_available">Доступна нова версія %s, натисніть для оновлення.</string>
|
||||
@@ -140,6 +149,10 @@
|
||||
<string name="close">Закрити</string>
|
||||
<string name="force_stop_app">Примусово зупинити</string>
|
||||
<string name="restart_app">Перезапустити</string>
|
||||
<string name="settings_amoled_mode">AMOLED-режим</string>
|
||||
<string name="settings_amoled_mode_summary">Увімкніть чисто чорну тему, корисну для екранів AMOLED, щоб зменшити навантаження на очі та заощадити заряд батареї.</string>
|
||||
<string name="restart_required">Потрібно перезавантажити</string>
|
||||
<string name="restart_app_message">Щоб ця зміна набула чинності, потрібно перезапустити програму.</string>
|
||||
<string name="failed_to_update_sepolicy">Не вдалося оновити правила SELinux для: %s</string>
|
||||
<string name="su_not_allowed">Надання прав суперкористувача заборонено для: %s</string>
|
||||
<string name="module_changelog">Журнал змін</string>
|
||||
@@ -168,8 +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="open">Відкрити</string>
|
||||
<string name="action">Скрипт</string>
|
||||
<string name="webui">WebUI</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>
|
||||
@@ -179,27 +193,36 @@
|
||||
<string name="install_next">Далі</string>
|
||||
<string name="select_file_tip">Рекомендовано образ розділу %1$s</string>
|
||||
<string name="select_kmi">Виберіть KMI</string>
|
||||
<string name="shrink_sparse_image">Зменшити modules.img образ</string>
|
||||
<string name="shrink_sparse_image_message">Змініть розмір modules.img образу, у якому знаходяться модулі, до його фактичного розміру. Зверніть увагу, що це може викликати ненормальну роботу модулів, тому використовуйте лише при необхідності (наприклад, для резервного копіювання).</string>
|
||||
<string name="shrink_sparse_image">Мінімізувати розріджене зображення</string>
|
||||
<string name="shrink_sparse_image_message">Змініть розмір розрідженого зображення, де розташований модуль, до його фактичного розміру. Зверніть увагу, що це може призвести до неправильної роботи модуля, тому використовуйте його лише за необхідності (наприклад, для бекапу).</string>
|
||||
<string name="settings_uninstall">Видалення</string>
|
||||
<string name="settings_uninstall_temporary">Тимчасове видалення</string>
|
||||
<string name="settings_uninstall_permanent">Повне видалення</string>
|
||||
<string name="settings_restore_stock_image">Відновити стандартний образ</string>
|
||||
<string name="settings_uninstall_temporary_message">Тимчасово видаліть KernelSU Next, відновивши початковий стан після наступного перезавантаження.</string>
|
||||
<string name="settings_uninstall_permanent_message">Повне і остаточне видалення KernelSU Next (Root та всіх модулів).</string>
|
||||
<string name="settings_restore_stock_image_message">Відновлення стандартного заводського образу (якщо є резервна копія), зазвичай використовується перед OTA; якщо потрібно видалити KernelSU Next, використовуйте "Повне видалення".</string>
|
||||
<string name="settings_restore_stock_image_message">Відновлення стандартного заводського образу (якщо є бекап), зазвичай використовується перед OTA; якщо потрібно видалити KernelSU Next, використовуйте "Повне видалення".</string>
|
||||
<string name="flashing">Прошивка</string>
|
||||
<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>
|
||||
|
||||
@@ -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,8 +3,8 @@
|
||||
<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_telegram">在 Telegram 联系</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>
|
||||
<string name="confirm">确认</string>
|
||||
@@ -12,42 +12,55 @@
|
||||
<string name="home">主页</string>
|
||||
<string name="home_not_installed">未安装</string>
|
||||
<string name="home_click_to_install">点击安装</string>
|
||||
<string name="lkm_mode_deprecated">LKM 模式现已弃用!</string>
|
||||
<string name="lkm_alternative_suggestion">请使用 GKI 内核或手动将 KernelSU Next 集成到你的设备。</string>
|
||||
<string name="home_working">工作中</string>
|
||||
<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_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">Hook 模式</string>
|
||||
<string name="enable">启用</string>
|
||||
<string name="disable">禁用</string>
|
||||
<string name="enabled">已启用</string>
|
||||
<string name="disabled">已禁用</string>
|
||||
<string name="susfs_supported">支持</string>
|
||||
<string name="home_susfs">SuSFS:%s</string>
|
||||
<string name="home_susfs_version">SuSFS 版本</string>
|
||||
<string name="home_susfs_sus_su">SuS SU</string>
|
||||
<string name="home_android">Android 版本</string>
|
||||
<string name="home_manager_version">管理器版本</string>
|
||||
<string name="home_abi">接口</string>
|
||||
<string name="home_selinux_status">SELinux 状态</string>
|
||||
<string name="selinux_status_disabled">被禁用</string>
|
||||
<string name="selinux_status_enforcing">强制执行</string>
|
||||
<string name="selinux_status_disabled">禁用</string>
|
||||
<string name="selinux_status_enforcing">严格模式</string>
|
||||
<string name="selinux_status_permissive">宽容模式</string>
|
||||
<string name="selinux_status_unknown">未知</string>
|
||||
<string name="superuser">超级用户</string>
|
||||
<string name="module_failed_to_enable">无法启用模块:%s</string>
|
||||
<string name="module_failed_to_disable">无法禁用模块:%s</string>
|
||||
<string name="module_empty">没有安装模块</string>
|
||||
<string name="module_failed_to_enable">启用模块失败数:%s</string>
|
||||
<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_sort_a_to_z">按 A-Z 排序</string>
|
||||
<string name="module_sort_z_to_a">按 Z-A 排序</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="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="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_edl">重启到 EDL</string>
|
||||
<string name="reboot_download">重启到下载模式</string>
|
||||
<string name="reboot_edl">重启到 EDL 模式</string>
|
||||
<string name="about">关于</string>
|
||||
<string name="module_uninstall_confirm">确定要卸载模块 %s 吗?</string>
|
||||
<string name="module_uninstall_success">%s 已卸载</string>
|
||||
@@ -62,20 +75,20 @@
|
||||
<string name="module_update_json">更新配置</string>
|
||||
<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="enable_developer_options_summary">显示隐藏的开发者专用设置和调试信息。</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>
|
||||
<string name="unavailable">不可用</string>
|
||||
<string name="use_overlay_fs">使用 OverlayFS(实验性)</string>
|
||||
<string name="use_overlay_fs">使用 OverlayFS</string>
|
||||
<string name="use_overlay_fs_summary">在 OverlayFS 和 Magic Mount 两种挂载系统之间进行切换。</string>
|
||||
<string name="reboot_required">需要重启</string>
|
||||
<string name="reboot_message">更改将在重启系统后生效。您想现在重启吗?</string>
|
||||
@@ -88,21 +101,22 @@
|
||||
<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="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>
|
||||
<string name="about_source_code"><![CDATA[在 %1$s 查看源代码]]></string>
|
||||
<string name="profile" translatable="false">App Profile</string>
|
||||
<string name="profile_default">默认</string>
|
||||
<string name="profile_template">模板</string>
|
||||
@@ -126,6 +140,8 @@
|
||||
<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>
|
||||
<string name="new_version_available">发现新版本:%s,点击升级。</string>
|
||||
@@ -133,15 +149,19 @@
|
||||
<string name="close">关闭</string>
|
||||
<string name="force_stop_app">强制停止</string>
|
||||
<string name="restart_app">重新启动</string>
|
||||
<string name="settings_amoled_mode">AMOLED 模式</string>
|
||||
<string name="settings_amoled_mode_summary">启用适用于 AMOLED 屏幕的纯黑主题,可减少视觉疲劳并节省电量。</string>
|
||||
<string name="restart_required">需要重启</string>
|
||||
<string name="restart_app_message">请重启 APP 以确保更改生效。</string>
|
||||
<string name="failed_to_update_sepolicy">为 %s 更新 SELinux 规则失败</string>
|
||||
<string name="su_not_allowed">拒绝授予%s超级用户权限</string>
|
||||
<string name="module_changelog">更新日志</string>
|
||||
<string name="settings_profile_template">App Profile 模板</string>
|
||||
<string name="settings_profile_template_summary">管理本地和在线的 App Profile 模版</string>
|
||||
<string name="settings_profile_template_summary">管理本地和在线的 App Profile 模版。</string>
|
||||
<string name="app_profile_template_create">创建模版</string>
|
||||
<string name="app_profile_template_edit">编辑模版</string>
|
||||
<string name="app_profile_template_id">模板 ID</string>
|
||||
<string name="app_profile_template_id_invalid">模板 ID 不合规</string>
|
||||
<string name="app_profile_template_id_invalid">模板 ID 不合法</string>
|
||||
<string name="app_profile_template_name">名字</string>
|
||||
<string name="app_profile_template_description">描述</string>
|
||||
<string name="app_profile_template_save">保存</string>
|
||||
@@ -159,9 +179,10 @@
|
||||
<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="webui">WebUI</string>
|
||||
<string name="open">打开</string>
|
||||
<string name="enable_web_debugging">启用 WebView 调试</string>
|
||||
<string name="enable_web_debugging_summary">可用于调试 WebUI,请仅在需要时启用。</string>
|
||||
@@ -173,7 +194,7 @@
|
||||
<string name="select_file_tip">建议选择 %1$s 分区镜像</string>
|
||||
<string name="select_kmi">选择 KMI</string>
|
||||
<string name="shrink_sparse_image">最小化稀疏文件</string>
|
||||
<string name="shrink_sparse_image_message">将模块所在的稀疏文件镜像调整为其实际大小,注意这可能导致模块工作异常,请仅在必要时(如备份)使用</string>
|
||||
<string name="shrink_sparse_image_message">将模块所在的稀疏文件镜像调整为其实际大小,注意这可能导致模块工作异常,请仅在必要时(如备份)使用。</string>
|
||||
<string name="settings_uninstall">卸载</string>
|
||||
<string name="settings_uninstall_temporary">临时卸载</string>
|
||||
<string name="settings_uninstall_permanent">完全卸载</string>
|
||||
@@ -188,7 +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_summary">临时禁止任何应用通过 su 命令获取 root 权限(已运行的 root 进程不受影响)</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_eruda">对 WebUI X 注入 Eruda</string>
|
||||
<string name="use_webuix_eruda_summary">使用 WebUI X 时注入控制台以便于调试,需要启用 WebView 调试功能。</string>
|
||||
<string name="customization">界面设置</string>
|
||||
<string name="developer">开发者选项</string>
|
||||
<string name="sucompat_disabled">SU 兼容被禁用</string>
|
||||
<string name="zygisk_required">需要 Zygisk</string>
|
||||
</resources>
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
<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_telegram">透過 Telegram 聯繫</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>
|
||||
<string name="confirm">確認</string>
|
||||
@@ -12,18 +12,28 @@
|
||||
<string name="home">主頁</string>
|
||||
<string name="home_not_installed">未安裝</string>
|
||||
<string name="home_click_to_install">點擊安裝</string>
|
||||
<string name="lkm_mode_deprecated">LKM 模式現已棄用!</string>
|
||||
<string name="lkm_alternative_suggestion">請使用 GKI 內核或手動將 KernelSU Next 整合到你的裝置。</string>
|
||||
<string name="home_working">工作中</string>
|
||||
<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_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">Hook 方法</string>
|
||||
<string name="enable">啓用</string>
|
||||
<string name="disable">禁用</string>
|
||||
<string name="enabled">已經啓用</string>
|
||||
<string name="disabled">已經禁用</string>
|
||||
<string name="susfs_supported">支援</string>
|
||||
<string name="home_susfs">SuSFS:%s</string>
|
||||
<string name="home_susfs_version">SuSFS 版本</string>
|
||||
<string name="home_susfs_sus_su">SuS SU</string>
|
||||
<string name="home_android">Android 版本</string>
|
||||
<string name="home_manager_version">管理器版本</string>
|
||||
<string name="home_manager_version">管理員版本</string>
|
||||
<string name="home_abi">ABI</string>
|
||||
<string name="home_selinux_status">SELinux 狀態</string>
|
||||
<string name="selinux_status_disabled">被禁用</string>
|
||||
<string name="selinux_status_enforcing">強制執行</string>
|
||||
@@ -37,11 +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="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="reboot_userspace">軟重啟</string>
|
||||
<string name="reboot_recovery">重啟到 Recovery</string>
|
||||
@@ -63,7 +76,7 @@
|
||||
<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>
|
||||
@@ -75,7 +88,7 @@
|
||||
<string name="home_magic_mount">Magic Mount</string>
|
||||
<string name="home_overlayfs_mount">OverlayFS</string>
|
||||
<string name="unavailable">不可用</string>
|
||||
<string name="use_overlay_fs">使用 OverlayFS (實驗性功能)</string>
|
||||
<string name="use_overlay_fs">使用 OverlayFS</string>
|
||||
<string name="use_overlay_fs_summary">對於 KernelSU Next 的掛載系統,在使用 OverlayFS 和 Magic Mount 之間進行切換。</string>
|
||||
<string name="reboot_required">需要重啟</string>
|
||||
<string name="reboot_message">更改將在重啟系統後生效。您想現在重啟嗎?</string>
|
||||
@@ -93,6 +106,7 @@
|
||||
<string name="proceed">繼續</string>
|
||||
<string name="cancel">取消</string>
|
||||
<string name="later">稍後</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>
|
||||
@@ -126,6 +140,8 @@
|
||||
<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>
|
||||
<string name="new_version_available">發現新版本:%s,點擊升級。</string>
|
||||
@@ -133,22 +149,26 @@
|
||||
<string name="close">關閉</string>
|
||||
<string name="force_stop_app">強制停止</string>
|
||||
<string name="restart_app">重新啟動</string>
|
||||
<string name="settings_amoled_mode">AMOLED 模式</string>
|
||||
<string name="settings_amoled_mode_summary">啓用 AMOLED 屏幕專用的純黑主題,減少視覺疲勞的同時節約電池使用。</string>
|
||||
<string name="restart_required">需要重啓</string>
|
||||
<string name="restart_app_message">請重啓程式確保改動生效。</string>
|
||||
<string name="failed_to_update_sepolicy">為:%s 更新 SELinux 規則失敗</string>
|
||||
<string name="su_not_allowed">拒絕授予%s超級使用者權限</string>
|
||||
<string name="module_changelog">更新日誌</string>
|
||||
<string name="settings_profile_template">App Profile 模板</string>
|
||||
<string name="settings_profile_template_summary">管理本地和線上的 App Profile 模板</string>
|
||||
<string name="settings_profile_template_summary">管理本地和線上的 App Profile 模板。</string>
|
||||
<string name="app_profile_template_create">創建模板</string>
|
||||
<string name="app_profile_template_edit">編輯模板</string>
|
||||
<string name="app_profile_template_id">模板 id</string>
|
||||
<string name="app_profile_template_id_invalid">模板 id 不合法</string>
|
||||
<string name="app_profile_template_id">模板 ID</string>
|
||||
<string name="app_profile_template_id_invalid">模板 ID 不合法</string>
|
||||
<string name="app_profile_template_name">名字</string>
|
||||
<string name="app_profile_template_description">描述</string>
|
||||
<string name="app_profile_template_save">保存</string>
|
||||
<string name="app_profile_template_delete">刪除</string>
|
||||
<string name="app_profile_template_view">查看模板</string>
|
||||
<string name="app_profile_template_readonly">只讀</string>
|
||||
<string name="app_profile_template_id_exist">模板 id 已存在!</string>
|
||||
<string name="app_profile_template_id_exist">模板 ID 已存在!</string>
|
||||
<string name="app_profile_import_export">匯入/匯出</string>
|
||||
<string name="app_profile_import_from_clipboard">從剪貼簿匯入</string>
|
||||
<string name="app_profile_export_to_clipboard">匯出到剪貼簿</string>
|
||||
@@ -159,12 +179,13 @@
|
||||
<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="webui">WebUI</string>
|
||||
<string name="open">打開</string>
|
||||
<string name="enable_web_debugging">啟用 WebView 偵錯</string>
|
||||
<string name="enable_web_debugging_summary">可用於偵錯 WebUI ,請僅在需要時啟用。</string>
|
||||
<string name="enable_web_debugging_summary">可用於偵錯 WebUI ,僅在需要時啟用。</string>
|
||||
<string name="direct_install">直接安裝(推薦)</string>
|
||||
<string name="select_file">選擇一個檔案</string>
|
||||
<string name="install_inactive_slot">安裝到未使用的槽位(OTA 後)</string>
|
||||
@@ -173,7 +194,7 @@
|
||||
<string name="select_file_tip">建議選擇 %1$s 分區映像檔</string>
|
||||
<string name="select_kmi">選擇 KMI</string>
|
||||
<string name="shrink_sparse_image">最小化稀疏檔案</string>
|
||||
<string name="shrink_sparse_image_message">將模組所在的稀疏檔案映像調整為其實際大小,注意這可能導致模組工作異常,請僅在必要時(如備份)使用</string>
|
||||
<string name="shrink_sparse_image_message">將模組所在的稀疏檔案映像調整為其實際大小,注意這可能導致模組工作異常,請僅在必要時(如備份)使用。</string>
|
||||
<string name="settings_uninstall">卸載</string>
|
||||
<string name="settings_uninstall_temporary">臨時卸載</string>
|
||||
<string name="settings_uninstall_permanent">永久卸載</string>
|
||||
@@ -189,6 +210,19 @@
|
||||
<string name="log_saved">日誌已保存</string>
|
||||
<string name="send_log">發送日誌</string>
|
||||
<string name="settings_disable_su">關閉 SU 相容</string>
|
||||
<string name="settings_disable_su_summary">暫時禁止任何應用通過 su 命令獲取 root 權限(已運行的 root 進程不受影響)</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>
|
||||
<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_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">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>
|
||||
@@ -150,12 +149,12 @@
|
||||
<string name="restart_app">Restart</string>
|
||||
<string name="settings_amoled_mode">AMOLED mode</string>
|
||||
<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_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>
|
||||
@@ -220,4 +220,16 @@
|
||||
<string name="use_webuix_eruda">Inject Eruda into WebUI X</string>
|
||||
<string name="use_webuix_eruda_summary">Inject a debug console into WebUI X to make debugging easier. Requires web debugging to be on.</string>
|
||||
<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>
|
||||
|
||||
@@ -28,8 +28,8 @@ cmaker {
|
||||
}
|
||||
|
||||
val androidMinSdkVersion = 26
|
||||
val androidTargetSdkVersion = 35
|
||||
val androidCompileSdkVersion = 35
|
||||
val androidTargetSdkVersion = 36
|
||||
val androidCompileSdkVersion = 36
|
||||
val androidCompileNdkVersion = "28.1.13356709"
|
||||
val androidSourceCompatibility = JavaVersion.VERSION_21
|
||||
val androidTargetCompatibility = JavaVersion.VERSION_21
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
[versions]
|
||||
agp = "8.10.0"
|
||||
agp = "8.10.1"
|
||||
kotlin = "2.1.21"
|
||||
ksp = "2.1.21-2.0.1"
|
||||
compose-bom = "2025.05.01"
|
||||
lifecycle = "2.9.0"
|
||||
ksp = "2.1.21-2.0.2"
|
||||
compose-bom = "2025.06.00"
|
||||
lifecycle = "2.9.1"
|
||||
navigation = "2.9.0"
|
||||
activity-compose = "1.10.1"
|
||||
kotlinx-coroutines = "1.10.2"
|
||||
@@ -11,13 +11,13 @@ coil-compose = "2.7.0"
|
||||
compose-destination = "2.2.0"
|
||||
sheets-compose-dialogs = "1.3.0"
|
||||
markdown = "4.6.2"
|
||||
webkit = "1.13.0"
|
||||
webkit = "1.14.0"
|
||||
appiconloader-coil = "1.5.0"
|
||||
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" }
|
||||
@@ -72,9 +72,6 @@ sheet-compose-dialogs-input = { group = "com.maxkeppeler.sheets-compose-dialogs"
|
||||
|
||||
markdown = { group = "io.noties.markwon", name = "core", version.ref = "markdown" }
|
||||
|
||||
lsposed-cxx = { module = "org.lsposed.libcxx:libcxx", version = "27.0.12077973" }
|
||||
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" }
|
||||
@@ -1,7 +1,7 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.2-bin.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
zipStorePath=wrapper/dists
|
||||
170
userspace/ksud_magic/Cargo.lock
generated
170
userspace/ksud_magic/Cargo.lock
generated
@@ -13,9 +13,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "adler2"
|
||||
version = "2.0.0"
|
||||
version = "2.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627"
|
||||
checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
|
||||
|
||||
[[package]]
|
||||
name = "adler32"
|
||||
@@ -81,9 +81,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "anstream"
|
||||
version = "0.6.18"
|
||||
version = "0.6.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b"
|
||||
checksum = "301af1932e46185686725e0fad2f8f2aa7da69dd70bf6ecc44d6b703844a3933"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"anstyle-parse",
|
||||
@@ -96,33 +96,33 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "anstyle"
|
||||
version = "1.0.10"
|
||||
version = "1.0.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9"
|
||||
checksum = "862ed96ca487e809f1c8e5a8447f6ee2cf102f846893800b20cebdf541fc6bbd"
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-parse"
|
||||
version = "0.2.6"
|
||||
version = "0.2.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9"
|
||||
checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2"
|
||||
dependencies = [
|
||||
"utf8parse",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-query"
|
||||
version = "1.1.2"
|
||||
version = "1.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c"
|
||||
checksum = "6c8bdeb6047d8983be085bab0ba1472e6dc604e7041dbf6fcd5e71523014fae9"
|
||||
dependencies = [
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-wincon"
|
||||
version = "3.0.8"
|
||||
version = "3.0.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6680de5231bd6ee4c6191b8a1325daa282b415391ec9d3a37bd34f2060dc73fa"
|
||||
checksum = "403f75924867bb1033c59fbf0797484329750cfbe3c4325cd33127941fabc882"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"once_cell_polyfill",
|
||||
@@ -184,9 +184,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "2.9.0"
|
||||
version = "2.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd"
|
||||
checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967"
|
||||
|
||||
[[package]]
|
||||
name = "block-buffer"
|
||||
@@ -199,9 +199,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "bumpalo"
|
||||
version = "3.17.0"
|
||||
version = "3.18.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf"
|
||||
checksum = "793db76d6187cd04dff33004d8e6c9cc4e05cd330500379d2394209271b4aeee"
|
||||
|
||||
[[package]]
|
||||
name = "byteorder"
|
||||
@@ -217,18 +217,18 @@ checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a"
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.2.23"
|
||||
version = "1.2.27"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5f4ac86a9e5bc1e2b3449ab9d7d3a6a405e3d1bb28d7b9be8614f55846ae3766"
|
||||
checksum = "d487aa071b5f64da6f19a3e848e3578944b726ee5a4854b82172f02aa876bfdc"
|
||||
dependencies = [
|
||||
"shlex",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268"
|
||||
|
||||
[[package]]
|
||||
name = "chrono"
|
||||
@@ -246,9 +246,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.5.38"
|
||||
version = "4.5.40"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ed93b9805f8ba930df42c2590f05453d5ec36cbb85d018868a5b24d31f6ac000"
|
||||
checksum = "40b6887a1d8685cebccf115538db5c0efe625ccac9696ad45c409d96566e910f"
|
||||
dependencies = [
|
||||
"clap_builder",
|
||||
"clap_derive",
|
||||
@@ -256,9 +256,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap_builder"
|
||||
version = "4.5.38"
|
||||
version = "4.5.40"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "379026ff283facf611b0ea629334361c4211d1b12ee01024eec1591133b04120"
|
||||
checksum = "e0c66c08ce9f0c698cbce5c0279d0bb6ac936d8674174fe48f736533b964f59e"
|
||||
dependencies = [
|
||||
"anstream",
|
||||
"anstyle",
|
||||
@@ -268,9 +268,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap_derive"
|
||||
version = "4.5.32"
|
||||
version = "4.5.40"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09176aae279615badda0765c0c0b3f6ed53f4709118af73cf4655d85d1530cd7"
|
||||
checksum = "d2c7947ae4cc3d851207c1adb5b5e260ff0cca11446b1d6d1423788e442257ce"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"proc-macro2",
|
||||
@@ -280,15 +280,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap_lex"
|
||||
version = "0.7.4"
|
||||
version = "0.7.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6"
|
||||
checksum = "b94f61472cee1439c0b966b47e3aca9ae07e45d070759512cd390ea2bebc6675"
|
||||
|
||||
[[package]]
|
||||
name = "colorchoice"
|
||||
version = "1.0.3"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990"
|
||||
checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75"
|
||||
|
||||
[[package]]
|
||||
name = "const_format"
|
||||
@@ -536,12 +536,12 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "errno"
|
||||
version = "0.3.11"
|
||||
version = "0.3.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "976dd42dc7e85965fe702eb8164f21f450704bdde31faefd6471dba214cb594e"
|
||||
checksum = "cea14ef9355e3beab063703aa9dab15afd25f0667c341310c1e5274bb1d0da18"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys 0.52.0",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -573,9 +573,9 @@ checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
|
||||
|
||||
[[package]]
|
||||
name = "flate2"
|
||||
version = "1.1.1"
|
||||
version = "1.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7ced92e76e966ca2fd84c8f7aa01a4aea65b0eb6648d72f7c8f3e2764a67fece"
|
||||
checksum = "4a3d7db9596fecd151c5f638c0ee5d5bd487b6e0ea232e5dc96d5250f6f94b1d"
|
||||
dependencies = [
|
||||
"crc32fast",
|
||||
"libz-rs-sys",
|
||||
@@ -594,9 +594,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "getopts"
|
||||
version = "0.2.21"
|
||||
version = "0.2.23"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "14dbbfd5c71d70241ecf9e6f13737f7b5ce823821063188d7e46c41d371eebd5"
|
||||
checksum = "cba6ae63eb948698e300f645f87c70f76630d505f23b8907cf1e193ee85048c1"
|
||||
dependencies = [
|
||||
"unicode-width",
|
||||
]
|
||||
@@ -631,9 +631,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.15.2"
|
||||
version = "0.15.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289"
|
||||
checksum = "5971ac85611da7067dbfcabef3c70ebb5606018acd9e2a3903a0da507521e0d5"
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
@@ -710,7 +710,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e"
|
||||
dependencies = [
|
||||
"equivalent",
|
||||
"hashbrown 0.15.2",
|
||||
"hashbrown 0.15.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -807,9 +807,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.172"
|
||||
version = "0.2.173"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa"
|
||||
checksum = "d8cfeafaffdbc32176b64fb251369d52ea9f0a8fbc6f8759edffef7b525d64bb"
|
||||
|
||||
[[package]]
|
||||
name = "libflate"
|
||||
@@ -843,9 +843,9 @@ checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de"
|
||||
|
||||
[[package]]
|
||||
name = "libz-rs-sys"
|
||||
version = "0.5.0"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6489ca9bd760fe9642d7644e827b0c9add07df89857b0416ee15c1cc1a3b8c5a"
|
||||
checksum = "172a788537a2221661b480fee8dc5f96c580eb34fa88764d3205dc356c7e4221"
|
||||
dependencies = [
|
||||
"zlib-rs",
|
||||
]
|
||||
@@ -891,15 +891,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.7.4"
|
||||
version = "2.7.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
|
||||
checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0"
|
||||
|
||||
[[package]]
|
||||
name = "miniz_oxide"
|
||||
version = "0.8.8"
|
||||
version = "0.8.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3be647b768db090acb35d5ec5db2b0e1f1de11133ca123b9eacf5137868f892a"
|
||||
checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316"
|
||||
dependencies = [
|
||||
"adler2",
|
||||
]
|
||||
@@ -945,12 +945,9 @@ checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
|
||||
|
||||
[[package]]
|
||||
name = "once_cell_polyfill"
|
||||
version = "1.70.0"
|
||||
version = "1.70.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2611b99ab098a31bdc8be48b4f1a285ca0ced28bd5b4f23e45efa8c63b09efa5"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
]
|
||||
checksum = "a4895175b425cb1f87721b59f0f286c2092bd4af812243672510e1ac53e2e0ad"
|
||||
|
||||
[[package]]
|
||||
name = "pin-project-lite"
|
||||
@@ -985,7 +982,7 @@ version = "0.17.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cc5b72d8145275d844d4b5f6d4e1eef00c8cd889edb6035c21675d1bb1f45c9f"
|
||||
dependencies = [
|
||||
"bitflags 2.9.0",
|
||||
"bitflags 2.9.1",
|
||||
"chrono",
|
||||
"flate2",
|
||||
"hex",
|
||||
@@ -999,7 +996,7 @@ version = "0.17.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "239df02d8349b06fc07398a3a1697b06418223b1c7725085e801e7c0fc6a12ec"
|
||||
dependencies = [
|
||||
"bitflags 2.9.0",
|
||||
"bitflags 2.9.1",
|
||||
"chrono",
|
||||
"hex",
|
||||
]
|
||||
@@ -1088,17 +1085,17 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rustc-demangle"
|
||||
version = "0.1.24"
|
||||
version = "0.1.25"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f"
|
||||
checksum = "989e6739f80c4ad5b13e0fd7fe89531180375b18520cc8c82080e4dc4035b84f"
|
||||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "0.38.34"
|
||||
source = "git+https://github.com/KernelSU-Next/rustix.git?branch=main#4a53fbc7cb7a07cabe87125cc21dbc27db316259"
|
||||
dependencies = [
|
||||
"bitflags 2.9.0",
|
||||
"errno 0.3.11",
|
||||
"bitflags 2.9.1",
|
||||
"errno 0.3.12",
|
||||
"itoa",
|
||||
"libc",
|
||||
"linux-raw-sys 0.4.15",
|
||||
@@ -1112,31 +1109,31 @@ version = "0.38.44"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154"
|
||||
dependencies = [
|
||||
"bitflags 2.9.0",
|
||||
"errno 0.3.11",
|
||||
"bitflags 2.9.1",
|
||||
"errno 0.3.12",
|
||||
"libc",
|
||||
"linux-raw-sys 0.4.15",
|
||||
"windows-sys 0.52.0",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "1.0.5"
|
||||
version = "1.0.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d97817398dd4bb2e6da002002db259209759911da105da92bec29ccb12cf58bf"
|
||||
checksum = "c71e83d6afe7ff64890ec6b71d6a69bb8a610ab78ce364b3352876bb4c801266"
|
||||
dependencies = [
|
||||
"bitflags 2.9.0",
|
||||
"errno 0.3.11",
|
||||
"bitflags 2.9.1",
|
||||
"errno 0.3.12",
|
||||
"libc",
|
||||
"linux-raw-sys 0.9.4",
|
||||
"windows-sys 0.52.0",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustversion"
|
||||
version = "1.0.20"
|
||||
version = "1.0.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2"
|
||||
checksum = "8a0d197bd2c9dc6e53b84da9556a69ba4cdfab8619eb41a8bd1cc2027a0f6b1d"
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
@@ -1240,9 +1237,9 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.101"
|
||||
version = "2.0.103"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf"
|
||||
checksum = "e4307e30089d6fd6aff212f2da3a1f9e32f3223b1f010fb09b7c95f90f3ca1e8"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -1258,8 +1255,8 @@ dependencies = [
|
||||
"fastrand",
|
||||
"getrandom",
|
||||
"once_cell",
|
||||
"rustix 1.0.5",
|
||||
"windows-sys 0.52.0",
|
||||
"rustix 1.0.7",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1283,9 +1280,9 @@ checksum = "c9e9a38711f559d9e3ce1cdb06dd7c5b8ea546bc90052da6d06bb76da74bb07c"
|
||||
|
||||
[[package]]
|
||||
name = "tokio"
|
||||
version = "1.45.0"
|
||||
version = "1.45.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2513ca694ef9ede0fb23fe71a4ee4107cb102b9dc1930f6d0fd77aae068ae165"
|
||||
checksum = "75ef51a33ef1da925cea3e4eb122833cb377c61439ca401b770f54902b806779"
|
||||
dependencies = [
|
||||
"backtrace",
|
||||
"bytes",
|
||||
@@ -1306,9 +1303,9 @@ checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-width"
|
||||
version = "0.1.14"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af"
|
||||
checksum = "4a1a07cc7db3810833284e8d372ccdc6da29741639ecc70c9ec107df0fa6154c"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-xid"
|
||||
@@ -1407,13 +1404,12 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "which"
|
||||
version = "7.0.3"
|
||||
version = "8.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "24d643ce3fd3e5b54854602a080f34fb10ab75e0b813ee32d00ca2b44fa74762"
|
||||
checksum = "d3fabb953106c3c8eea8306e4393700d7657561cb43122571b172bbfb7c7ba1d"
|
||||
dependencies = [
|
||||
"either",
|
||||
"env_home",
|
||||
"rustix 1.0.5",
|
||||
"rustix 1.0.7",
|
||||
"winsafe",
|
||||
]
|
||||
|
||||
@@ -1439,7 +1435,7 @@ version = "0.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
|
||||
dependencies = [
|
||||
"windows-sys 0.52.0",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1485,9 +1481,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windows-link"
|
||||
version = "0.1.1"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38"
|
||||
checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a"
|
||||
|
||||
[[package]]
|
||||
name = "windows-result"
|
||||
@@ -1601,7 +1597,7 @@ version = "0.39.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1"
|
||||
dependencies = [
|
||||
"bitflags 2.9.0",
|
||||
"bitflags 2.9.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1662,9 +1658,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "zlib-rs"
|
||||
version = "0.5.0"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "868b928d7949e09af2f6086dfc1e01936064cc7a819253bce650d4e2a2d63ba8"
|
||||
checksum = "626bd9fa9734751fc50d6060752170984d7053f5a39061f524cda68023d4db8a"
|
||||
|
||||
[[package]]
|
||||
name = "zopfli"
|
||||
|
||||
@@ -38,7 +38,7 @@ rust-embed = { version = "8", features = [
|
||||
"debug-embed",
|
||||
"compression", # must clean build after updating binaries
|
||||
] }
|
||||
which = "7"
|
||||
which = "8"
|
||||
getopts = "0.2"
|
||||
sha256 = "1"
|
||||
sha1 = "0.10"
|
||||
|
||||
@@ -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}")
|
||||
};
|
||||
|
||||
186
userspace/ksud_overlayfs/Cargo.lock
generated
186
userspace/ksud_overlayfs/Cargo.lock
generated
@@ -13,9 +13,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "adler2"
|
||||
version = "2.0.0"
|
||||
version = "2.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627"
|
||||
checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
|
||||
|
||||
[[package]]
|
||||
name = "adler32"
|
||||
@@ -29,7 +29,7 @@ version = "0.8.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"cfg-if 1.0.1",
|
||||
"once_cell",
|
||||
"version_check",
|
||||
"zerocopy",
|
||||
@@ -81,9 +81,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "anstream"
|
||||
version = "0.6.18"
|
||||
version = "0.6.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b"
|
||||
checksum = "301af1932e46185686725e0fad2f8f2aa7da69dd70bf6ecc44d6b703844a3933"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"anstyle-parse",
|
||||
@@ -96,33 +96,33 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "anstyle"
|
||||
version = "1.0.10"
|
||||
version = "1.0.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9"
|
||||
checksum = "862ed96ca487e809f1c8e5a8447f6ee2cf102f846893800b20cebdf541fc6bbd"
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-parse"
|
||||
version = "0.2.6"
|
||||
version = "0.2.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9"
|
||||
checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2"
|
||||
dependencies = [
|
||||
"utf8parse",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-query"
|
||||
version = "1.1.2"
|
||||
version = "1.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c"
|
||||
checksum = "6c8bdeb6047d8983be085bab0ba1472e6dc604e7041dbf6fcd5e71523014fae9"
|
||||
dependencies = [
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-wincon"
|
||||
version = "3.0.8"
|
||||
version = "3.0.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6680de5231bd6ee4c6191b8a1325daa282b415391ec9d3a37bd34f2060dc73fa"
|
||||
checksum = "403f75924867bb1033c59fbf0797484329750cfbe3c4325cd33127941fabc882"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"once_cell_polyfill",
|
||||
@@ -168,7 +168,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6806a6321ec58106fea15becdad98371e28d92ccbc7c8f1b3b6dd724fe8f1002"
|
||||
dependencies = [
|
||||
"addr2line",
|
||||
"cfg-if 1.0.0",
|
||||
"cfg-if 1.0.1",
|
||||
"libc",
|
||||
"miniz_oxide",
|
||||
"object",
|
||||
@@ -184,9 +184,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "2.9.0"
|
||||
version = "2.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd"
|
||||
checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967"
|
||||
|
||||
[[package]]
|
||||
name = "block-buffer"
|
||||
@@ -199,9 +199,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "bumpalo"
|
||||
version = "3.17.0"
|
||||
version = "3.18.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf"
|
||||
checksum = "793db76d6187cd04dff33004d8e6c9cc4e05cd330500379d2394209271b4aeee"
|
||||
|
||||
[[package]]
|
||||
name = "byteorder"
|
||||
@@ -217,9 +217,9 @@ checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a"
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.2.23"
|
||||
version = "1.2.27"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5f4ac86a9e5bc1e2b3449ab9d7d3a6a405e3d1bb28d7b9be8614f55846ae3766"
|
||||
checksum = "d487aa071b5f64da6f19a3e848e3578944b726ee5a4854b82172f02aa876bfdc"
|
||||
dependencies = [
|
||||
"shlex",
|
||||
]
|
||||
@@ -232,9 +232,9 @@ checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268"
|
||||
|
||||
[[package]]
|
||||
name = "chrono"
|
||||
@@ -252,9 +252,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.5.38"
|
||||
version = "4.5.40"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ed93b9805f8ba930df42c2590f05453d5ec36cbb85d018868a5b24d31f6ac000"
|
||||
checksum = "40b6887a1d8685cebccf115538db5c0efe625ccac9696ad45c409d96566e910f"
|
||||
dependencies = [
|
||||
"clap_builder",
|
||||
"clap_derive",
|
||||
@@ -262,9 +262,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap_builder"
|
||||
version = "4.5.38"
|
||||
version = "4.5.40"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "379026ff283facf611b0ea629334361c4211d1b12ee01024eec1591133b04120"
|
||||
checksum = "e0c66c08ce9f0c698cbce5c0279d0bb6ac936d8674174fe48f736533b964f59e"
|
||||
dependencies = [
|
||||
"anstream",
|
||||
"anstyle",
|
||||
@@ -274,9 +274,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap_derive"
|
||||
version = "4.5.32"
|
||||
version = "4.5.40"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09176aae279615badda0765c0c0b3f6ed53f4709118af73cf4655d85d1530cd7"
|
||||
checksum = "d2c7947ae4cc3d851207c1adb5b5e260ff0cca11446b1d6d1423788e442257ce"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"proc-macro2",
|
||||
@@ -286,15 +286,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap_lex"
|
||||
version = "0.7.4"
|
||||
version = "0.7.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6"
|
||||
checksum = "b94f61472cee1439c0b966b47e3aca9ae07e45d070759512cd390ea2bebc6675"
|
||||
|
||||
[[package]]
|
||||
name = "colorchoice"
|
||||
version = "1.0.3"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990"
|
||||
checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75"
|
||||
|
||||
[[package]]
|
||||
name = "const_format"
|
||||
@@ -361,7 +361,7 @@ version = "1.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"cfg-if 1.0.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -495,7 +495,7 @@ version = "0.8.35"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"cfg-if 1.0.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -542,12 +542,12 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "errno"
|
||||
version = "0.3.11"
|
||||
version = "0.3.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "976dd42dc7e85965fe702eb8164f21f450704bdde31faefd6471dba214cb594e"
|
||||
checksum = "cea14ef9355e3beab063703aa9dab15afd25f0667c341310c1e5274bb1d0da18"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys 0.52.0",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -579,9 +579,9 @@ checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
|
||||
|
||||
[[package]]
|
||||
name = "flate2"
|
||||
version = "1.1.1"
|
||||
version = "1.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7ced92e76e966ca2fd84c8f7aa01a4aea65b0eb6648d72f7c8f3e2764a67fece"
|
||||
checksum = "4a3d7db9596fecd151c5f638c0ee5d5bd487b6e0ea232e5dc96d5250f6f94b1d"
|
||||
dependencies = [
|
||||
"crc32fast",
|
||||
"libz-rs-sys",
|
||||
@@ -600,9 +600,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "getopts"
|
||||
version = "0.2.21"
|
||||
version = "0.2.23"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "14dbbfd5c71d70241ecf9e6f13737f7b5ce823821063188d7e46c41d371eebd5"
|
||||
checksum = "cba6ae63eb948698e300f645f87c70f76630d505f23b8907cf1e193ee85048c1"
|
||||
dependencies = [
|
||||
"unicode-width",
|
||||
]
|
||||
@@ -613,7 +613,7 @@ version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"cfg-if 1.0.1",
|
||||
"libc",
|
||||
"r-efi",
|
||||
"wasi",
|
||||
@@ -637,9 +637,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.15.2"
|
||||
version = "0.15.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289"
|
||||
checksum = "5971ac85611da7067dbfcabef3c70ebb5606018acd9e2a3903a0da507521e0d5"
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
@@ -729,7 +729,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e"
|
||||
dependencies = [
|
||||
"equivalent",
|
||||
"hashbrown 0.15.2",
|
||||
"hashbrown 0.15.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -828,9 +828,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.172"
|
||||
version = "0.2.173"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa"
|
||||
checksum = "d8cfeafaffdbc32176b64fb251369d52ea9f0a8fbc6f8759edffef7b525d64bb"
|
||||
|
||||
[[package]]
|
||||
name = "libflate"
|
||||
@@ -864,9 +864,9 @@ checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de"
|
||||
|
||||
[[package]]
|
||||
name = "libz-rs-sys"
|
||||
version = "0.5.0"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6489ca9bd760fe9642d7644e827b0c9add07df89857b0416ee15c1cc1a3b8c5a"
|
||||
checksum = "172a788537a2221661b480fee8dc5f96c580eb34fa88764d3205dc356c7e4221"
|
||||
dependencies = [
|
||||
"zlib-rs",
|
||||
]
|
||||
@@ -921,9 +921,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.7.4"
|
||||
version = "2.7.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
|
||||
checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0"
|
||||
|
||||
[[package]]
|
||||
name = "memmap"
|
||||
@@ -937,9 +937,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "miniz_oxide"
|
||||
version = "0.8.8"
|
||||
version = "0.8.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3be647b768db090acb35d5ec5db2b0e1f1de11133ca123b9eacf5137868f892a"
|
||||
checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316"
|
||||
dependencies = [
|
||||
"adler2",
|
||||
]
|
||||
@@ -985,12 +985,9 @@ checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
|
||||
|
||||
[[package]]
|
||||
name = "once_cell_polyfill"
|
||||
version = "1.70.0"
|
||||
version = "1.70.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2611b99ab098a31bdc8be48b4f1a285ca0ced28bd5b4f23e45efa8c63b09efa5"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
]
|
||||
checksum = "a4895175b425cb1f87721b59f0f286c2092bd4af812243672510e1ac53e2e0ad"
|
||||
|
||||
[[package]]
|
||||
name = "pin-project-lite"
|
||||
@@ -1025,7 +1022,7 @@ version = "0.17.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cc5b72d8145275d844d4b5f6d4e1eef00c8cd889edb6035c21675d1bb1f45c9f"
|
||||
dependencies = [
|
||||
"bitflags 2.9.0",
|
||||
"bitflags 2.9.1",
|
||||
"chrono",
|
||||
"flate2",
|
||||
"hex",
|
||||
@@ -1039,7 +1036,7 @@ version = "0.17.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "239df02d8349b06fc07398a3a1697b06418223b1c7725085e801e7c0fc6a12ec"
|
||||
dependencies = [
|
||||
"bitflags 2.9.0",
|
||||
"bitflags 2.9.1",
|
||||
"chrono",
|
||||
"hex",
|
||||
]
|
||||
@@ -1128,17 +1125,17 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rustc-demangle"
|
||||
version = "0.1.24"
|
||||
version = "0.1.25"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f"
|
||||
checksum = "989e6739f80c4ad5b13e0fd7fe89531180375b18520cc8c82080e4dc4035b84f"
|
||||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "0.38.34"
|
||||
source = "git+https://github.com/KernelSU-Next/rustix.git?branch=main#4a53fbc7cb7a07cabe87125cc21dbc27db316259"
|
||||
dependencies = [
|
||||
"bitflags 2.9.0",
|
||||
"errno 0.3.11",
|
||||
"bitflags 2.9.1",
|
||||
"errno 0.3.12",
|
||||
"itoa",
|
||||
"libc",
|
||||
"linux-raw-sys 0.4.15",
|
||||
@@ -1152,31 +1149,31 @@ version = "0.38.44"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154"
|
||||
dependencies = [
|
||||
"bitflags 2.9.0",
|
||||
"errno 0.3.11",
|
||||
"bitflags 2.9.1",
|
||||
"errno 0.3.12",
|
||||
"libc",
|
||||
"linux-raw-sys 0.4.15",
|
||||
"windows-sys 0.52.0",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "1.0.5"
|
||||
version = "1.0.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d97817398dd4bb2e6da002002db259209759911da105da92bec29ccb12cf58bf"
|
||||
checksum = "c71e83d6afe7ff64890ec6b71d6a69bb8a610ab78ce364b3352876bb4c801266"
|
||||
dependencies = [
|
||||
"bitflags 2.9.0",
|
||||
"errno 0.3.11",
|
||||
"bitflags 2.9.1",
|
||||
"errno 0.3.12",
|
||||
"libc",
|
||||
"linux-raw-sys 0.9.4",
|
||||
"windows-sys 0.52.0",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustversion"
|
||||
version = "1.0.20"
|
||||
version = "1.0.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2"
|
||||
checksum = "8a0d197bd2c9dc6e53b84da9556a69ba4cdfab8619eb41a8bd1cc2027a0f6b1d"
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
@@ -1231,7 +1228,7 @@ version = "0.10.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"cfg-if 1.0.1",
|
||||
"cpufeatures",
|
||||
"digest",
|
||||
]
|
||||
@@ -1242,7 +1239,7 @@ version = "0.10.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"cfg-if 1.0.1",
|
||||
"cpufeatures",
|
||||
"digest",
|
||||
]
|
||||
@@ -1280,9 +1277,9 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.101"
|
||||
version = "2.0.103"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf"
|
||||
checksum = "e4307e30089d6fd6aff212f2da3a1f9e32f3223b1f010fb09b7c95f90f3ca1e8"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -1298,8 +1295,8 @@ dependencies = [
|
||||
"fastrand",
|
||||
"getrandom",
|
||||
"once_cell",
|
||||
"rustix 1.0.5",
|
||||
"windows-sys 0.52.0",
|
||||
"rustix 1.0.7",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1343,9 +1340,9 @@ checksum = "c9e9a38711f559d9e3ce1cdb06dd7c5b8ea546bc90052da6d06bb76da74bb07c"
|
||||
|
||||
[[package]]
|
||||
name = "tokio"
|
||||
version = "1.45.0"
|
||||
version = "1.45.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2513ca694ef9ede0fb23fe71a4ee4107cb102b9dc1930f6d0fd77aae068ae165"
|
||||
checksum = "75ef51a33ef1da925cea3e4eb122833cb377c61439ca401b770f54902b806779"
|
||||
dependencies = [
|
||||
"backtrace",
|
||||
"bytes",
|
||||
@@ -1366,9 +1363,9 @@ checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-width"
|
||||
version = "0.1.14"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af"
|
||||
checksum = "4a1a07cc7db3810833284e8d372ccdc6da29741639ecc70c9ec107df0fa6154c"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-xid"
|
||||
@@ -1413,7 +1410,7 @@ version = "0.2.100"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"cfg-if 1.0.1",
|
||||
"once_cell",
|
||||
"rustversion",
|
||||
"wasm-bindgen-macro",
|
||||
@@ -1467,13 +1464,12 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "which"
|
||||
version = "7.0.3"
|
||||
version = "8.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "24d643ce3fd3e5b54854602a080f34fb10ab75e0b813ee32d00ca2b44fa74762"
|
||||
checksum = "d3fabb953106c3c8eea8306e4393700d7657561cb43122571b172bbfb7c7ba1d"
|
||||
dependencies = [
|
||||
"either",
|
||||
"env_home",
|
||||
"rustix 1.0.5",
|
||||
"rustix 1.0.7",
|
||||
"winsafe",
|
||||
]
|
||||
|
||||
@@ -1499,7 +1495,7 @@ version = "0.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
|
||||
dependencies = [
|
||||
"windows-sys 0.52.0",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1545,9 +1541,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windows-link"
|
||||
version = "0.1.1"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38"
|
||||
checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a"
|
||||
|
||||
[[package]]
|
||||
name = "windows-result"
|
||||
@@ -1661,7 +1657,7 @@ version = "0.39.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1"
|
||||
dependencies = [
|
||||
"bitflags 2.9.0",
|
||||
"bitflags 2.9.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1722,9 +1718,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "zlib-rs"
|
||||
version = "0.5.0"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "868b928d7949e09af2f6086dfc1e01936064cc7a819253bce650d4e2a2d63ba8"
|
||||
checksum = "626bd9fa9734751fc50d6060752170984d7053f5a39061f524cda68023d4db8a"
|
||||
|
||||
[[package]]
|
||||
name = "zopfli"
|
||||
|
||||
@@ -37,7 +37,7 @@ rust-embed = { version = "8", features = [
|
||||
"debug-embed",
|
||||
"compression", # must clean build after updating binaries
|
||||
] }
|
||||
which = "7"
|
||||
which = "8"
|
||||
getopts = "0.2"
|
||||
sha256 = "1"
|
||||
sha1 = "0.10"
|
||||
|
||||
@@ -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