Compare commits
211 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9919d573fe | ||
|
|
d73af38bf4 | ||
|
|
dfe7852c25 | ||
|
|
abe6184f63 | ||
|
|
e8afb2143b | ||
|
|
7e1c363bad | ||
|
|
bcf6809deb | ||
|
|
bce5f6cf61 | ||
|
|
5495eebb38 | ||
|
|
d255801666 | ||
|
|
44f2601126 | ||
|
|
3a7366c4bc | ||
|
|
5da289714d | ||
|
|
a18d718744 | ||
|
|
c90fc461d9 | ||
|
|
e80fbe8934 | ||
|
|
e0650ade4f | ||
|
|
378b8458f2 | ||
|
|
182028d9ea | ||
|
|
0c3b8e7610 | ||
|
|
332fdcd2a7 | ||
|
|
aa20d04d3a | ||
|
|
949106bc09 | ||
|
|
67babc2858 | ||
|
|
d087ec510e | ||
|
|
c2ed3da87c | ||
|
|
f2d159f732 | ||
|
|
468fc2207d | ||
|
|
eced8bae82 | ||
|
|
e61ecb3963 | ||
|
|
304f4f8b2c | ||
|
|
48888087e1 | ||
|
|
75a70e70be | ||
|
|
cf825f912c | ||
|
|
1944a49fd8 | ||
|
|
657f343f5c | ||
|
|
74bb90b3d8 | ||
|
|
8bf9828c80 | ||
|
|
06324def38 | ||
|
|
70259a5ec5 | ||
|
|
a63057c594 | ||
|
|
48d5270611 | ||
|
|
712d0f3342 | ||
|
|
d6084aeca1 | ||
|
|
562b9624d7 | ||
|
|
a68d5e8bbe | ||
|
|
d45aa8197e | ||
|
|
314d3ef97a | ||
|
|
e3750ccd51 | ||
|
|
a712efe9d8 | ||
|
|
2266362e24 | ||
|
|
b7056b5baa | ||
|
|
8bd07bf56c | ||
|
|
569183efe9 | ||
|
|
937cf25e9b | ||
|
|
e2b6617577 | ||
|
|
8323d6394b | ||
|
|
e31b892a20 | ||
|
|
65b1518e26 | ||
|
|
865dbd3799 | ||
|
|
7de9d7967a | ||
|
|
aa2d2454e1 | ||
|
|
b850336872 | ||
|
|
138dec35c7 | ||
|
|
569fffa962 | ||
|
|
2a283e6793 | ||
|
|
73a7ba3ac9 | ||
|
|
c0c4ea9f86 | ||
|
|
a13179cd09 | ||
|
|
de089b7b73 | ||
|
|
1b700fb8e0 | ||
|
|
f0febf13f2 | ||
|
|
a002967a92 | ||
|
|
1167b20d89 | ||
|
|
297ff3ae90 | ||
|
|
a39e2ce15a | ||
|
|
78eda275d6 | ||
|
|
1fce0fd77d | ||
|
|
c942393f21 | ||
|
|
040cc30e73 | ||
|
|
6b75ffc928 | ||
|
|
a7f1b21f91 | ||
|
|
c8f7d9d5bc | ||
|
|
2520d45dc4 | ||
|
|
8bdf8d98c3 | ||
|
|
04025f3d32 | ||
|
|
4c7ed9c8ee | ||
|
|
c36f8b0df3 | ||
|
|
5ccec93940 | ||
|
|
14b15e18f3 | ||
|
|
3e6f3b4d80 | ||
|
|
470b3106cb | ||
|
|
e0074bc3ab | ||
|
|
8d04ecdc52 | ||
|
|
c83b1e88b9 | ||
|
|
0446cc499e | ||
|
|
8a6507e834 | ||
|
|
09893b9472 | ||
|
|
13d2290205 | ||
|
|
46e4c85563 | ||
|
|
d925036bd6 | ||
|
|
fbf2799674 | ||
|
|
12c7558b91 | ||
|
|
d55af76260 | ||
|
|
e4c70e2efb | ||
|
|
a971fee132 | ||
|
|
a45e0f78ef | ||
|
|
bf07dcb9ea | ||
|
|
e5f5b8f831 | ||
|
|
a80513fc50 | ||
|
|
f71de1742a | ||
|
|
57c65fdcda | ||
|
|
0a9cf5f9aa | ||
|
|
08fdf2bdad | ||
|
|
690c6bac38 | ||
|
|
af81308097 | ||
|
|
90b79f5c04 | ||
|
|
0c5dcec7bc | ||
|
|
a30dfbc15d | ||
|
|
52f3335977 | ||
|
|
c8b3e953ad | ||
|
|
e9d0526e1b | ||
|
|
408c3be675 | ||
|
|
f67f16733f | ||
|
|
25a173ad7b | ||
|
|
75ec88f7a7 | ||
|
|
e9c5ffb430 | ||
|
|
cb2cdaed12 | ||
|
|
029c7f1e2a | ||
|
|
5a8d6895fa | ||
|
|
bb11e23006 | ||
|
|
37b00d49c8 | ||
|
|
8055aed507 | ||
|
|
e7cef05c6a | ||
|
|
313746b578 | ||
|
|
a7c557222c | ||
|
|
9c902fb264 | ||
|
|
079f74d960 | ||
|
|
00d7de5276 | ||
|
|
3e928365de | ||
|
|
47ba174fb1 | ||
|
|
9446296daa | ||
|
|
7175a6fa7d | ||
|
|
1b06f7d317 | ||
|
|
c739bf6bfb | ||
|
|
dec9a72b41 | ||
|
|
593cbaa067 | ||
|
|
fb8906e371 | ||
|
|
df943250ac | ||
|
|
5a522a1489 | ||
|
|
0b0d64b9d0 | ||
|
|
18876e8a69 | ||
|
|
b668378e23 | ||
|
|
9de2c09a27 | ||
|
|
6b3d2bef12 | ||
|
|
344ed41bc7 | ||
|
|
2e711c3ac9 | ||
|
|
1bf4486cf1 | ||
|
|
cb116286ed | ||
|
|
7f0ae95dfb | ||
|
|
fe7ec9dcf5 | ||
|
|
d88eccdda3 | ||
|
|
78fe01d9a4 | ||
|
|
60cb41c76b | ||
|
|
af78f3bac4 | ||
|
|
fff86dcc8d | ||
|
|
5ec053ca34 | ||
|
|
e9f1631b06 | ||
|
|
3705993330 | ||
|
|
3db338da3e | ||
|
|
d126d0f5b8 | ||
|
|
549adebb30 | ||
|
|
40bada35c6 | ||
|
|
074903a299 | ||
|
|
328bee94e5 | ||
|
|
0db25f14f1 | ||
|
|
877e4f9416 | ||
|
|
8b3e864ffa | ||
|
|
5f5f677b7b | ||
|
|
3933d83d3e | ||
|
|
4abd35fb44 | ||
|
|
e68afb04eb | ||
|
|
bf2be96b29 | ||
|
|
b0b5048b01 | ||
|
|
aff69af690 | ||
|
|
c1d156cd6b | ||
|
|
e58e00be9d | ||
|
|
440fe972f4 | ||
|
|
e24588b961 | ||
|
|
57c8d69e83 | ||
|
|
79c0bebcf5 | ||
|
|
321c9c20d5 | ||
|
|
f6134b47da | ||
|
|
8c282b28a0 | ||
|
|
656cd11876 | ||
|
|
5e77c08872 | ||
|
|
1090f64117 | ||
|
|
470aaa29dc | ||
|
|
c6664af45b | ||
|
|
d6b0ce2565 | ||
|
|
770c9632ae | ||
|
|
cd60773d73 | ||
|
|
06cdd92129 | ||
|
|
315df33bd6 | ||
|
|
f990bda4e5 | ||
|
|
b755ad3602 | ||
|
|
b060b2827e | ||
|
|
73493b288f | ||
|
|
87640fb824 | ||
|
|
fb0a48f9db | ||
|
|
acc8670aa9 |
28
.github/dependabot.yml
vendored
28
.github/dependabot.yml
vendored
@@ -1,28 +0,0 @@
|
|||||||
version: 2
|
|
||||||
updates:
|
|
||||||
- package-ecosystem: github-actions
|
|
||||||
directory: /
|
|
||||||
schedule:
|
|
||||||
interval: daily
|
|
||||||
groups:
|
|
||||||
actions:
|
|
||||||
patterns:
|
|
||||||
- "*"
|
|
||||||
- package-ecosystem: cargo
|
|
||||||
directory: userspace/ksud
|
|
||||||
schedule:
|
|
||||||
interval: daily
|
|
||||||
allow:
|
|
||||||
- dependency-type: "all"
|
|
||||||
groups:
|
|
||||||
crates:
|
|
||||||
patterns:
|
|
||||||
- "*"
|
|
||||||
- package-ecosystem: gradle
|
|
||||||
directory: manager
|
|
||||||
schedule:
|
|
||||||
interval: daily
|
|
||||||
groups:
|
|
||||||
maven:
|
|
||||||
patterns:
|
|
||||||
- "*"
|
|
||||||
137
.github/workflows/avd-kernel.yml
vendored
137
.github/workflows/avd-kernel.yml
vendored
@@ -1,137 +0,0 @@
|
|||||||
name: GKI Kernel Build
|
|
||||||
|
|
||||||
on:
|
|
||||||
workflow_call:
|
|
||||||
inputs:
|
|
||||||
version_name:
|
|
||||||
required: true
|
|
||||||
type: string
|
|
||||||
description: >
|
|
||||||
With SUBLEVEL of kernel,
|
|
||||||
for example: android12-5.10.66
|
|
||||||
arch:
|
|
||||||
required: true
|
|
||||||
type: string
|
|
||||||
description: >
|
|
||||||
Build arch: aarch64/x86_64
|
|
||||||
debug:
|
|
||||||
required: false
|
|
||||||
type: boolean
|
|
||||||
default: true
|
|
||||||
manifest_name:
|
|
||||||
required: false
|
|
||||||
type: string
|
|
||||||
description: >
|
|
||||||
Local repo manifest xml path,
|
|
||||||
typically for AVD kernel build.
|
|
||||||
secrets:
|
|
||||||
BOOT_SIGN_KEY:
|
|
||||||
required: false
|
|
||||||
CHAT_ID:
|
|
||||||
required: false
|
|
||||||
BOT_TOKEN:
|
|
||||||
required: false
|
|
||||||
MESSAGE_THREAD_ID:
|
|
||||||
required: false
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build:
|
|
||||||
name: Build ${{ inputs.version_name }}
|
|
||||||
runs-on: ubuntu-22.04
|
|
||||||
steps:
|
|
||||||
- name: Maximize build space
|
|
||||||
uses: easimon/maximize-build-space@master
|
|
||||||
with:
|
|
||||||
root-reserve-mb: 8192
|
|
||||||
temp-reserve-mb: 2048
|
|
||||||
remove-dotnet: 'true'
|
|
||||||
remove-android: 'true'
|
|
||||||
remove-haskell: 'true'
|
|
||||||
remove-codeql: 'true'
|
|
||||||
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
path: KernelSU
|
|
||||||
fetch-depth: 0
|
|
||||||
|
|
||||||
- name: Setup need_upload
|
|
||||||
id: need_upload
|
|
||||||
run: |
|
|
||||||
if [ ! -z "${{ secrets.BOT_TOKEN }}" ]; then
|
|
||||||
echo "UPLOAD=true" >> $GITHUB_OUTPUT
|
|
||||||
else
|
|
||||||
echo "UPLOAD=false" >> $GITHUB_OUTPUT
|
|
||||||
fi
|
|
||||||
|
|
||||||
- name: Setup kernel source
|
|
||||||
run: |
|
|
||||||
echo "Free space:"
|
|
||||||
df -h
|
|
||||||
cd $GITHUB_WORKSPACE
|
|
||||||
sudo apt-get install repo -y
|
|
||||||
mkdir android-kernel && cd android-kernel
|
|
||||||
repo init --depth=1 -u https://android.googlesource.com/kernel/manifest -m "$GITHUB_WORKSPACE/KernelSU/.github/manifests/${{ inputs.manifest_name }}" --repo-rev=v2.16
|
|
||||||
repo --version
|
|
||||||
repo --trace sync -c -j$(nproc --all) --no-tags
|
|
||||||
df -h
|
|
||||||
|
|
||||||
- name: Setup KernelSU
|
|
||||||
env:
|
|
||||||
PATCH_PATH: ${{ inputs.patch_path }}
|
|
||||||
IS_DEBUG_KERNEL: ${{ inputs.debug }}
|
|
||||||
run: |
|
|
||||||
cd $GITHUB_WORKSPACE/android-kernel
|
|
||||||
echo "[+] KernelSU setup"
|
|
||||||
GKI_ROOT=$(pwd)
|
|
||||||
echo "[+] GKI_ROOT: $GKI_ROOT"
|
|
||||||
echo "[+] Copy KernelSU driver to $GKI_ROOT/common/drivers"
|
|
||||||
ln -sf $GITHUB_WORKSPACE/KernelSU/kernel $GKI_ROOT/common/drivers/kernelsu
|
|
||||||
echo "[+] Add KernelSU driver to Makefile"
|
|
||||||
DRIVER_MAKEFILE=$GKI_ROOT/common/drivers/Makefile
|
|
||||||
DRIVER_KCONFIG=$GKI_ROOT/common/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 $GKI_ROOT/common/ && git apply $GITHUB_WORKSPACE/KernelSU/.github/patches/$PATCH_PATH/*.patch || echo "[-] No patch found"
|
|
||||||
|
|
||||||
if [ "$IS_DEBUG_KERNEL" = "true" ]; then
|
|
||||||
echo "[+] Enable debug features for kernel"
|
|
||||||
printf "\nccflags-y += -DCONFIG_KSU_DEBUG\n" >> $GITHUB_WORKSPACE/KernelSU/kernel/Makefile
|
|
||||||
fi
|
|
||||||
repo status
|
|
||||||
echo "[+] KernelSU setup done."
|
|
||||||
cd $GITHUB_WORKSPACE/KernelSU
|
|
||||||
VERSION=$(($(git rev-list --count HEAD) + 10200))
|
|
||||||
echo "VERSION: $VERSION"
|
|
||||||
echo "kernelsu_version=$VERSION" >> $GITHUB_ENV
|
|
||||||
|
|
||||||
- name: Make working directory clean to avoid dirty
|
|
||||||
working-directory: android-kernel
|
|
||||||
run: |
|
|
||||||
rm common/android/abi_gki_protected_exports_* || echo "No protected exports!"
|
|
||||||
git config --global user.email "bot@kernelsu.org"
|
|
||||||
git config --global user.name "KernelSUBot"
|
|
||||||
cd common/ && git add -A && git commit -a -m "Add KernelSU"
|
|
||||||
repo status
|
|
||||||
|
|
||||||
- name: Build kernel
|
|
||||||
working-directory: android-kernel
|
|
||||||
run: |
|
|
||||||
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
|
|
||||||
tools/bazel run --config=fast --config=stamp --lto=thin //common-modules/virtual-device:virtual_device_${{ inputs.arch }}_dist -- --dist_dir=dist
|
|
||||||
NAME=kernel-${{ inputs.arch }}-avd-${{ inputs.version_name }}-${{ env.kernelsu_version }}
|
|
||||||
TARGET_IMAGE=dist/bzImage
|
|
||||||
if [ ! -e $TARGET_IMAGE ]; then
|
|
||||||
TARGET_IMAGE=dist/Image
|
|
||||||
fi
|
|
||||||
mv $TARGET_IMAGE $NAME
|
|
||||||
echo "file_path=android-kernel/$NAME" >> $GITHUB_ENV
|
|
||||||
|
|
||||||
- name: Upload Kernel
|
|
||||||
uses: actions/upload-artifact@v4
|
|
||||||
with:
|
|
||||||
name: kernel-${{ inputs.arch }}-avd-${{ inputs.version_name }}-${{ env.kernelsu_version }}
|
|
||||||
path: "${{ env.file_path }}"
|
|
||||||
74
.github/workflows/build-lkm.yml
vendored
74
.github/workflows/build-lkm.yml
vendored
@@ -1,74 +0,0 @@
|
|||||||
name: Build LKM for KernelSU
|
|
||||||
on:
|
|
||||||
workflow_call:
|
|
||||||
inputs:
|
|
||||||
upload:
|
|
||||||
required: false
|
|
||||||
type: boolean
|
|
||||||
default: false
|
|
||||||
description: "Whether to upload to branch"
|
|
||||||
secrets:
|
|
||||||
# username:github_pat
|
|
||||||
TOKEN:
|
|
||||||
required: true
|
|
||||||
workflow_dispatch:
|
|
||||||
inputs:
|
|
||||||
upload:
|
|
||||||
required: false
|
|
||||||
type: boolean
|
|
||||||
default: false
|
|
||||||
description: "Whether to upload to branch"
|
|
||||||
jobs:
|
|
||||||
build-lkm:
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
include:
|
|
||||||
- version: "android12-5.10"
|
|
||||||
sub_level: 233
|
|
||||||
os_patch_level: 2025-02
|
|
||||||
- version: "android13-5.10"
|
|
||||||
sub_level: 228
|
|
||||||
os_patch_level: 2025-01
|
|
||||||
- version: "android13-5.15"
|
|
||||||
sub_level: 170
|
|
||||||
os_patch_level: 2025-01
|
|
||||||
- version: "android14-5.15"
|
|
||||||
sub_level: 170
|
|
||||||
os_patch_level: 2025-01
|
|
||||||
- version: "android14-6.1"
|
|
||||||
sub_level: 128
|
|
||||||
os_patch_level: 2025-03
|
|
||||||
- version: "android15-6.6"
|
|
||||||
sub_level: 77
|
|
||||||
os_patch_level: 2025-03
|
|
||||||
# uses: ./.github/workflows/gki-kernel-mock.yml when debugging
|
|
||||||
uses: ./.github/workflows/gki-kernel.yml
|
|
||||||
with:
|
|
||||||
version: ${{ matrix.version }}
|
|
||||||
version_name: ${{ matrix.version }}.${{ matrix.sub_level }}
|
|
||||||
tag: ${{ matrix.version }}-${{ matrix.os_patch_level }}
|
|
||||||
os_patch_level: ${{ matrix.os_patch_level }}
|
|
||||||
build_lkm: true
|
|
||||||
|
|
||||||
push-to-branch:
|
|
||||||
needs: [build-lkm]
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
if: ${{ inputs.upload }}
|
|
||||||
steps:
|
|
||||||
- name: Download all workflow run artifacts
|
|
||||||
uses: actions/download-artifact@v4
|
|
||||||
with:
|
|
||||||
path: bin/
|
|
||||||
merge-multiple: true
|
|
||||||
- name: Push to branch LKM
|
|
||||||
run: |
|
|
||||||
cd bin
|
|
||||||
git config --global init.defaultBranch lkm
|
|
||||||
git init
|
|
||||||
git remote add origin https://${{ secrets.TOKEN }}@github.com/${{ github.repository }}
|
|
||||||
git config --local user.name "github-actions[bot]"
|
|
||||||
git config --local user.email "41898282+github-actions[bot]@users.noreply.github.com"
|
|
||||||
find . -type f
|
|
||||||
git add .
|
|
||||||
git commit -m "Upload LKM from ${{ github.sha }}" -m "https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}"
|
|
||||||
git push --force --set-upstream origin lkm
|
|
||||||
94
.github/workflows/build-manager.yml
vendored
94
.github/workflows/build-manager.yml
vendored
@@ -8,7 +8,7 @@ on:
|
|||||||
- 'manager/**'
|
- 'manager/**'
|
||||||
- 'kernel/**'
|
- 'kernel/**'
|
||||||
- 'userspace/ksud/**'
|
- 'userspace/ksud/**'
|
||||||
- 'userspace/zakomksd/**'
|
- 'userspace/susfs/**'
|
||||||
- 'userspace/kpmmgr/**'
|
- 'userspace/kpmmgr/**'
|
||||||
pull_request:
|
pull_request:
|
||||||
branches: [ "main" ]
|
branches: [ "main" ]
|
||||||
@@ -16,89 +16,20 @@ on:
|
|||||||
- 'manager/**'
|
- 'manager/**'
|
||||||
workflow_call:
|
workflow_call:
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
inputs:
|
|
||||||
build_lkm:
|
|
||||||
required: true
|
|
||||||
type: choice
|
|
||||||
default: "auto"
|
|
||||||
options:
|
|
||||||
- "true"
|
|
||||||
- "false"
|
|
||||||
- "auto"
|
|
||||||
description: "Whether to build lkm"
|
|
||||||
upload_lkm:
|
|
||||||
required: true
|
|
||||||
type: boolean
|
|
||||||
default: false
|
|
||||||
description: "Whether to upload lkm"
|
|
||||||
jobs:
|
jobs:
|
||||||
check-build-lkm:
|
build-susfs:
|
||||||
runs-on: ubuntu-latest
|
|
||||||
outputs:
|
|
||||||
build_lkm: ${{ steps.check-build.outputs.build_lkm }}
|
|
||||||
upload_lkm: ${{ steps.check-build.outputs.upload_lkm }}
|
|
||||||
steps:
|
|
||||||
- name: check build
|
|
||||||
id: check-build
|
|
||||||
run: |
|
|
||||||
if [ "${{ github.event_name }}" == "workflow_dispatch" ] && [ "${{ inputs.build_lkm }}" != "auto" ]; then
|
|
||||||
kernel_changed="${{ inputs.build_lkm }}"
|
|
||||||
else
|
|
||||||
kernel_changed=true
|
|
||||||
mkdir tmp
|
|
||||||
cd tmp
|
|
||||||
git config --global init.defaultBranch bot
|
|
||||||
git config --global user.name 'Bot'
|
|
||||||
git config --global user.email 'bot@github.shirkneko.io'
|
|
||||||
git init .
|
|
||||||
git remote add origin https://github.com/${{ github.repository }}
|
|
||||||
CURRENT_COMMIT="${{ github.event.head_commit.id }}"
|
|
||||||
git fetch origin $CURRENT_COMMIT --depth=1
|
|
||||||
git fetch origin lkm --depth=1
|
|
||||||
LKM_COMMIT="$(git log --format=%B -n 1 origin/lkm | head -n 1)"
|
|
||||||
LKM_COMMIT="${LKM_COMMIT#Upload LKM from }"
|
|
||||||
LKM_COMMIT=$(echo "$LKM_COMMIT" | tr -d '[:space:]')
|
|
||||||
echo "LKM_COMMIT=$LKM_COMMIT"
|
|
||||||
git fetch origin "$LKM_COMMIT" --depth=1
|
|
||||||
git diff --quiet "$LKM_COMMIT" "$CURRENT_COMMIT" -- kernel :!kernel/setup.sh .github/workflows/build-lkm.yml .github/workflows/build-kernel-*.yml && kernel_changed=false
|
|
||||||
cd ..
|
|
||||||
rm -rf tmp
|
|
||||||
fi
|
|
||||||
if [ "${{ github.event_name }}" == "push" ] && [ "${{ github.ref }}" == 'refs/heads/susfs' ]; then
|
|
||||||
need_upload=true
|
|
||||||
elif [ "${{ github.event_name }}" == "workflow_dispatch" ]; then
|
|
||||||
need_upload="${{ inputs.upload_lkm }}"
|
|
||||||
else
|
|
||||||
need_upload=false
|
|
||||||
fi
|
|
||||||
echo "kernel changed: $kernel_changed"
|
|
||||||
echo "need upload: $need_upload"
|
|
||||||
echo "build_lkm=$kernel_changed" >> "$GITHUB_OUTPUT"
|
|
||||||
echo "upload_lkm=$need_upload" >> "$GITHUB_OUTPUT"
|
|
||||||
|
|
||||||
build-lkm:
|
|
||||||
needs: check-build-lkm
|
|
||||||
uses: ./.github/workflows/build-lkm.yml
|
|
||||||
if: ${{ needs.check-build-lkm.outputs.build_lkm == 'true' }}
|
|
||||||
with:
|
|
||||||
upload: ${{ needs.check-build-lkm.outputs.upload_lkm == 'true' }}
|
|
||||||
secrets: inherit
|
|
||||||
build-zakomksd:
|
|
||||||
if: ${{ always() }}
|
|
||||||
needs: [ check-build-lkm, build-lkm ]
|
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
include:
|
include:
|
||||||
- target: aarch64-linux-android
|
- target: aarch64-linux-android
|
||||||
os: ubuntu-latest
|
os: ubuntu-latest
|
||||||
uses: ./.github/workflows/zakomksd.yml
|
uses: ./.github/workflows/susfs.yml
|
||||||
with:
|
with:
|
||||||
target: ${{ matrix.target }}
|
target: ${{ matrix.target }}
|
||||||
os: ${{ matrix.os }}
|
os: ${{ matrix.os }}
|
||||||
|
|
||||||
build-kpmmgr:
|
build-kpmmgr:
|
||||||
if: ${{ always() }}
|
needs: build-susfs
|
||||||
needs: [ check-build-lkm, build-lkm ]
|
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
include:
|
include:
|
||||||
@@ -110,8 +41,7 @@ jobs:
|
|||||||
os: ${{ matrix.os }}
|
os: ${{ matrix.os }}
|
||||||
|
|
||||||
build-ksud:
|
build-ksud:
|
||||||
if: ${{ always() }}
|
needs: build-kpmmgr
|
||||||
needs: [ check-build-lkm, build-lkm ]
|
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
include:
|
include:
|
||||||
@@ -123,8 +53,6 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
target: ${{ matrix.target }}
|
target: ${{ matrix.target }}
|
||||||
os: ${{ matrix.os }}
|
os: ${{ matrix.os }}
|
||||||
pack_lkm: true
|
|
||||||
pull_lkm: ${{ needs.check-build-lkm.outputs.build_lkm != 'true' }}
|
|
||||||
|
|
||||||
build-manager:
|
build-manager:
|
||||||
if: ${{ always() }}
|
if: ${{ always() }}
|
||||||
@@ -174,10 +102,10 @@ jobs:
|
|||||||
- name: Setup Android SDK
|
- name: Setup Android SDK
|
||||||
uses: android-actions/setup-android@v3
|
uses: android-actions/setup-android@v3
|
||||||
|
|
||||||
- name: Download arm64 zakomksd
|
- name: Download arm64 susfs
|
||||||
uses: actions/download-artifact@v4
|
uses: actions/download-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: zakomksd-aarch64-linux-android
|
name: susfs-aarch64-linux-android
|
||||||
path: .
|
path: .
|
||||||
|
|
||||||
- name: Download arm64 kpmmgr
|
- name: Download arm64 kpmmgr
|
||||||
@@ -202,18 +130,18 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
mkdir -p app/src/main/jniLibs/arm64-v8a
|
mkdir -p app/src/main/jniLibs/arm64-v8a
|
||||||
mkdir -p app/src/main/jniLibs/x86_64
|
mkdir -p app/src/main/jniLibs/x86_64
|
||||||
cp -f ../aarch64-linux-android/release/zakomk ../manager/app/src/main/jniLibs/arm64-v8a/libzakomk.so
|
cp -f ../aarch64-linux-android/release/zakozako ../manager/app/src/main/jniLibs/arm64-v8a/libzakozako.so
|
||||||
cp -f ../x86_64-linux-android/release/zakomk ../manager/app/src/main/jniLibs/x86_64/libzakomk.so
|
cp -f ../x86_64-linux-android/release/zakozako ../manager/app/src/main/jniLibs/x86_64/libzakozako.so
|
||||||
|
|
||||||
- name: Copy kpmmgr to app jniLibs
|
- name: Copy kpmmgr to app jniLibs
|
||||||
run: |
|
run: |
|
||||||
mkdir -p app/src/main/jniLibs/arm64-v8a
|
mkdir -p app/src/main/jniLibs/arm64-v8a
|
||||||
cp -f ../arm64-v8a/kpmmgr ../manager/app/src/main/jniLibs/arm64-v8a/libkpmmgr.so
|
cp -f ../arm64-v8a/kpmmgr ../manager/app/src/main/jniLibs/arm64-v8a/libkpmmgr.so
|
||||||
|
|
||||||
- name: Copy zakomksd to app jniLibs
|
- name: Copy susfs to app jniLibs
|
||||||
run: |
|
run: |
|
||||||
mkdir -p app/src/main/jniLibs/arm64-v8a
|
mkdir -p app/src/main/jniLibs/arm64-v8a
|
||||||
cp -f ../arm64-v8a/zakomksd ../manager/app/src/main/jniLibs/arm64-v8a/libzakomksd.so
|
cp -f ../arm64-v8a/zakozakozako ../manager/app/src/main/jniLibs/arm64-v8a/libzakozakozako.so
|
||||||
|
|
||||||
- name: Build with Gradle
|
- name: Build with Gradle
|
||||||
run: |
|
run: |
|
||||||
|
|||||||
79
.github/workflows/gki-kernel-mock.yml
vendored
79
.github/workflows/gki-kernel-mock.yml
vendored
@@ -1,79 +0,0 @@
|
|||||||
name: GKI Kernel Build
|
|
||||||
|
|
||||||
on:
|
|
||||||
workflow_call:
|
|
||||||
inputs:
|
|
||||||
version:
|
|
||||||
required: true
|
|
||||||
type: string
|
|
||||||
description: >
|
|
||||||
Output directory of gki,
|
|
||||||
for example: android12-5.10
|
|
||||||
version_name:
|
|
||||||
required: true
|
|
||||||
type: string
|
|
||||||
description: >
|
|
||||||
With SUBLEVEL of kernel,
|
|
||||||
for example: android12-5.10.66
|
|
||||||
tag:
|
|
||||||
required: true
|
|
||||||
type: string
|
|
||||||
description: >
|
|
||||||
Part of branch name of common kernel manifest,
|
|
||||||
for example: android12-5.10-2021-11
|
|
||||||
os_patch_level:
|
|
||||||
required: false
|
|
||||||
type: string
|
|
||||||
description: >
|
|
||||||
Patch level of common kernel manifest,
|
|
||||||
for example: 2021-11
|
|
||||||
default: 2022-05
|
|
||||||
patch_path:
|
|
||||||
required: false
|
|
||||||
type: string
|
|
||||||
description: >
|
|
||||||
Directory name of .github/patches/<patch_path>
|
|
||||||
for example: 5.10
|
|
||||||
use_cache:
|
|
||||||
required: false
|
|
||||||
type: boolean
|
|
||||||
default: true
|
|
||||||
embed_ksud:
|
|
||||||
required: false
|
|
||||||
type: string
|
|
||||||
default: ksud-aarch64-linux-android
|
|
||||||
description: >
|
|
||||||
Artifact name of prebuilt ksud to be embedded
|
|
||||||
for example: ksud-aarch64-linux-android
|
|
||||||
debug:
|
|
||||||
required: false
|
|
||||||
type: boolean
|
|
||||||
default: false
|
|
||||||
build_lkm:
|
|
||||||
required: false
|
|
||||||
type: boolean
|
|
||||||
default: false
|
|
||||||
secrets:
|
|
||||||
BOOT_SIGN_KEY:
|
|
||||||
required: false
|
|
||||||
CHAT_ID:
|
|
||||||
required: false
|
|
||||||
BOT_TOKEN:
|
|
||||||
required: false
|
|
||||||
MESSAGE_THREAD_ID:
|
|
||||||
required: false
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
mock_build:
|
|
||||||
name: Mock build ${{ inputs.version_name }}
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Create mocking ko
|
|
||||||
run: |
|
|
||||||
echo "${{ inputs.version }}_kernelsu.ko" > ${{ inputs.version }}_kernelsu.ko
|
|
||||||
- name: Upload LKM
|
|
||||||
uses: actions/upload-artifact@v4
|
|
||||||
if: ${{ inputs.build_lkm == true }}
|
|
||||||
with:
|
|
||||||
name: ${{ inputs.version }}-lkm
|
|
||||||
path: ./*_kernelsu.ko
|
|
||||||
258
.github/workflows/gki-kernel.yml
vendored
258
.github/workflows/gki-kernel.yml
vendored
@@ -1,258 +0,0 @@
|
|||||||
name: GKI Kernel Build
|
|
||||||
|
|
||||||
on:
|
|
||||||
workflow_call:
|
|
||||||
inputs:
|
|
||||||
version:
|
|
||||||
required: true
|
|
||||||
type: string
|
|
||||||
description: >
|
|
||||||
Output directory of gki,
|
|
||||||
for example: android12-5.10
|
|
||||||
version_name:
|
|
||||||
required: true
|
|
||||||
type: string
|
|
||||||
description: >
|
|
||||||
With SUBLEVEL of kernel,
|
|
||||||
for example: android12-5.10.66
|
|
||||||
tag:
|
|
||||||
required: true
|
|
||||||
type: string
|
|
||||||
description: >
|
|
||||||
Part of branch name of common kernel manifest,
|
|
||||||
for example: android12-5.10-2021-11
|
|
||||||
os_patch_level:
|
|
||||||
required: false
|
|
||||||
type: string
|
|
||||||
description: >
|
|
||||||
Patch level of common kernel manifest,
|
|
||||||
for example: 2021-11
|
|
||||||
default: 2022-05
|
|
||||||
patch_path:
|
|
||||||
required: false
|
|
||||||
type: string
|
|
||||||
description: >
|
|
||||||
Directory name of .github/patches/<patch_path>
|
|
||||||
for example: 5.10
|
|
||||||
use_cache:
|
|
||||||
required: false
|
|
||||||
type: boolean
|
|
||||||
default: true
|
|
||||||
embed_ksud:
|
|
||||||
required: false
|
|
||||||
type: string
|
|
||||||
default: ksud-aarch64-linux-android
|
|
||||||
description: >
|
|
||||||
Artifact name of prebuilt ksud to be embedded
|
|
||||||
for example: ksud-aarch64-linux-android
|
|
||||||
debug:
|
|
||||||
required: false
|
|
||||||
type: boolean
|
|
||||||
default: false
|
|
||||||
build_lkm:
|
|
||||||
required: false
|
|
||||||
type: boolean
|
|
||||||
default: false
|
|
||||||
secrets:
|
|
||||||
BOOT_SIGN_KEY:
|
|
||||||
required: false
|
|
||||||
CHAT_ID:
|
|
||||||
required: false
|
|
||||||
BOT_TOKEN:
|
|
||||||
required: false
|
|
||||||
MESSAGE_THREAD_ID:
|
|
||||||
required: false
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build:
|
|
||||||
name: Build ${{ inputs.version_name }}
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
env:
|
|
||||||
CCACHE_COMPILERCHECK: "%compiler% -dumpmachine; %compiler% -dumpversion"
|
|
||||||
CCACHE_NOHASHDIR: "true"
|
|
||||||
CCACHE_HARDLINK: "true"
|
|
||||||
steps:
|
|
||||||
- name: Maximize build space
|
|
||||||
uses: easimon/maximize-build-space@master
|
|
||||||
with:
|
|
||||||
root-reserve-mb: 8192
|
|
||||||
temp-reserve-mb: 2048
|
|
||||||
remove-dotnet: 'true'
|
|
||||||
remove-android: 'true'
|
|
||||||
remove-haskell: 'true'
|
|
||||||
remove-codeql: 'true'
|
|
||||||
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
path: KernelSU
|
|
||||||
fetch-depth: 0
|
|
||||||
|
|
||||||
- name: Setup need_upload
|
|
||||||
id: need_upload
|
|
||||||
run: |
|
|
||||||
if [ ! -z "${{ secrets.BOT_TOKEN }}" ]; then
|
|
||||||
echo "UPLOAD=true" >> $GITHUB_OUTPUT
|
|
||||||
else
|
|
||||||
echo "UPLOAD=false" >> $GITHUB_OUTPUT
|
|
||||||
fi
|
|
||||||
|
|
||||||
- name: Setup kernel source
|
|
||||||
run: |
|
|
||||||
echo "Free space:"
|
|
||||||
df -h
|
|
||||||
cd $GITHUB_WORKSPACE
|
|
||||||
sudo apt-get install repo -y
|
|
||||||
mkdir android-kernel && cd android-kernel
|
|
||||||
repo init --depth=1 --u https://android.googlesource.com/kernel/manifest -b common-${{ inputs.tag }} --repo-rev=v2.35
|
|
||||||
REMOTE_BRANCH=$(git ls-remote https://android.googlesource.com/kernel/common ${{ inputs.tag }})
|
|
||||||
DEFAULT_MANIFEST_PATH=.repo/manifests/default.xml
|
|
||||||
if grep -q deprecated <<< $REMOTE_BRANCH; then
|
|
||||||
echo "Found deprecated branch: ${{ inputs.tag }}"
|
|
||||||
sed -i 's/"${{ inputs.tag }}"/"deprecated\/${{ inputs.tag }}"/g' $DEFAULT_MANIFEST_PATH
|
|
||||||
cat $DEFAULT_MANIFEST_PATH
|
|
||||||
fi
|
|
||||||
repo --version
|
|
||||||
repo --trace sync -c -j$(nproc --all) --no-tags
|
|
||||||
df -h
|
|
||||||
|
|
||||||
- name: Setup KernelSU
|
|
||||||
env:
|
|
||||||
PATCH_PATH: ${{ inputs.patch_path }}
|
|
||||||
IS_DEBUG_KERNEL: ${{ inputs.debug }}
|
|
||||||
run: |
|
|
||||||
cd $GITHUB_WORKSPACE/android-kernel
|
|
||||||
echo "[+] KernelSU setup"
|
|
||||||
GKI_ROOT=$(pwd)
|
|
||||||
echo "[+] GKI_ROOT: $GKI_ROOT"
|
|
||||||
echo "[+] Copy KernelSU driver to $GKI_ROOT/common/drivers"
|
|
||||||
ln -sf $GITHUB_WORKSPACE/KernelSU/kernel $GKI_ROOT/common/drivers/kernelsu
|
|
||||||
echo "[+] Add KernelSU driver to Makefile"
|
|
||||||
DRIVER_MAKEFILE=$GKI_ROOT/common/drivers/Makefile
|
|
||||||
DRIVER_KCONFIG=$GKI_ROOT/common/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 Compilation Patches"
|
|
||||||
if [ ! -e build/build.sh ]; then
|
|
||||||
GLIBC_VERSION=$(ldd --version 2>/dev/null | head -n 1 | awk '{print $NF}')
|
|
||||||
echo "GLIBC_VERSION: $GLIBC_VERSION"
|
|
||||||
if [ "$(printf '%s\n' "2.38" "$GLIBC_VERSION" | sort -V | head -n1)" = "2.38" ]; then
|
|
||||||
echo "Patching resolve_btfids/Makefile"
|
|
||||||
cd $GKI_ROOT/common/ && sed -i '/\$(Q)\$(MAKE) -C \$(SUBCMD_SRC) OUTPUT=\$(abspath \$(dir \$@))\/ \$(abspath \$@)/s//$(Q)$(MAKE) -C $(SUBCMD_SRC) EXTRA_CFLAGS="$(CFLAGS)" OUTPUT=$(abspath $(dir $@))\/ $(abspath $@)/' tools/bpf/resolve_btfids/Makefile || echo "No patch needed."
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ "$IS_DEBUG_KERNEL" = "true" ]; then
|
|
||||||
echo "[+] Enable debug features for kernel"
|
|
||||||
printf "\nccflags-y += -DCONFIG_KSU_DEBUG\n" >> $GITHUB_WORKSPACE/KernelSU/kernel/Makefile
|
|
||||||
fi
|
|
||||||
repo status
|
|
||||||
echo "[+] KernelSU setup done."
|
|
||||||
|
|
||||||
- name: Symbol magic
|
|
||||||
run: |
|
|
||||||
echo "[+] Export all symbol from abi_gki_aarch64.xml"
|
|
||||||
COMMON_ROOT=$GITHUB_WORKSPACE/android-kernel/common
|
|
||||||
KSU_ROOT=$GITHUB_WORKSPACE/KernelSU
|
|
||||||
ABI_XML=$COMMON_ROOT/android/abi_gki_aarch64.xml
|
|
||||||
SYMBOL_LIST=$COMMON_ROOT/android/abi_gki_aarch64
|
|
||||||
# python3 $KSU_ROOT/scripts/abi_gki_all.py $ABI_XML > $SYMBOL_LIST
|
|
||||||
echo "[+] Add KernelSU symbols"
|
|
||||||
cat $KSU_ROOT/kernel/export_symbol.txt | awk '{sub("[ \t]+","");print " "$0}' >> $SYMBOL_LIST
|
|
||||||
|
|
||||||
- name: Setup ccache
|
|
||||||
if: inputs.use_cache == true
|
|
||||||
uses: hendrikmuhs/ccache-action@v1
|
|
||||||
with:
|
|
||||||
key: gki-kernel-aarch64-${{ inputs.version_name }}
|
|
||||||
max-size: 2G
|
|
||||||
save: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }}
|
|
||||||
|
|
||||||
- name: Setup for LKM
|
|
||||||
if: ${{ inputs.build_lkm == true }}
|
|
||||||
working-directory: android-kernel
|
|
||||||
run: |
|
|
||||||
pip install ast-grep-cli
|
|
||||||
sudo apt-get install llvm-15 -y
|
|
||||||
ast-grep -U -p '$$$ check_exports($$$) {$$$}' -r '' common/scripts/mod/modpost.c
|
|
||||||
ast-grep -U -p 'check_exports($$$);' -r '' common/scripts/mod/modpost.c
|
|
||||||
sed -i '/config KSU/,/help/{s/default y/default m/}' common/drivers/kernelsu/Kconfig
|
|
||||||
echo "drivers/kernelsu/kernelsu.ko" >> common/android/gki_aarch64_modules
|
|
||||||
|
|
||||||
# bazel build, android14-5.15, android14-6.1 use bazel
|
|
||||||
if [ ! -e build/build.sh ]; then
|
|
||||||
sed -i 's/needs unknown symbol/Dont abort when unknown symbol/g' build/kernel/*.sh || echo "No unknown symbol scripts found"
|
|
||||||
if [ -e common/modules.bzl ]; then
|
|
||||||
sed -i 's/_COMMON_GKI_MODULES_LIST = \[/_COMMON_GKI_MODULES_LIST = \[ "drivers\/kernelsu\/kernelsu.ko",/g' common/modules.bzl
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
TARGET_FILE="build/kernel/build.sh"
|
|
||||||
if [ ! -e "$TARGET_FILE" ]; then
|
|
||||||
TARGET_FILE="build/build.sh"
|
|
||||||
fi
|
|
||||||
sed -i 's/needs unknown symbol/Dont abort when unknown symbol/g' $TARGET_FILE || echo "No unknown symbol in $TARGET_FILE"
|
|
||||||
sed -i 's/if ! diff -u "\${KERNEL_DIR}\/\${MODULES_ORDER}" "\${OUT_DIR}\/modules\.order"; then/if false; then/g' $TARGET_FILE
|
|
||||||
sed -i 's@${ROOT_DIR}/build/abi/compare_to_symbol_list@echo@g' $TARGET_FILE
|
|
||||||
sed -i 's/needs unknown symbol/Dont abort when unknown symbol/g' build/kernel/*.sh || echo "No unknown symbol scripts found"
|
|
||||||
fi
|
|
||||||
|
|
||||||
- name: Make working directory clean to avoid dirty
|
|
||||||
working-directory: android-kernel
|
|
||||||
run: |
|
|
||||||
rm common/android/abi_gki_protected_exports_* || echo "No protected exports!"
|
|
||||||
git config --global user.email "bot@kernelsu.org"
|
|
||||||
git config --global user.name "KernelSUBot"
|
|
||||||
cd common/ && git add -A && git commit -a -m "Add KernelSU"
|
|
||||||
repo status
|
|
||||||
|
|
||||||
- name: Build Kernel/LKM
|
|
||||||
working-directory: android-kernel
|
|
||||||
run: |
|
|
||||||
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
|
|
||||||
if [ -e build/build.sh ]; then
|
|
||||||
LTO=thin BUILD_CONFIG=common/build.config.gki.aarch64 build/build.sh CC="/usr/bin/ccache clang"
|
|
||||||
else
|
|
||||||
tools/bazel run --disk_cache=/home/runner/.cache/bazel --config=fast --config=stamp --lto=thin //common:kernel_aarch64_dist -- --dist_dir=dist
|
|
||||||
fi
|
|
||||||
|
|
||||||
- name: Prepare artifacts
|
|
||||||
id: prepareArtifacts
|
|
||||||
run: |
|
|
||||||
OUTDIR=android-kernel/out/${{ inputs.version }}/dist
|
|
||||||
if [ ! -e $OUTDIR ]; then
|
|
||||||
OUTDIR=android-kernel/dist
|
|
||||||
fi
|
|
||||||
mkdir output
|
|
||||||
if [ "${{ inputs.build_lkm}}" = "true" ]; then
|
|
||||||
llvm-strip-15 -d $OUTDIR/kernelsu.ko
|
|
||||||
mv $OUTDIR/kernelsu.ko ./output/${{ inputs.version }}_kernelsu.ko
|
|
||||||
else
|
|
||||||
cp $OUTDIR/Image ./output/
|
|
||||||
cp $OUTDIR/Image.lz4 ./output/
|
|
||||||
git clone https://github.com/Kernel-SU/AnyKernel3
|
|
||||||
rm -rf ./AnyKernel3/.git
|
|
||||||
cp $OUTDIR/Image ./AnyKernel3/
|
|
||||||
fi
|
|
||||||
|
|
||||||
- name: Upload Image and Image.gz
|
|
||||||
uses: actions/upload-artifact@v4
|
|
||||||
if: ${{ inputs.build_lkm == false }}
|
|
||||||
with:
|
|
||||||
name: Image-${{ inputs.version_name }}_${{ inputs.os_patch_level }}
|
|
||||||
path: ./output/*
|
|
||||||
|
|
||||||
- name: Upload AnyKernel3
|
|
||||||
if: ${{ inputs.build_lkm == false }}
|
|
||||||
uses: actions/upload-artifact@v4
|
|
||||||
with:
|
|
||||||
name: AnyKernel3-${{ inputs.version_name }}_${{ inputs.os_patch_level }}
|
|
||||||
path: ./AnyKernel3/*
|
|
||||||
|
|
||||||
- name: Upload LKM
|
|
||||||
uses: actions/upload-artifact@v4
|
|
||||||
if: ${{ inputs.build_lkm == true }}
|
|
||||||
with:
|
|
||||||
name: ${{ inputs.version }}-lkm
|
|
||||||
path: ./output/*_kernelsu.ko
|
|
||||||
2
.github/workflows/ksud.yml
vendored
2
.github/workflows/ksud.yml
vendored
@@ -71,4 +71,4 @@ jobs:
|
|||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: ksud-${{ inputs.target }}
|
name: ksud-${{ inputs.target }}
|
||||||
path: userspace/ksud/target/**/release/zakomk*
|
path: userspace/ksud/target/**/release/zakozako*
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
name: Build zakomksd
|
name: Build susfs
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: [ "mian" ]
|
branches: [ "mian" ]
|
||||||
paths:
|
paths:
|
||||||
- '.github/workflows/zakomksd.yml'
|
- '.github/workflows/susfs.yml'
|
||||||
- 'userspace/zakomksd/**'
|
- 'userspace/susfs/**'
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
workflow_call:
|
workflow_call:
|
||||||
inputs:
|
inputs:
|
||||||
@@ -19,7 +19,7 @@ on:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build-susfs:
|
build-susfs:
|
||||||
name: Build userspace zakomksd
|
name: Build userspace susfs
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
@@ -28,13 +28,13 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|
||||||
- name: Build zakomksd
|
- name: Build susfs
|
||||||
working-directory: ./userspace/zakomksd
|
working-directory: ./userspace/susfs
|
||||||
run: |
|
run: |
|
||||||
$ANDROID_NDK_HOME/ndk-build
|
$ANDROID_NDK_HOME/ndk-build
|
||||||
|
|
||||||
- name: Upload a Build Artifact
|
- name: Upload a Build Artifact
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: zakomksd-aarch64-linux-android
|
name: susfs-aarch64-linux-android
|
||||||
path: ./userspace/zakomksd/libs
|
path: ./userspace/susfs/libs
|
||||||
@@ -1,49 +1,63 @@
|
|||||||
# SukiSU
|
# SukiSU
|
||||||
|
|
||||||
**Enlish** | [简体中文](README.md)
|
**English** | [简体中文](README.md) | [日本語](README-ja.md)
|
||||||
|
|
||||||
|
|
||||||
Android device root solution based on [KernelSU](https://github.com/KernelSU/KernelSU)
|
Android device root solution based on [KernelSU](https://github.com/tiann/KernelSU)
|
||||||
|
|
||||||
**Experimental! Use at your own risk! **This solution is based on [KernelSU]() and is experimental!
|
**Experimental! Use at your own risk!** This solution is based on [KernelSU](https://github.com/tiann/KernelSU) and is experimental!
|
||||||
|
|
||||||
>
|
>
|
||||||
> This is an unofficial fork, all rights reserved [@tiann](https://github.com/tiann)
|
> This is an unofficial fork. All rights are reserved to [@tiann](https://github.com/tiann)
|
||||||
|
> However, we will be a separately maintained branch of KSU in the future
|
||||||
>
|
>
|
||||||
|
|
||||||
- Fully adapted for non-GKI devices (susfs-dev and unsusfs-patched dev branches only)
|
- Fully adapted for non-GKI devices (susfs-dev and unsusfs-patched dev branches only)
|
||||||
|
|
||||||
## How to add
|
## How to add
|
||||||
|
|
||||||
Using the susfs-dev branch (integrated susfs with support for non-GKI devices)
|
Use the susfs-stable or susfs-dev branch (integrated susfs with support for non-GKI devices)
|
||||||
|
|
||||||
```
|
```
|
||||||
curl -LSs "https://raw.githubusercontent.com/ShirkNeko/SukiSU-Ultra/main/kernel/setup.sh" | bash -s susfs-dev
|
curl -LSs "https://raw.githubusercontent.com/ShirkNeko/SukiSU-Ultra/main/kernel/setup.sh" | bash -s susfs-dev
|
||||||
```
|
```
|
||||||
|
|
||||||
Use main branching (no longer with support for non-GKI devices)
|
Use the main branch
|
||||||
```
|
```
|
||||||
curl -LSs "https://raw.githubusercontent.com/ShirkNeko/SukiSU-Ultra/main/kernel/setup.sh" | bash -s main
|
curl -LSs "https://raw.githubusercontent.com/ShirkNeko/KernelSU/main/kernel/setup.sh" | bash -s main
|
||||||
```
|
```
|
||||||
|
|
||||||
## How to use integrated susfs
|
## How to use integrated susfs
|
||||||
|
|
||||||
Use the susfs-dev branch directly without any patching
|
1. Use the susfs-dev branch directly without any patching
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## KPM support
|
||||||
|
|
||||||
|
- We have removed duplicate KSU functions based on KernelPatch and retained KPM support.
|
||||||
|
- We will introduce more APatch-compatible functions to ensure the integrity of KPM functionality.
|
||||||
|
|
||||||
|
|
||||||
|
Open source address: https://github.com/ShirkNeko/SukiSU_KernelPatch_patch
|
||||||
|
|
||||||
|
|
||||||
|
KPM template address: https://github.com/udochina/KPM-Build-Anywhere
|
||||||
|
|
||||||
## More links
|
## More links
|
||||||
Projects compiled based on Sukisu and susfs
|
Projects compiled based on Sukisu and susfs
|
||||||
- [GKI](https://github.com/ShirkNeko/GKI_KernelSU_SUSFS)
|
- [GKI](https://github.com/ShirkNeko/GKI_KernelSU_SUSFS)
|
||||||
- [OnePlus](https://github.com/ShirkNeko/Action_OnePlus_MKSU_SUSFS)
|
- [OnePlus](https://github.com/ShirkNeko/Action_OnePlus_MKSU_SUSFS)
|
||||||
|
|
||||||
## Hook method
|
## Hook method
|
||||||
- This method references the hook manual to (https://github.com/rsuntk/KernelSU)
|
- This method references the hook method from (https://github.com/rsuntk/KernelSU)
|
||||||
|
|
||||||
1. **KPROBES hook:**
|
1. **KPROBES hook:**
|
||||||
- This fork only supports GKI (5.10 - 6.x) kernels, all non-GKI kernels must use manual hooks.
|
- This method only supports GKI (5.10 - 6.x) kernels, and all non-GKI kernels must use manual hooks.
|
||||||
- For Loadable Kernel Modules (LKM)
|
- For Loadable Kernel Modules (LKM)
|
||||||
- Default hooking method for GKI kernels
|
- Default hooking method for GKI kernels
|
||||||
- Requires `CONFIG_KPROBES=y`. 2.
|
- Requires `CONFIG_KPROBES=y`.
|
||||||
2. **Hooks manual:**
|
2. **Manual hooks:**
|
||||||
- For GKI (5.10 - 6.x) kernels, add `CONFIG_KSU_MANUAL_HOOK=y` to the kernel defconfig and make sure to protect KernelSU hooks by using `#ifdef CONFIG_KSU_MANUAL_HOOK` instead of `#ifdef CONFIG_KSU`.
|
- For GKI (5.10 - 6.x) kernels, add `CONFIG_KSU_MANUAL_HOOK=y` to the kernel defconfig and make sure to protect KernelSU hooks by using `#ifdef CONFIG_KSU_MANUAL_HOOK` instead of `#ifdef CONFIG_KSU`.
|
||||||
- Standard KernelSU hooks: https://kernelsu.org/guide/how-to-integrate-for-non-gki.html#manually-modify-the-kernel-source
|
- Standard KernelSU hooks: https://kernelsu.org/guide/how-to-integrate-for-non-gki.html#manually-modify-the-kernel-source
|
||||||
- backslashxx syscall hooks: https://github.com/backslashxx/KernelSU/issues/5
|
- backslashxx syscall hooks: https://github.com/backslashxx/KernelSU/issues/5
|
||||||
@@ -51,51 +65,55 @@ Projects compiled based on Sukisu and susfs
|
|||||||
|
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
[GKI]
|
### GKI
|
||||||
1. such as millet redmi samsung and other devices (does not include the magic kernel manufacturers such as: meizu, a plus real me oppo)
|
1. such as Xiaomi, Redmi, Samsung, and other devices (does not include manufacturers that modified the kernel like Meizu, OnePlus, RealMe, and OPPO)
|
||||||
2. find more links in the GKI build project to find the device kernel version directly download with TWRP or kernel flashing tool to brush into the zip with AnyKernel3 suffix can be
|
2. Use the prebuilt GKI kernel, the ones with their name ending with AnyKernel3, mentioned in the 'More Links' section, and then flash it with recoveries like TWRP
|
||||||
3. General without the suffix of the .zip compressed package is universal, gz suffix for the special TianGui models, lz4 suffix for Google models, general brush without the suffix can be!
|
3. Generally, packages with a plain .zip suffix are universal. However, if your device has a MediaTek processor, you should use the ones with .gz suffix, and packages with .lz4 suffix are dedicated to Google devices.
|
||||||
|
|
||||||
[OnePlus]
|
### OnePlus
|
||||||
1. Find the Yiga project in the More link and fill in your own, then build it with cloud compilation, and finally brush in the zip with AnyKernel3 suffix.
|
1. Use the link mentioned in the 'More Links' section to create a customized build with your device information, and then flash the zip file with the AnyKernel3 suffix.
|
||||||
Note: You only need to fill in the first two kernel versions, such as 5.10, 5.15, 6.1, 6.6.
|
> [!Note]
|
||||||
- Please search for the processor codename by yourself, usually it is all English without numbers.
|
> - You only need to fill in the first two parts of kernel versions, such as 5.10, 5.15, 6.1, or 6.6.
|
||||||
- Branching and configuration files, please fill in the kernel open source address.
|
> - Please search for the processor codename by yourself, usually it is all English without numbers.
|
||||||
|
> - You can find the branch and configuration files from the OnePlus open-source kernel repository.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
1. Kernel-based `su` and root access management.
|
1. Kernel-based `su` and root access management.
|
||||||
2. Not based on [OverlayFS](https://en.wikipedia.org/wiki/OverlayFS) module system. 3.
|
2. Not based on [OverlayFS](https://en.wikipedia.org/wiki/OverlayFS) module system, but based on [Magic Mount](https://github.com/5ec1cff/KernelSU) from 5ec1cff
|
||||||
3. [Application Profiles](https://kernelsu.org/guide/app-profile.html): Lock root privileges in a cage. 4.
|
3. [App Profile](https://kernelsu.org/guide/app-profile.html): Lock root privileges in a cage.
|
||||||
4. Bringing back non-GKI/GKI 1.0 support
|
4. Bringing back non-GKI/GKI 1.0 support
|
||||||
5. More customization
|
5. More customization
|
||||||
|
6. Support for KPM kernel modules
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
- The file in the “kernel” directory is [GPL-2.0-only](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html).
|
- The file in the “kernel” directory is under [GPL-2.0-only](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html) license.
|
||||||
- All other parts except the “kernel” directory are [GPL-3.0 or later](https://www.gnu.org/licenses/gpl-3.0.html).
|
- All other parts except the “kernel” directory are under [GPL-3.0 or later](https://www.gnu.org/licenses/gpl-3.0.html) license.
|
||||||
|
|
||||||
## Sponsorship list
|
## Sponsorship list
|
||||||
- [Ktouls](https://github.com/Ktouls) Thanks so much for bringing me support
|
- [Ktouls](https://github.com/Ktouls) Thanks so much for bringing me support
|
||||||
- [zaoqi123](https://github.com/zaoqi123) It's not a bad idea to buy me a milk tea
|
- [zaoqi123](https://github.com/zaoqi123) It's not a bad idea to buy me a milk tea
|
||||||
- [wswzgdg](https://github.com/wswzgdg) Many thanks for supporting this project
|
- [wswzgdg](https://github.com/wswzgdg) Many thanks for supporting this project
|
||||||
|
- [yspbwx2010](https://github.com/yspbwx2010) Many thanks
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
How the above list does not have your name, I will keep you updated, thanks again for your support!
|
If the above list does not have your name, I will update it as soon as possible, and thanks again for your support!
|
||||||
|
|
||||||
## Contributions
|
## Contributions
|
||||||
|
|
||||||
- [KernelSU](https://github.com/tiann/KernelSU): original project
|
- [KernelSU](https://github.com/tiann/KernelSU): original project
|
||||||
- [MKSU](https://github.com/5ec1cff/KernelSU): Used project
|
- [MKSU](https://github.com/5ec1cff/KernelSU): Used project
|
||||||
- [RKSU](https://github.com/rsuntk/KernelsU):Re-support of non-GKI devices using the kernel of this project
|
- [RKSU](https://github.com/rsuntk/KernelsU): Reintroduced the support of non-GKI devices using the kernel of this project
|
||||||
- [susfs](https://gitlab.com/simonpunk/susfs4ksu):Used susfs file system
|
- [susfs](https://gitlab.com/simonpunk/susfs4ksu):Used susfs file system
|
||||||
- [KernelSU](https://git.zx2c4.com/kernel-assisted-superuser/about/): KernelSU conceptualization
|
- [KernelSU](https://git.zx2c4.com/kernel-assisted-superuser/about/): KernelSU conceptualization
|
||||||
- [Magisk](https://github.com/topjohnwu/Magisk): Powerful root utility
|
- [Magisk](https://github.com/topjohnwu/Magisk): Powerful root utility
|
||||||
- [genuine](https://github.com/brevent/genuine/): APK v2 Signature Verification
|
- [genuine](https://github.com/brevent/genuine/): APK v2 Signature Verification
|
||||||
- [Diamorphine](https://github.com/m0nad/Diamorphine): Some rootkit skills.
|
- [Diamorphine](https://github.com/m0nad/Diamorphine): Some rootkit utilities.
|
||||||
|
- [KernelPatch](https://github.com/bmax121/KernelPatch): KernelPatch is a key part of the APatch implementation of the kernel module
|
||||||
|
|||||||
112
docs/README-ja.md
Normal file
112
docs/README-ja.md
Normal file
@@ -0,0 +1,112 @@
|
|||||||
|
# SukiSU
|
||||||
|
|
||||||
|
**日本語** | [简体中文](README.md) | [English](README-en.md)
|
||||||
|
|
||||||
|
[KernelSU](https://github.com/tiann/KernelSU) をベースとした Android デバイスの root ソリューション
|
||||||
|
|
||||||
|
**試験中なビルドです!自己責任で使用してください!**<br>
|
||||||
|
このソリューションは [KernelSU](https://github.com/tiann/KernelSU) に基づいていますが、試験中なビルドです。
|
||||||
|
|
||||||
|
>
|
||||||
|
> これは非公式なフォークです。すべての権利は [@tiann](https://github.com/tiann) に帰属します。
|
||||||
|
>
|
||||||
|
>ただし、将来的には KSU とは別に管理されるブランチとなる予定です。
|
||||||
|
|
||||||
|
- GKI 非対応なデバイスに完全に適応 (susfs-dev と unsusfs-patched dev ブランチのみ)
|
||||||
|
|
||||||
|
## 追加方法
|
||||||
|
susfs-stable または susfs-dev ブランチ (GKI 非対応デバイスに対応する統合された susfs) 使用してください。
|
||||||
|
|
||||||
|
```
|
||||||
|
curl -LSs "https://raw.githubusercontent.com/ShirkNeko/SukiSU-Ultra/main/kernel/setup.sh" | bash -s susfs-dev
|
||||||
|
```
|
||||||
|
|
||||||
|
メインブランチを使用する場合
|
||||||
|
```
|
||||||
|
curl -LSs "https://raw.githubusercontent.com/ShirkNeko/KernelSU/main/kernel/setup.sh" | bash -s main
|
||||||
|
```
|
||||||
|
## 統合された susfs の使い方
|
||||||
|
1. パッチを当てずに susfs-dev ブランチを直接使用してください。
|
||||||
|
|
||||||
|
## KPM に対応
|
||||||
|
- KernelPatch に基づいて重複した KSU の機能を削除、KPM の対応を維持させています。
|
||||||
|
- KPM 機能の整合性を確保するために、APatch の互換機能を更に向上させる予定です。
|
||||||
|
|
||||||
|
|
||||||
|
オープンソースアドレス: https://github.com/ShirkNeko/SukiSU_KernelPatch_patch
|
||||||
|
|
||||||
|
|
||||||
|
KPM テンプレートのアドレス: https://github.com/udochina/KPM-Build-Anywhere
|
||||||
|
|
||||||
|
## その他のリンク
|
||||||
|
SukiSU と susfs をベースにコンパイルされたプロジェクトです。
|
||||||
|
- [GKI](https://github.com/ShirkNeko/GKI_KernelSU_SUSFS)
|
||||||
|
- [OnePlus](https://github.com/ShirkNeko/Action_OnePlus_MKSU_SUSFS)
|
||||||
|
|
||||||
|
## フックの方式
|
||||||
|
- この方式は (https://github.com/rsuntk/KernelSU) のフック方式を参照してください。
|
||||||
|
|
||||||
|
1. **KPROBES フック:**
|
||||||
|
- この方式は GKI (5.10 - 6.x) のカーネルのみに対応しています。GKI 以外のカーネルは手動でフックを使用する必要があります。
|
||||||
|
- 読み込み可能なカーネルモジュールの場合 (LKM)
|
||||||
|
- GKI カーネルのデフォルトとなるフック方式
|
||||||
|
- `CONFIG_KPROBES=y` が必要です。
|
||||||
|
2. **手動でフック:**
|
||||||
|
- GKI (5.10 - 6.x) のカーネルの場合、カーネルの defconfig に `CONFIG_KSU_MANUAL_HOOK=y` を追加して `#ifdef CONFIG_KSU` ではなく `#ifdef CONFIG_KSU_MANUAL_HOOK` を使用して KernelSU フックを保護するようにしてください。
|
||||||
|
- 標準の KernelSU フック: https://kernelsu.org/guide/how-to-integrate-for-non-gki.html#manually-modify-the-kernel-source
|
||||||
|
- backslashxx syscall フック: https://github.com/backslashxx/KernelSU/issues/5
|
||||||
|
- KPROBES を手動で統合する一部の非 GKI デバイスでは手動の VFS フック `new_hook.patch` パッチは不要です。
|
||||||
|
|
||||||
|
|
||||||
|
## 使い方
|
||||||
|
### GKI
|
||||||
|
1. Xiaomi、Redmi、Samsung などのデバイス (Meizu、OnePlus、Realme、OPPO などのカーネルを変更したメーカー以外)
|
||||||
|
2. `その他のリンク`の項目で言及されているカーネル名が、AnyKernel3 で終わるビルド済みの GKI カーネルを TWRP などのリカバリーでフラッシュします。
|
||||||
|
3. 一般的な .zip の接頭辞を持つパッケージは汎用的になります。ただし、デバイスに MediaTek 製の SoC が搭載されている場合は、.gz の接頭辞を持つパッケージを使用する必要があります。その他に .lz4 の接頭辞を持つパッケージは Google 製デバイス専用です。
|
||||||
|
|
||||||
|
### OnePlus
|
||||||
|
1. `その他のリンク`の項目に記載されているリンクを開き、デバイス情報を使用してカスタマイズされたカーネルをビルドし、AnyKernel3 の接頭辞を持つ .zip ファイルをフラッシュします。
|
||||||
|
> [!Note]
|
||||||
|
> - 5.10、5.15、6.1、6.6 などのカーネルバージョンの最初の 2 文字のみを入力する必要があります。
|
||||||
|
> - SoC のコードネームは自分で検索してください。通常は、数字がなく英語表記のみです。
|
||||||
|
> - ブランチと構成ファイルは、OnePlus オープンソースカーネルリポジトリから見つけることができます。
|
||||||
|
|
||||||
|
|
||||||
|
## 機能
|
||||||
|
|
||||||
|
1. カーネルベースな `su` および root アクセスの管理。
|
||||||
|
2. [OverlayFS](https://en.wikipedia.org/wiki/OverlayFS) モジュールシステムではなく、 5ec1cff 氏の [Magic Mount](https://github.com/5ec1cff/KernelSU) に基づいています。
|
||||||
|
3. [アプリプロファイル](https://kernelsu.org/guide/app-profile.html): root 権限をケージ内にロックします。
|
||||||
|
4. 非 GKI / GKI 1.0 の対応を復活
|
||||||
|
5. その他のカスタマイズ
|
||||||
|
6. KPM カーネルモジュールに対応
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## ライセンス
|
||||||
|
|
||||||
|
- “kernel” ディレクトリ内のファイルは [GPL-2.0](https://www.gnu.org/licenses/old-licenses/gpl-2.0.ja.html) のみライセンス下にあります。
|
||||||
|
- “kernel” ディレクトリを除くその他すべての部分は [GPL-3.0 またはそれ以降](https://www.gnu.org/licenses/gpl-3.0.html) のライセンス下にあります。
|
||||||
|
|
||||||
|
## スポンサーシップの一覧
|
||||||
|
- [Ktouls](https://github.com/Ktouls) 応援をしてくれたことに感謝。
|
||||||
|
- [zaoqi123](https://github.com/zaoqi123) ミルクティーを買ってあげるのも良い考えですね。
|
||||||
|
- [wswzgdg](https://github.com/wswzgdg) このプロジェクトを支援していただき、ありがとうございます。
|
||||||
|
- [yspbwx2010](https://github.com/yspbwx2010) どうもありがとう。
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
上記の一覧にあなたの名前がない場合は、できるだけ早急に更新しますので再度ご支援をお願いします。
|
||||||
|
|
||||||
|
## 貢献者
|
||||||
|
|
||||||
|
- [KernelSU](https://github.com/tiann/KernelSU): オリジナルのプロジェクトです。
|
||||||
|
- [MKSU](https://github.com/5ec1cff/KernelSU): 使用しているプロジェクトです。
|
||||||
|
- [RKSU](https://github.com/rsuntk/KernelsU): このプロジェクトのカーネルを使用して非 GKI デバイスのサポートを追加しています。
|
||||||
|
- [susfs](https://gitlab.com/simonpunk/susfs4ksu):使用している susfs ファイルシステムです。
|
||||||
|
- [KernelSU](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 ユーティリティを使用しています。
|
||||||
|
- [KernelPatch](https://github.com/bmax121/KernelPatch): KernelPatch はカーネルモジュールの APatch 実装での重要な部分となります。
|
||||||
@@ -1,14 +1,15 @@
|
|||||||
# SukiSU
|
# SukiSU Ultra
|
||||||
|
|
||||||
**简体中文** | [English](README-en.md)
|
**简体中文** | [English](README-en.md) | [日本語](README-ja.md)
|
||||||
|
|
||||||
基于 [KernelSU](https://github.com/tiann/KernelSU) 的安卓设备 root 解决方案
|
基于 [KernelSU](https://github.com/tiann/KernelSU) 的安卓设备 root 解决方案
|
||||||
|
|
||||||
**实验性!使用风险自负!**
|
**实验性! 使用风险自负!**
|
||||||
|
|
||||||
|
|
||||||
>
|
>
|
||||||
> 这是非官方分支,保留所有权利 [@tiann](https://github.com/tiann)
|
> 这是非官方分支,保留所有权利 [@tiann](https://github.com/tiann)
|
||||||
|
> 但是,我们将会在未来成为一个单独维护的KSU分支
|
||||||
>
|
>
|
||||||
|
|
||||||
|
|
||||||
@@ -16,33 +17,46 @@
|
|||||||
在内核源码的根目录下执行以下命令:
|
在内核源码的根目录下执行以下命令:
|
||||||
|
|
||||||
使用 susfs-dev 分支(已集成susfs,带非GKI设备的支持)
|
使用 susfs-dev 分支(已集成susfs,带非GKI设备的支持)
|
||||||
|
|
||||||
```
|
```
|
||||||
curl -LSs "https://raw.githubusercontent.com/ShirkNeko/SukiSU-Ultra/main/kernel/setup.sh" | bash -s susfs-dev
|
curl -LSs "https://raw.githubusercontent.com/ShirkNeko/SukiSU-Ultra/main/kernel/setup.sh" | bash -s susfs-dev
|
||||||
```
|
```
|
||||||
|
|
||||||
使用 main 分支(不再带非GKI设备的支持)
|
|
||||||
|
使用 main 分支
|
||||||
```
|
```
|
||||||
curl -LSs "https://raw.githubusercontent.com/ShirkNeko/SukiSU-Ultra/main/kernel/setup.sh" | bash -s main
|
curl -LSs "https://raw.githubusercontent.com/ShirkNeko/SukiSU-Ultra/main/kernel/setup.sh" | bash -s main
|
||||||
```
|
```
|
||||||
|
|
||||||
## 如何集成 susfs
|
## 如何集成 susfs
|
||||||
|
|
||||||
1. 直接使用 susfs-dev 分支,不需要再集成 susfs
|
1. 直接使用 susfs-stable 或者 susfs-dev 分支,不需要再集成 susfs
|
||||||
|
|
||||||
|
|
||||||
## 钩子方法
|
## 钩子方法
|
||||||
- 此部分引用自 [rsuntk 的钩子方法](https://github.com/rsuntk/KernelSU)
|
- 此部分引用自 [rsuntk 的钩子方法](https://github.com/rsuntk/KernelSU)
|
||||||
|
|
||||||
1. **KPROBES 钩子:**
|
1. **KPROBES 钩子:**
|
||||||
- 此方法仅支持 GKI 2.0(5.10 - 6.x)内核,所有非 GKI 2.0 内核都必须使用手动钩子
|
- 此方法仅支持 GKI 2.0 (5.10 - 6.x) 内核, 所有非 GKI 2.0 内核都必须使用手动钩子
|
||||||
- 用于可加载内核模块 (LKM)
|
- 用于可加载内核模块 (LKM)
|
||||||
- GKI 2.0 内核的默认钩子方法
|
- GKI 2.0 内核的默认钩子方法
|
||||||
- 需要 `CONFIG_KPROBES=y`
|
- 需要 `CONFIG_KPROBES=y`
|
||||||
2. **手动钩子:**
|
2. **手动钩子:**
|
||||||
- 对于 GKI 2.0(5.10 - 6.x)内核,需要在对应设备的 defconfig 文件中添加 `CONFIG_KSU_MANUAL_HOOK=y` 并确保使用 `#ifdef CONFIG_KSU_MANUAL_HOOK` 而不是 `#ifdef CONFIG_KSU` 来保护 KernelSU 钩子
|
- 对于 GKI 2.0 (5.10 - 6.x) 内核,需要在对应设备的 defconfig 文件中添加 `CONFIG_KSU_MANUAL_HOOK=y` 并确保使用 `#ifdef CONFIG_KSU_MANUAL_HOOK` 而不是 `#ifdef CONFIG_KSU` 来保护 KernelSU 钩子
|
||||||
- 标准的 KernelSU 钩子:https://kernelsu.org/guide/how-to-integrate-for-non-gki.html#manually-modify-the-kernel-source
|
- 标准的 KernelSU 钩子:https://kernelsu.org/guide/how-to-integrate-for-non-gki.html#manually-modify-the-kernel-source
|
||||||
- backslashxx 的 syscall 手动钩子:https://github.com/backslashxx/KernelSU/issues/5
|
- backslashxx 的 syscall 手动钩子:https://github.com/backslashxx/KernelSU/issues/5
|
||||||
- 部分手动集成KPROBES的非 GKI 2.0 设备不需要手动 VFS 钩子 `new_hook.patch` 补丁
|
- 部分手动集成 KPROBES 的非 GKI 2.0 设备不需要手动 VFS 钩子 `new_hook.patch` 补丁
|
||||||
|
|
||||||
|
|
||||||
|
## KPM支持
|
||||||
|
|
||||||
|
- 我们基于KernelPatch去掉了和KSU重复的功能,保留了KPM支持
|
||||||
|
- 我们将会引入更多的兼容APatch的函数来确保KPM功能的完整性
|
||||||
|
|
||||||
|
|
||||||
|
开源地址: https://github.com/ShirkNeko/SukiSU_KernelPatch_patch
|
||||||
|
|
||||||
|
|
||||||
|
KPM模板地址: https://github.com/udochina/KPM-Build-Anywhere
|
||||||
|
|
||||||
|
|
||||||
## 更多链接
|
## 更多链接
|
||||||
@@ -54,17 +68,17 @@ curl -LSs "https://raw.githubusercontent.com/ShirkNeko/SukiSU-Ultra/main/kernel/
|
|||||||
## 使用方法
|
## 使用方法
|
||||||
|
|
||||||
### GKI
|
### GKI
|
||||||
1. 适用于如小米红米三星等的 GKI 2.0 的设备(不包含魔改内核的厂商如魅族、一加、真我和 oppo)
|
1. 适用于如小米红米三星等的 GKI 2.0 的设备 (不包含魔改内核的厂商如魅族、一加、真我和 oppo)
|
||||||
2. 找到更多链接里的 GKI 构建的项目找到设备内核版本直接下载用TWRP或者内核刷写工具刷入带 AnyKernel3 后缀的压缩包即可
|
2. 找到更多链接里的 GKI 构建的项目找到设备内核版本直接下载用TWRP或者内核刷写工具刷入带 AnyKernel3 后缀的压缩包即可
|
||||||
3. 一般不带后缀的 .zip 压缩包是通用,gz 后缀的为天玑机型专用,lz4 后缀的为谷歌系机型专用,一般刷不带后缀的即可
|
3. 一般不带后缀的 .zip 压缩包是通用,gz 后缀的为天玑机型专用,lz4 后缀的为谷歌系机型专用,一般刷不带后缀的即可
|
||||||
|
|
||||||
### 一加
|
### 一加
|
||||||
1.找到更多链接里的一加项目进行自行填写,然后云编译构建,最后刷入带 AnyKernel3 后缀的压缩包即可
|
1.找到更多链接里的一加项目进行自行填写,然后云编译构建,最后刷入带 AnyKernel3 后缀的压缩包即可
|
||||||
|
|
||||||
注意事项:
|
> [!Note]
|
||||||
- 内核版本只需要填写前两位即可,如 5.10,5.15,6.1,6.6
|
> - 内核版本只需要填写前两位即可,如 5.10,5.15,6.1,6.6
|
||||||
- 处理器代号请自行搜索,一般为全英文不带数字的代号
|
> - 处理器代号请自行搜索,一般为全英文不带数字的代号
|
||||||
- 分支和配置文件请自行到一加内核开源地址进行填写
|
> - 分支和配置文件请自行到一加内核开源地址进行填写
|
||||||
|
|
||||||
|
|
||||||
## 特点
|
## 特点
|
||||||
@@ -72,8 +86,9 @@ curl -LSs "https://raw.githubusercontent.com/ShirkNeko/SukiSU-Ultra/main/kernel/
|
|||||||
1. 基于内核的 `su` 和 root 访问管理
|
1. 基于内核的 `su` 和 root 访问管理
|
||||||
2. 基于 5ec1cff 的 [Magic Mount](https://github.com/5ec1cff/KernelSU) 的模块系统
|
2. 基于 5ec1cff 的 [Magic Mount](https://github.com/5ec1cff/KernelSU) 的模块系统
|
||||||
3. [App Profile](https://kernelsu.org/guide/app-profile.html):将 root 权限锁在笼子里
|
3. [App Profile](https://kernelsu.org/guide/app-profile.html):将 root 权限锁在笼子里
|
||||||
4. 恢复对非 GKI 2.0 内核的支持(仅限susfs-dev和未进行susfs补丁的dev分支)
|
4. 恢复对非 GKI 2.0 内核的支持
|
||||||
5. 更多自定义功能
|
5. 更多自定义功能
|
||||||
|
6. 对KPM内核模块的支持
|
||||||
|
|
||||||
|
|
||||||
## 许可证
|
## 许可证
|
||||||
@@ -85,11 +100,12 @@ curl -LSs "https://raw.githubusercontent.com/ShirkNeko/SukiSU-Ultra/main/kernel/
|
|||||||
- [Ktouls](https://github.com/Ktouls) 非常感谢你给我带来的支持
|
- [Ktouls](https://github.com/Ktouls) 非常感谢你给我带来的支持
|
||||||
- [zaoqi123](https://github.com/zaoqi123) 请我喝奶茶也不错
|
- [zaoqi123](https://github.com/zaoqi123) 请我喝奶茶也不错
|
||||||
- [wswzgdg](https://github.com/wswzgdg) 非常感谢对此项目的支持
|
- [wswzgdg](https://github.com/wswzgdg) 非常感谢对此项目的支持
|
||||||
|
- [yspbwx2010](https://github.com/yspbwx2010) 非常感谢
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
如何以上名单没有你的名称,我会及时更新,再次感谢大家的支持
|
如果以上名单没有你的名称,我会及时更新,再次感谢大家的支持
|
||||||
|
|
||||||
## 贡献
|
## 贡献
|
||||||
|
|
||||||
@@ -101,3 +117,4 @@ curl -LSs "https://raw.githubusercontent.com/ShirkNeko/SukiSU-Ultra/main/kernel/
|
|||||||
- [Magisk](https://github.com/topjohnwu/Magisk):强大的 root 工具
|
- [Magisk](https://github.com/topjohnwu/Magisk):强大的 root 工具
|
||||||
- [genuine](https://github.com/brevent/genuine/):APK v2 签名验证
|
- [genuine](https://github.com/brevent/genuine/):APK v2 签名验证
|
||||||
- [Diamorphine](https://github.com/m0nad/Diamorphine):一些 rootkit 技能
|
- [Diamorphine](https://github.com/m0nad/Diamorphine):一些 rootkit 技能
|
||||||
|
- [KernelPatch](https://github.com/bmax121/KernelPatch): KernelPatch是APatch实现内核模块的关键部分
|
||||||
|
|||||||
@@ -24,11 +24,12 @@ config KSU_HOOK
|
|||||||
override the kernel version check and enable the hook functionality.
|
override the kernel version check and enable the hook functionality.
|
||||||
|
|
||||||
config KPM
|
config KPM
|
||||||
bool "Enable KernelSU KPM"
|
bool "Enable SukiSU KPM"
|
||||||
default n
|
default n
|
||||||
help
|
help
|
||||||
This option enables the KernelSU KPM feature. If enabled, it will
|
Enabling this option will activate the KPM feature of SukiSU.
|
||||||
override the kernel version check and enable the hook functionality.
|
This option is suitable for scenarios where you need to force KPM to be enabled.
|
||||||
|
but it may affect system stability.
|
||||||
|
|
||||||
|
|
||||||
endmenu
|
endmenu
|
||||||
|
|||||||
@@ -22,14 +22,14 @@ obj-$(CONFIG_KPM) += kpm/
|
|||||||
# .git is a text file while the module is imported by 'git submodule add'.
|
# .git is a text file while the module is imported by 'git submodule add'.
|
||||||
ifeq ($(shell test -e $(srctree)/$(src)/../.git; echo $$?),0)
|
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)
|
$(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_GIT_VERSION := $(shell cd $(srctree)/$(src); /usr/bin/env PATH="$$PATH":/usr/bin:/usr/local/bin git rev-list --count main)
|
||||||
# ksu_version: major * 10000 + git version + 606 for historical reasons
|
# ksu_version: major * 10000 + git version + 606 for historical reasons
|
||||||
$(eval KSU_VERSION=$(shell expr 10000 + $(KSU_GIT_VERSION) + 606))
|
$(eval KSU_VERSION=$(shell expr 10000 + $(KSU_GIT_VERSION) + 606))
|
||||||
$(info -- KernelSU version: $(KSU_VERSION))
|
$(info -- SukiSU version: $(KSU_VERSION))
|
||||||
ccflags-y += -DKSU_VERSION=$(KSU_VERSION)
|
ccflags-y += -DKSU_VERSION=$(KSU_VERSION)
|
||||||
else # If there is no .git file, the default version will be passed.
|
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 a git submodule!")
|
$(warning "KSU_GIT_VERSION not defined! It is better to make SukiSU a git submodule!")
|
||||||
ccflags-y += -DKSU_VERSION=16
|
ccflags-y += -DKSU_VERSION=12800
|
||||||
endif
|
endif
|
||||||
|
|
||||||
ifndef KSU_EXPECTED_SIZE
|
ifndef KSU_EXPECTED_SIZE
|
||||||
@@ -47,9 +47,14 @@ endif
|
|||||||
|
|
||||||
$(info -- KernelSU Manager signature size: $(KSU_EXPECTED_SIZE))
|
$(info -- KernelSU Manager signature size: $(KSU_EXPECTED_SIZE))
|
||||||
$(info -- KernelSU Manager signature hash: $(KSU_EXPECTED_HASH))
|
$(info -- KernelSU Manager signature hash: $(KSU_EXPECTED_HASH))
|
||||||
$(info -- Supported Unofficial Manager: ShirkNeko (GKI) (Non-GKI))
|
$(info -- Supported Unofficial Manager: 5ec1cff (GKI) ShirkNeko udochina (GKI and KPM))
|
||||||
KERNEL_VERSION := $(VERSION).$(PATCHLEVEL)
|
KERNEL_VERSION := $(VERSION).$(PATCHLEVEL)
|
||||||
$(info -- KERNEL_VERSION: $(KERNEL_VERSION))
|
$(info -- KERNEL_VERSION: $(KERNEL_VERSION))
|
||||||
|
ifeq ($(CONFIG_KPM),y)
|
||||||
|
$(info -- KPM is enabled)
|
||||||
|
else
|
||||||
|
$(info -- KPM is disabled)
|
||||||
|
endif
|
||||||
|
|
||||||
|
|
||||||
ccflags-y += -DEXPECTED_SIZE=$(KSU_EXPECTED_SIZE)
|
ccflags-y += -DEXPECTED_SIZE=$(KSU_EXPECTED_SIZE)
|
||||||
|
|||||||
@@ -419,10 +419,7 @@ int ksu_handle_prctl(int option, unsigned long arg2, unsigned long arg3,
|
|||||||
|
|
||||||
pr_info("KPM: calling before arg2=%d\n", (int) arg2);
|
pr_info("KPM: calling before arg2=%d\n", (int) arg2);
|
||||||
|
|
||||||
res = sukisu_handle_kpm(arg2, arg3, arg4);
|
res = sukisu_handle_kpm(arg2, arg3, arg4, arg5);
|
||||||
copy_to_user(result, &res, sizeof(res));
|
|
||||||
|
|
||||||
pr_info("KPM: calling before arg2=%d res=%d\n", (int) arg2, (int) res);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,2 +1,6 @@
|
|||||||
obj-y += kpm.o
|
obj-y += kpm.o
|
||||||
obj-y += compact.o
|
obj-y += compact.o
|
||||||
|
obj-y += super_access.o
|
||||||
|
|
||||||
|
ccflags-y += -Wno-implicit-function-declaration -Wno-strict-prototypes -Wno-int-conversion -Wno-gcc-compat
|
||||||
|
ccflags-y += -Wno-declaration-after-statement -Wno-unused-function
|
||||||
@@ -26,37 +26,56 @@
|
|||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include "kpm.h"
|
#include "kpm.h"
|
||||||
#include "compact.h"
|
#include "compact.h"
|
||||||
|
#include "../allowlist.h"
|
||||||
|
#include "../manager.h"
|
||||||
|
|
||||||
unsigned long sukisu_compact_find_symbol(const char* name);
|
unsigned long sukisu_compact_find_symbol(const char* name);
|
||||||
|
|
||||||
// ======================================================================
|
// ======================================================================
|
||||||
|
// 兼容函数 for KPM
|
||||||
|
|
||||||
const char* kpver = "0.10";
|
static
|
||||||
|
int sukisu_is_su_allow_uid(uid_t uid) {
|
||||||
|
return ksu_is_allow_uid(uid) ? 1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
int sukisu_get_ap_mod_exclude(uid_t uid) {
|
||||||
|
// Not supported
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
int sukisu_is_uid_should_umount(uid_t uid) {
|
||||||
|
return ksu_uid_should_umount(uid) ? 1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
int sukisu_is_current_uid_manager() {
|
||||||
|
return is_manager();
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
uid_t sukisu_get_manager_uid() {
|
||||||
|
return ksu_manager_uid;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ======================================================================
|
||||||
|
|
||||||
struct CompactAddressSymbol {
|
struct CompactAddressSymbol {
|
||||||
const char* symbol_name;
|
const char* symbol_name;
|
||||||
void* addr;
|
void* addr;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct CompactAliasSymbol {
|
static struct CompactAddressSymbol address_symbol [] = {
|
||||||
const char* symbol_name;
|
|
||||||
const char* compact_symbol_name;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct CompactAddressSymbol address_symbol [] = {
|
|
||||||
{ "kallsyms_lookup_name", &kallsyms_lookup_name },
|
{ "kallsyms_lookup_name", &kallsyms_lookup_name },
|
||||||
{ "compact_find_symbol", &sukisu_compact_find_symbol },
|
{ "compact_find_symbol", &sukisu_compact_find_symbol },
|
||||||
{ "compat_copy_to_user", ©_to_user },
|
{ "is_run_in_sukisu_ultra", (void*)1 },
|
||||||
{ "compat_strncpy_from_user", &strncpy_from_user },
|
{ "is_su_allow_uid", &sukisu_is_su_allow_uid },
|
||||||
{ "kpver", &kpver },
|
{ "get_ap_mod_exclude", &sukisu_get_ap_mod_exclude },
|
||||||
{ "is_run_in_sukisu_ultra", (void*)1 }
|
{ "is_uid_should_umount", &sukisu_is_uid_should_umount },
|
||||||
};
|
{ "is_current_uid_manager", &sukisu_is_current_uid_manager },
|
||||||
|
{ "get_manager_uid", &sukisu_get_manager_uid }
|
||||||
struct CompactAliasSymbol alias_symbol[] = {
|
|
||||||
{"kf_strncat", "strncat"},
|
|
||||||
{"kf_strlen", "strlen" },
|
|
||||||
{"kf_strcpy", "strcpy"},
|
|
||||||
{"compat_copy_to_user", "__arch_copy_to_user"}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
unsigned long sukisu_compact_find_symbol(const char* name) {
|
unsigned long sukisu_compact_find_symbol(const char* name) {
|
||||||
@@ -71,30 +90,13 @@ unsigned long sukisu_compact_find_symbol(const char* name) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 如果符号名以 "kf__" 开头,尝试解析去掉前缀的部分 */
|
|
||||||
if (strncmp(name, "kf__", 4) == 0) {
|
|
||||||
const char *real_name = name + 4; // 去掉 "kf__"
|
|
||||||
addr = (unsigned long)kallsyms_lookup_name(real_name);
|
|
||||||
if (addr) {
|
|
||||||
return addr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 通过内核来查
|
// 通过内核来查
|
||||||
addr = kallsyms_lookup_name(name);
|
addr = kallsyms_lookup_name(name);
|
||||||
if(addr) {
|
if(addr) {
|
||||||
return addr;
|
return addr;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 查不到就查查兼容的符号
|
|
||||||
for(i = 0; i < (sizeof(alias_symbol) / sizeof(struct CompactAliasSymbol)); i++) {
|
|
||||||
struct CompactAliasSymbol* symbol = &alias_symbol[i];
|
|
||||||
if(strcmp(name, symbol->symbol_name) == 0) {
|
|
||||||
addr = kallsyms_lookup_name(symbol->compact_symbol_name);
|
|
||||||
if(addr)
|
|
||||||
return addr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
EXPORT_SYMBOL(sukisu_compact_find_symbol);
|
||||||
|
|||||||
1340
kernel/kpm/kpm.c
1340
kernel/kpm/kpm.c
File diff suppressed because it is too large
Load Diff
@@ -1,44 +1,44 @@
|
|||||||
#ifndef ___SUKISU_KPM_H
|
#ifndef ___SUKISU_KPM_H
|
||||||
#define ___SUKISU_KPM_H
|
#define ___SUKISU_KPM_H
|
||||||
|
|
||||||
int sukisu_handle_kpm(unsigned long arg3, unsigned long arg4, unsigned long arg5);
|
int sukisu_handle_kpm(unsigned long arg2, unsigned long arg3, unsigned long arg4, unsigned long arg5);
|
||||||
int sukisu_is_kpm_control_code(unsigned long arg2);
|
int sukisu_is_kpm_control_code(unsigned long arg2);
|
||||||
|
|
||||||
// KPM控制代码
|
// KPM控制代码
|
||||||
#define CMD_KPM_CONTROL 28
|
#define CMD_KPM_CONTROL 28
|
||||||
#define CMD_KPM_CONTROL_MAX 34
|
#define CMD_KPM_CONTROL_MAX 35
|
||||||
|
|
||||||
// 控制代码
|
// 控制代码
|
||||||
|
|
||||||
// prctl(xxx, xxx, 1, "PATH", "ARGS")
|
// prctl(xxx, 28, "PATH", "ARGS")
|
||||||
// success return 0, error return -N
|
// success return 0, error return -N
|
||||||
#define SUKISU_KPM_LOAD 28
|
#define SUKISU_KPM_LOAD 28
|
||||||
|
|
||||||
// prctl(xxx, xxx, 2, "NAME")
|
// prctl(xxx, 29, "NAME")
|
||||||
// success return 0, error return -N
|
// success return 0, error return -N
|
||||||
#define SUKISU_KPM_UNLOAD 29
|
#define SUKISU_KPM_UNLOAD 29
|
||||||
|
|
||||||
// num = prctl(xxx, xxx, 3)
|
// num = prctl(xxx, 30)
|
||||||
// error return -N
|
// error return -N
|
||||||
// success return +num or 0
|
// success return +num or 0
|
||||||
#define SUKISU_KPM_NUM 30
|
#define SUKISU_KPM_NUM 30
|
||||||
|
|
||||||
// prctl(xxx, xxx, 4, Buffer, BufferSize)
|
// prctl(xxx, 31, Buffer, BufferSize)
|
||||||
// success return +out, error return -N
|
// success return +out, error return -N
|
||||||
#define SUKISU_KPM_LIST 31
|
#define SUKISU_KPM_LIST 31
|
||||||
|
|
||||||
// prctl(xxx, xxx, 5, "NAME", Buffer[256])
|
// prctl(xxx, 32, "NAME", Buffer[256])
|
||||||
// success return +out, error return -N
|
// success return +out, error return -N
|
||||||
#define SUKISU_KPM_INFO 32
|
#define SUKISU_KPM_INFO 32
|
||||||
|
|
||||||
// prctl(xxx, xxx, 6, "NAME", "ARGS")
|
// prctl(xxx, 33, "NAME", "ARGS")
|
||||||
// success return KPM's result value
|
// success return KPM's result value
|
||||||
// error return -N
|
// error return -N
|
||||||
#define SUKISU_KPM_CONTROL 33
|
#define SUKISU_KPM_CONTROL 33
|
||||||
|
|
||||||
// prctl(xxx, xxx, 7)
|
// prctl(xxx, 34, buffer, bufferSize)
|
||||||
// success will printf to stdout and return 0
|
// success return KPM's result value
|
||||||
// error will return -1
|
// error return -N
|
||||||
#define SUKISU_KPM_PRINT 34
|
#define SUKISU_KPM_VERSION 34
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
@@ -0,0 +1,285 @@
|
|||||||
|
#include <linux/export.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/kernel.h>
|
||||||
|
#include <linux/fs.h>
|
||||||
|
#include <linux/kernfs.h>
|
||||||
|
#include <linux/file.h>
|
||||||
|
#include <linux/slab.h>
|
||||||
|
#include <linux/vmalloc.h>
|
||||||
|
#include <linux/uaccess.h>
|
||||||
|
#include <linux/elf.h>
|
||||||
|
#include <linux/kallsyms.h>
|
||||||
|
#include <linux/version.h>
|
||||||
|
#include <linux/list.h>
|
||||||
|
#include <linux/spinlock.h>
|
||||||
|
#include <linux/rcupdate.h>
|
||||||
|
#include <asm/elf.h> /* 包含 ARM64 重定位类型定义 */
|
||||||
|
#include <linux/vmalloc.h>
|
||||||
|
#include <linux/mm.h>
|
||||||
|
#include <linux/string.h>
|
||||||
|
#include <asm/cacheflush.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/vmalloc.h>
|
||||||
|
#include <linux/set_memory.h>
|
||||||
|
#include <linux/version.h>
|
||||||
|
#include <linux/export.h>
|
||||||
|
#include <linux/slab.h>
|
||||||
|
#include "kpm.h"
|
||||||
|
#include "compact.h"
|
||||||
|
#include <linux/types.h>
|
||||||
|
#include <linux/stddef.h>
|
||||||
|
|
||||||
|
// 结构体成员元数据
|
||||||
|
struct DynamicStructMember {
|
||||||
|
const char* name;
|
||||||
|
size_t size;
|
||||||
|
size_t offset;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 结构体元数据(包含总大小)
|
||||||
|
struct DynamicStructInfo {
|
||||||
|
const char* name;
|
||||||
|
size_t count;
|
||||||
|
size_t total_size;
|
||||||
|
struct DynamicStructMember* members;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 定义结构体元数据的宏(直接使用 struct 名称)
|
||||||
|
#define DYNAMIC_STRUCT_BEGIN(struct_name) \
|
||||||
|
static struct DynamicStructMember struct_name##_members[] = {
|
||||||
|
|
||||||
|
#define DEFINE_MEMBER(struct_name, member) \
|
||||||
|
{ \
|
||||||
|
.name = #member, \
|
||||||
|
.size = sizeof(((struct struct_name*)0)->member), \
|
||||||
|
.offset = offsetof(struct struct_name, member) \
|
||||||
|
},
|
||||||
|
|
||||||
|
#define DYNAMIC_STRUCT_END(struct_name) \
|
||||||
|
}; \
|
||||||
|
static struct DynamicStructInfo struct_name##_info = { \
|
||||||
|
.name = #struct_name, \
|
||||||
|
.count = sizeof(struct_name##_members) / sizeof(struct DynamicStructMember), \
|
||||||
|
.total_size = sizeof(struct struct_name), \
|
||||||
|
.members = struct_name##_members \
|
||||||
|
};
|
||||||
|
|
||||||
|
// ==================================================================================
|
||||||
|
|
||||||
|
#include <linux/version.h>
|
||||||
|
|
||||||
|
#define KERNEL_VERSION_6_1 KERNEL_VERSION(6, 1, 0)
|
||||||
|
#define KERNEL_VERSION_5_15 KERNEL_VERSION(5, 15, 0)
|
||||||
|
|
||||||
|
#include <../fs/mount.h>
|
||||||
|
#include <linux/mount.h>
|
||||||
|
|
||||||
|
// 定义元数据
|
||||||
|
DYNAMIC_STRUCT_BEGIN(mount)
|
||||||
|
DEFINE_MEMBER(mount, mnt_parent)
|
||||||
|
DEFINE_MEMBER(mount, mnt)
|
||||||
|
DEFINE_MEMBER(mount, mnt_id)
|
||||||
|
DEFINE_MEMBER(mount, mnt_group_id)
|
||||||
|
DEFINE_MEMBER(mount, mnt_expiry_mark)
|
||||||
|
DEFINE_MEMBER(mount, mnt_master)
|
||||||
|
DEFINE_MEMBER(mount, mnt_devname)
|
||||||
|
DYNAMIC_STRUCT_END(mount)
|
||||||
|
|
||||||
|
DYNAMIC_STRUCT_BEGIN(vfsmount)
|
||||||
|
DEFINE_MEMBER(vfsmount, mnt_root)
|
||||||
|
DEFINE_MEMBER(vfsmount, mnt_sb)
|
||||||
|
DEFINE_MEMBER(vfsmount, mnt_flags)
|
||||||
|
DYNAMIC_STRUCT_END(vfsmount)
|
||||||
|
|
||||||
|
DYNAMIC_STRUCT_BEGIN(mnt_namespace)
|
||||||
|
DEFINE_MEMBER(mnt_namespace, ns)
|
||||||
|
DEFINE_MEMBER(mnt_namespace, root)
|
||||||
|
DEFINE_MEMBER(mnt_namespace, seq)
|
||||||
|
DEFINE_MEMBER(mnt_namespace, mounts)
|
||||||
|
#if LINUX_VERSION_CODE < KERNEL_VERSION_5_15
|
||||||
|
DEFINE_MEMBER(mnt_namespace, count)
|
||||||
|
#endif
|
||||||
|
DYNAMIC_STRUCT_END(mnt_namespace)
|
||||||
|
|
||||||
|
#include <linux/kprobes.h>
|
||||||
|
|
||||||
|
#ifdef CONFIG_KPROBES
|
||||||
|
DYNAMIC_STRUCT_BEGIN(kprobe)
|
||||||
|
DEFINE_MEMBER(kprobe, addr)
|
||||||
|
DEFINE_MEMBER(kprobe, symbol_name)
|
||||||
|
DEFINE_MEMBER(kprobe, offset)
|
||||||
|
DEFINE_MEMBER(kprobe, pre_handler)
|
||||||
|
DEFINE_MEMBER(kprobe, post_handler)
|
||||||
|
#if LINUX_VERSION_CODE < KERNEL_VERSION_5_15
|
||||||
|
DEFINE_MEMBER(kprobe, fault_handler)
|
||||||
|
#endif
|
||||||
|
DEFINE_MEMBER(kprobe, flags)
|
||||||
|
DYNAMIC_STRUCT_END(kprobe)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <linux/mm.h>
|
||||||
|
#include <linux/mm_types.h>
|
||||||
|
|
||||||
|
DYNAMIC_STRUCT_BEGIN(vm_area_struct)
|
||||||
|
DEFINE_MEMBER(vm_area_struct,vm_start)
|
||||||
|
DEFINE_MEMBER(vm_area_struct,vm_end)
|
||||||
|
DEFINE_MEMBER(vm_area_struct,vm_flags)
|
||||||
|
DEFINE_MEMBER(vm_area_struct,anon_vma)
|
||||||
|
DEFINE_MEMBER(vm_area_struct,vm_pgoff)
|
||||||
|
DEFINE_MEMBER(vm_area_struct,vm_file)
|
||||||
|
DEFINE_MEMBER(vm_area_struct,vm_private_data)
|
||||||
|
#ifdef CONFIG_ANON_VMA_NAME
|
||||||
|
DEFINE_MEMBER(vm_area_struct, anon_name)
|
||||||
|
#endif
|
||||||
|
DEFINE_MEMBER(vm_area_struct, vm_ops)
|
||||||
|
DYNAMIC_STRUCT_END(vm_area_struct)
|
||||||
|
|
||||||
|
DYNAMIC_STRUCT_BEGIN(vm_operations_struct)
|
||||||
|
DEFINE_MEMBER(vm_operations_struct, open)
|
||||||
|
DEFINE_MEMBER(vm_operations_struct, close)
|
||||||
|
DEFINE_MEMBER(vm_operations_struct, name)
|
||||||
|
DEFINE_MEMBER(vm_operations_struct, access)
|
||||||
|
DYNAMIC_STRUCT_END(vm_operations_struct)
|
||||||
|
|
||||||
|
#include <linux/netlink.h>
|
||||||
|
|
||||||
|
DYNAMIC_STRUCT_BEGIN(netlink_kernel_cfg)
|
||||||
|
DEFINE_MEMBER(netlink_kernel_cfg, groups)
|
||||||
|
DEFINE_MEMBER(netlink_kernel_cfg, flags)
|
||||||
|
DEFINE_MEMBER(netlink_kernel_cfg, input)
|
||||||
|
DEFINE_MEMBER(netlink_kernel_cfg, cb_mutex)
|
||||||
|
DEFINE_MEMBER(netlink_kernel_cfg, bind)
|
||||||
|
DEFINE_MEMBER(netlink_kernel_cfg, unbind)
|
||||||
|
#if LINUX_VERSION_CODE < KERNEL_VERSION_6_1
|
||||||
|
DEFINE_MEMBER(netlink_kernel_cfg, compare)
|
||||||
|
#endif
|
||||||
|
DYNAMIC_STRUCT_END(netlink_kernel_cfg)
|
||||||
|
|
||||||
|
|
||||||
|
#include <linux/sched.h>
|
||||||
|
DYNAMIC_STRUCT_BEGIN(task_struct)
|
||||||
|
DEFINE_MEMBER(task_struct, pid)
|
||||||
|
DEFINE_MEMBER(task_struct, tgid)
|
||||||
|
DEFINE_MEMBER(task_struct, cred)
|
||||||
|
DEFINE_MEMBER(task_struct, real_cred)
|
||||||
|
DEFINE_MEMBER(task_struct, comm)
|
||||||
|
DEFINE_MEMBER(task_struct, parent)
|
||||||
|
DEFINE_MEMBER(task_struct, group_leader)
|
||||||
|
DEFINE_MEMBER(task_struct, mm)
|
||||||
|
DEFINE_MEMBER(task_struct, active_mm)
|
||||||
|
DEFINE_MEMBER(task_struct, thread_pid)
|
||||||
|
DEFINE_MEMBER(task_struct, files)
|
||||||
|
DEFINE_MEMBER(task_struct, seccomp)
|
||||||
|
#ifdef CONFIG_THREAD_INFO_IN_TASK
|
||||||
|
DEFINE_MEMBER(task_struct, thread_info)
|
||||||
|
#endif
|
||||||
|
#ifdef CONFIG_CGROUPS
|
||||||
|
DEFINE_MEMBER(task_struct, cgroups)
|
||||||
|
#endif
|
||||||
|
#ifdef CONFIG_SECURITY
|
||||||
|
DEFINE_MEMBER(task_struct, security)
|
||||||
|
#endif
|
||||||
|
DEFINE_MEMBER(task_struct, thread)
|
||||||
|
DYNAMIC_STRUCT_END(task_struct)
|
||||||
|
|
||||||
|
// =====================================================================================================================
|
||||||
|
|
||||||
|
#define STRUCT_INFO(name) &(name##_info)
|
||||||
|
|
||||||
|
static
|
||||||
|
struct DynamicStructInfo* dynamic_struct_infos[] = {
|
||||||
|
STRUCT_INFO(mount),
|
||||||
|
STRUCT_INFO(vfsmount),
|
||||||
|
STRUCT_INFO(mnt_namespace),
|
||||||
|
#ifdef CONFIG_KPROBES
|
||||||
|
STRUCT_INFO(kprobe),
|
||||||
|
#endif
|
||||||
|
STRUCT_INFO(vm_area_struct),
|
||||||
|
STRUCT_INFO(vm_operations_struct),
|
||||||
|
STRUCT_INFO(netlink_kernel_cfg),
|
||||||
|
STRUCT_INFO(task_struct)
|
||||||
|
};
|
||||||
|
|
||||||
|
// return 0 if successful
|
||||||
|
// return -1 if struct not defined
|
||||||
|
int sukisu_super_find_struct(
|
||||||
|
const char* struct_name,
|
||||||
|
size_t* out_size,
|
||||||
|
int* out_members
|
||||||
|
) {
|
||||||
|
for(size_t i = 0; i < (sizeof(dynamic_struct_infos) / sizeof(dynamic_struct_infos[0])); i++) {
|
||||||
|
struct DynamicStructInfo* info = dynamic_struct_infos[i];
|
||||||
|
if(strcmp(struct_name, info->name) == 0) {
|
||||||
|
if(out_size)
|
||||||
|
*out_size = info->total_size;
|
||||||
|
if(out_members)
|
||||||
|
*out_members = info->count;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL(sukisu_super_find_struct);
|
||||||
|
|
||||||
|
// Dynamic access struct
|
||||||
|
// return 0 if successful
|
||||||
|
// return -1 if struct not defined
|
||||||
|
// return -2 if member not defined
|
||||||
|
int sukisu_super_access (
|
||||||
|
const char* struct_name,
|
||||||
|
const char* member_name,
|
||||||
|
size_t* out_offset,
|
||||||
|
size_t* out_size
|
||||||
|
) {
|
||||||
|
for(size_t i = 0; i < (sizeof(dynamic_struct_infos) / sizeof(dynamic_struct_infos[0])); i++) {
|
||||||
|
struct DynamicStructInfo* info = dynamic_struct_infos[i];
|
||||||
|
if(strcmp(struct_name, info->name) == 0) {
|
||||||
|
for (size_t i1 = 0; i1 < info->count; i1++) {
|
||||||
|
if (strcmp(info->members[i1].name, member_name) == 0) {
|
||||||
|
if(out_offset)
|
||||||
|
*out_offset = info->members[i].offset;
|
||||||
|
if(out_size)
|
||||||
|
*out_size = info->members[i].size;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL(sukisu_super_access);
|
||||||
|
|
||||||
|
// 动态 container_of 宏
|
||||||
|
#define DYNAMIC_CONTAINER_OF(offset, member_ptr) ({ \
|
||||||
|
(offset != (size_t)-1) ? (void*)((char*)(member_ptr) - offset) : NULL; \
|
||||||
|
})
|
||||||
|
|
||||||
|
// Dynamic container_of
|
||||||
|
// return 0 if success
|
||||||
|
// return -1 if current struct not defined
|
||||||
|
// return -2 if target member not defined
|
||||||
|
int sukisu_super_container_of(
|
||||||
|
const char* struct_name,
|
||||||
|
const char* member_name,
|
||||||
|
void* ptr,
|
||||||
|
void** out_ptr
|
||||||
|
) {
|
||||||
|
if(ptr == NULL) {
|
||||||
|
return -3;
|
||||||
|
}
|
||||||
|
for(size_t i = 0; i < (sizeof(dynamic_struct_infos) / sizeof(dynamic_struct_infos[0])); i++) {
|
||||||
|
struct DynamicStructInfo* info = dynamic_struct_infos[i];
|
||||||
|
if(strcmp(struct_name, info->name) == 0) {
|
||||||
|
for (size_t i1 = 0; i1 < info->count; i1++) {
|
||||||
|
if (strcmp(info->members[i1].name, member_name) == 0) {
|
||||||
|
*out_ptr = (void*) DYNAMIC_CONTAINER_OF(info->members[i1].offset, ptr);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL(sukisu_super_container_of);
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
#ifndef __SUKISU_SUPER_ACCESS_H
|
||||||
|
#define __SUKISU_SUPER_ACCESS_H
|
||||||
|
|
||||||
|
#include <linux/types.h>
|
||||||
|
#include <linux/stddef.h>
|
||||||
|
#include "kpm.h"
|
||||||
|
#include "compact.h"
|
||||||
|
|
||||||
|
// return 0 if successful
|
||||||
|
// return -1 if struct not defined
|
||||||
|
int sukisu_super_find_struct(
|
||||||
|
const char* struct_name,
|
||||||
|
size_t* out_size,
|
||||||
|
int* out_members
|
||||||
|
);
|
||||||
|
|
||||||
|
// Dynamic access struct
|
||||||
|
// return 0 if successful
|
||||||
|
// return -1 if struct not defined
|
||||||
|
// return -2 if member not defined
|
||||||
|
int sukisu_super_access (
|
||||||
|
const char* struct_name,
|
||||||
|
const char* member_name,
|
||||||
|
size_t* out_offset,
|
||||||
|
size_t* out_size
|
||||||
|
);
|
||||||
|
|
||||||
|
// Dynamic container_of
|
||||||
|
// return 0 if success
|
||||||
|
// return -1 if current struct not defined
|
||||||
|
// return -2 if target member not defined
|
||||||
|
int sukisu_super_container_of(
|
||||||
|
const char* struct_name,
|
||||||
|
const char* member_name,
|
||||||
|
void* ptr,
|
||||||
|
void** out_ptr
|
||||||
|
);
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -25,7 +25,7 @@ apksign {
|
|||||||
}
|
}
|
||||||
|
|
||||||
android {
|
android {
|
||||||
namespace = "shirkneko.zako.sukisu"
|
namespace = "zako.zako.zako"
|
||||||
|
|
||||||
buildTypes {
|
buildTypes {
|
||||||
release {
|
release {
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
// IKsuInterface.aidl
|
package zako.zako.zako;
|
||||||
package shirkneko.zako.sukisu;
|
|
||||||
|
|
||||||
import android.content.pm.PackageInfo;
|
import android.content.pm.PackageInfo;
|
||||||
import rikka.parcelablelist.ParcelableListSlice;
|
import rikka.parcelablelist.ParcelableListSlice;
|
||||||
@@ -12,7 +12,7 @@
|
|||||||
|
|
||||||
extern "C"
|
extern "C"
|
||||||
JNIEXPORT jboolean JNICALL
|
JNIEXPORT jboolean JNICALL
|
||||||
Java_shirkneko_zako_sukisu_Natives_becomeManager(JNIEnv *env, jobject, jstring pkg) {
|
Java_zako_zako_zako_Natives_becomeManager(JNIEnv *env, jobject, jstring pkg) {
|
||||||
auto cpkg = env->GetStringUTFChars(pkg, nullptr);
|
auto cpkg = env->GetStringUTFChars(pkg, nullptr);
|
||||||
auto result = become_manager(cpkg);
|
auto result = become_manager(cpkg);
|
||||||
env->ReleaseStringUTFChars(pkg, cpkg);
|
env->ReleaseStringUTFChars(pkg, cpkg);
|
||||||
@@ -21,13 +21,13 @@ Java_shirkneko_zako_sukisu_Natives_becomeManager(JNIEnv *env, jobject, jstring p
|
|||||||
|
|
||||||
extern "C"
|
extern "C"
|
||||||
JNIEXPORT jint JNICALL
|
JNIEXPORT jint JNICALL
|
||||||
Java_shirkneko_zako_sukisu_Natives_getVersion(JNIEnv *env, jobject) {
|
Java_zako_zako_zako_Natives_getVersion(JNIEnv *env, jobject) {
|
||||||
return get_version();
|
return get_version();
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C"
|
extern "C"
|
||||||
JNIEXPORT jintArray JNICALL
|
JNIEXPORT jintArray JNICALL
|
||||||
Java_shirkneko_zako_sukisu_Natives_getAllowList(JNIEnv *env, jobject) {
|
Java_zako_zako_zako_Natives_getAllowList(JNIEnv *env, jobject) {
|
||||||
int uids[1024];
|
int uids[1024];
|
||||||
int size = 0;
|
int size = 0;
|
||||||
bool result = get_allow_list(uids, &size);
|
bool result = get_allow_list(uids, &size);
|
||||||
@@ -42,13 +42,13 @@ Java_shirkneko_zako_sukisu_Natives_getAllowList(JNIEnv *env, jobject) {
|
|||||||
|
|
||||||
extern "C"
|
extern "C"
|
||||||
JNIEXPORT jboolean JNICALL
|
JNIEXPORT jboolean JNICALL
|
||||||
Java_shirkneko_zako_sukisu_Natives_isSafeMode(JNIEnv *env, jclass clazz) {
|
Java_zako_zako_zako_Natives_isSafeMode(JNIEnv *env, jclass clazz) {
|
||||||
return is_safe_mode();
|
return is_safe_mode();
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C"
|
extern "C"
|
||||||
JNIEXPORT jboolean JNICALL
|
JNIEXPORT jboolean JNICALL
|
||||||
Java_shirkneko_zako_sukisu_Natives_isLkmMode(JNIEnv *env, jclass clazz) {
|
Java_zako_zako_zako_Natives_isLkmMode(JNIEnv *env, jclass clazz) {
|
||||||
return is_lkm_mode();
|
return is_lkm_mode();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -111,7 +111,7 @@ static void fillArrayWithList(JNIEnv *env, jobject list, int *data, int count) {
|
|||||||
|
|
||||||
extern "C"
|
extern "C"
|
||||||
JNIEXPORT jobject JNICALL
|
JNIEXPORT jobject JNICALL
|
||||||
Java_shirkneko_zako_sukisu_Natives_getAppProfile(JNIEnv *env, jobject, jstring pkg, jint uid) {
|
Java_zako_zako_zako_Natives_getAppProfile(JNIEnv *env, jobject, jstring pkg, jint uid) {
|
||||||
if (env->GetStringLength(pkg) > KSU_MAX_PACKAGE_NAME) {
|
if (env->GetStringLength(pkg) > KSU_MAX_PACKAGE_NAME) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
@@ -129,7 +129,7 @@ Java_shirkneko_zako_sukisu_Natives_getAppProfile(JNIEnv *env, jobject, jstring p
|
|||||||
|
|
||||||
bool useDefaultProfile = !get_app_profile(key, &profile);
|
bool useDefaultProfile = !get_app_profile(key, &profile);
|
||||||
|
|
||||||
auto cls = env->FindClass("shirkneko/zako/sukisu/Natives$Profile");
|
auto cls = env->FindClass("zako/zako/zako/Natives$Profile");
|
||||||
auto constructor = env->GetMethodID(cls, "<init>", "()V");
|
auto constructor = env->GetMethodID(cls, "<init>", "()V");
|
||||||
auto obj = env->NewObject(cls, constructor);
|
auto obj = env->NewObject(cls, constructor);
|
||||||
auto keyField = env->GetFieldID(cls, "name", "Ljava/lang/String;");
|
auto keyField = env->GetFieldID(cls, "name", "Ljava/lang/String;");
|
||||||
@@ -207,8 +207,8 @@ Java_shirkneko_zako_sukisu_Natives_getAppProfile(JNIEnv *env, jobject, jstring p
|
|||||||
|
|
||||||
extern "C"
|
extern "C"
|
||||||
JNIEXPORT jboolean JNICALL
|
JNIEXPORT jboolean JNICALL
|
||||||
Java_shirkneko_zako_sukisu_Natives_setAppProfile(JNIEnv *env, jobject clazz, jobject profile) {
|
Java_zako_zako_zako_Natives_setAppProfile(JNIEnv *env, jobject clazz, jobject profile) {
|
||||||
auto cls = env->FindClass("shirkneko/zako/sukisu/Natives$Profile");
|
auto cls = env->FindClass("zako/zako/zako/Natives$Profile");
|
||||||
|
|
||||||
auto keyField = env->GetFieldID(cls, "name", "Ljava/lang/String;");
|
auto keyField = env->GetFieldID(cls, "name", "Ljava/lang/String;");
|
||||||
auto currentUidField = env->GetFieldID(cls, "currentUid", "I");
|
auto currentUidField = env->GetFieldID(cls, "currentUid", "I");
|
||||||
@@ -293,16 +293,16 @@ Java_shirkneko_zako_sukisu_Natives_setAppProfile(JNIEnv *env, jobject clazz, job
|
|||||||
}
|
}
|
||||||
extern "C"
|
extern "C"
|
||||||
JNIEXPORT jboolean JNICALL
|
JNIEXPORT jboolean JNICALL
|
||||||
Java_shirkneko_zako_sukisu_Natives_uidShouldUmount(JNIEnv *env, jobject thiz, jint uid) {
|
Java_zako_zako_zako_Natives_uidShouldUmount(JNIEnv *env, jobject thiz, jint uid) {
|
||||||
return uid_should_umount(uid);
|
return uid_should_umount(uid);
|
||||||
}
|
}
|
||||||
extern "C"
|
extern "C"
|
||||||
JNIEXPORT jboolean JNICALL
|
JNIEXPORT jboolean JNICALL
|
||||||
Java_shirkneko_zako_sukisu_Natives_isSuEnabled(JNIEnv *env, jobject thiz) {
|
Java_zako_zako_zako_Natives_isSuEnabled(JNIEnv *env, jobject thiz) {
|
||||||
return is_su_enabled();
|
return is_su_enabled();
|
||||||
}
|
}
|
||||||
extern "C"
|
extern "C"
|
||||||
JNIEXPORT jboolean JNICALL
|
JNIEXPORT jboolean JNICALL
|
||||||
Java_shirkneko_zako_sukisu_Natives_setSuEnabled(JNIEnv *env, jobject thiz, jboolean enabled) {
|
Java_zako_zako_zako_Natives_setSuEnabled(JNIEnv *env, jobject thiz, jboolean enabled) {
|
||||||
return set_su_enabled(enabled);
|
return set_su_enabled(enabled);
|
||||||
}
|
}
|
||||||
28
manager/app/src/main/java/io/zako/zako/UltraShellHelper.java
Normal file
28
manager/app/src/main/java/io/zako/zako/UltraShellHelper.java
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
package io.zako.zako;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
import zako.zako.zako.ui.util.KsuCli;
|
||||||
|
|
||||||
|
public class UltraShellHelper {
|
||||||
|
public static String runCmd(String cmds) {
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
for(String str : KsuCli.INSTANCE.getGLOBAL_MNT_SHELL()
|
||||||
|
.newJob()
|
||||||
|
.add(cmds)
|
||||||
|
.to(new ArrayList<>(), null)
|
||||||
|
.exec()
|
||||||
|
.getOut()) {
|
||||||
|
sb.append(str).append("\n");
|
||||||
|
}
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isPathExists(String path) {
|
||||||
|
return !runCmd("file " + path).contains("No such file or directory");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void CopyFileTo(String path, String target) {
|
||||||
|
runCmd("cp -f " + path + " " + target);
|
||||||
|
}
|
||||||
|
}
|
||||||
14
manager/app/src/main/java/io/zako/zako/UltraToolInstall.java
Normal file
14
manager/app/src/main/java/io/zako/zako/UltraToolInstall.java
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
package io.zako.zako;
|
||||||
|
|
||||||
|
import static zako.zako.zako.ui.util.KsuCliKt.getKpmmgrPath;
|
||||||
|
|
||||||
|
public class UltraToolInstall {
|
||||||
|
private static final String OUTSIDE_KPMMGR_PATH = "/data/adb/ksu/bin/kpmmgr";
|
||||||
|
public static void tryToInstall() {
|
||||||
|
String kpmmgrPath = getKpmmgrPath();
|
||||||
|
if (!UltraShellHelper.isPathExists(OUTSIDE_KPMMGR_PATH)) {
|
||||||
|
UltraShellHelper.CopyFileTo(kpmmgrPath, OUTSIDE_KPMMGR_PATH);
|
||||||
|
UltraShellHelper.runCmd("chmod a+rx " + OUTSIDE_KPMMGR_PATH);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,297 +0,0 @@
|
|||||||
package shirkneko.zako.sukisu.ui.screen
|
|
||||||
|
|
||||||
import android.app.Activity.RESULT_OK
|
|
||||||
import android.content.Intent
|
|
||||||
import android.util.Log
|
|
||||||
import androidx.activity.compose.rememberLauncherForActivityResult
|
|
||||||
import androidx.activity.result.contract.ActivityResultContracts
|
|
||||||
import androidx.compose.foundation.layout.*
|
|
||||||
import androidx.compose.foundation.lazy.LazyColumn
|
|
||||||
import androidx.compose.foundation.lazy.items
|
|
||||||
import androidx.compose.material.icons.Icons
|
|
||||||
import androidx.compose.material.icons.outlined.*
|
|
||||||
import androidx.compose.material3.*
|
|
||||||
import androidx.compose.material3.pulltorefresh.PullToRefreshBox
|
|
||||||
import androidx.compose.runtime.*
|
|
||||||
import androidx.compose.ui.Alignment
|
|
||||||
import androidx.compose.ui.Modifier
|
|
||||||
import androidx.compose.ui.platform.LocalContext
|
|
||||||
import androidx.compose.ui.res.stringResource
|
|
||||||
import androidx.compose.ui.text.style.TextAlign
|
|
||||||
import androidx.compose.ui.unit.dp
|
|
||||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
|
||||||
import com.ramcosta.composedestinations.annotation.Destination
|
|
||||||
import com.ramcosta.composedestinations.annotation.RootGraph
|
|
||||||
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import shirkneko.zako.sukisu.R
|
|
||||||
import shirkneko.zako.sukisu.ui.component.ConfirmResult
|
|
||||||
import shirkneko.zako.sukisu.ui.component.SearchAppBar
|
|
||||||
import shirkneko.zako.sukisu.ui.component.rememberConfirmDialog
|
|
||||||
import shirkneko.zako.sukisu.ui.component.rememberLoadingDialog
|
|
||||||
import shirkneko.zako.sukisu.ui.theme.getCardColors
|
|
||||||
import shirkneko.zako.sukisu.ui.theme.getCardElevation
|
|
||||||
import shirkneko.zako.sukisu.ui.viewmodel.KpmViewModel
|
|
||||||
import shirkneko.zako.sukisu.ui.util.loadKpmModule
|
|
||||||
import shirkneko.zako.sukisu.ui.util.unloadKpmModule
|
|
||||||
import java.io.File
|
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
|
||||||
@Destination<RootGraph>
|
|
||||||
@Composable
|
|
||||||
fun KpmScreen(
|
|
||||||
navigator: DestinationsNavigator,
|
|
||||||
viewModel: KpmViewModel = viewModel()
|
|
||||||
) {
|
|
||||||
val context = LocalContext.current
|
|
||||||
val scope = rememberCoroutineScope()
|
|
||||||
val snackBarHost = remember { SnackbarHostState() }
|
|
||||||
val confirmDialog = rememberConfirmDialog()
|
|
||||||
val loadingDialog = rememberLoadingDialog()
|
|
||||||
|
|
||||||
val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState())
|
|
||||||
|
|
||||||
val kpmInstall = stringResource(R.string.kpm_install)
|
|
||||||
val kpmInstallConfirm = stringResource(R.string.kpm_install_confirm)
|
|
||||||
val kpmInstallSuccess = stringResource(R.string.kpm_install_success)
|
|
||||||
val kpmInstallFailed = stringResource(R.string.kpm_install_failed)
|
|
||||||
val install = stringResource(R.string.install)
|
|
||||||
val cancel = stringResource(R.string.cancel)
|
|
||||||
val kpmUninstall = stringResource(R.string.kpm_uninstall)
|
|
||||||
val kpmUninstallConfirmTemplate = stringResource(R.string.kpm_uninstall_confirm)
|
|
||||||
val uninstall = stringResource(R.string.uninstall)
|
|
||||||
val kpmUninstallSuccess = stringResource(R.string.kpm_uninstall_success)
|
|
||||||
val kpmUninstallFailed = stringResource(R.string.kpm_uninstall_failed)
|
|
||||||
|
|
||||||
val selectPatchLauncher = rememberLauncherForActivityResult(
|
|
||||||
contract = ActivityResultContracts.StartActivityForResult()
|
|
||||||
) { result ->
|
|
||||||
if (result.resultCode != RESULT_OK) return@rememberLauncherForActivityResult
|
|
||||||
|
|
||||||
val uri = result.data?.data ?: return@rememberLauncherForActivityResult
|
|
||||||
|
|
||||||
scope.launch {
|
|
||||||
// 复制文件到临时目录
|
|
||||||
val tempFile = File(context.cacheDir, "temp_patch.kpm")
|
|
||||||
context.contentResolver.openInputStream(uri)?.use { input ->
|
|
||||||
tempFile.outputStream().use { output ->
|
|
||||||
input.copyTo(output)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val confirmResult = confirmDialog.awaitConfirm(
|
|
||||||
title = kpmInstall,
|
|
||||||
content = kpmInstallConfirm,
|
|
||||||
confirm = install,
|
|
||||||
dismiss = cancel
|
|
||||||
)
|
|
||||||
|
|
||||||
if (confirmResult == ConfirmResult.Confirmed) {
|
|
||||||
val success = loadingDialog.withLoading {
|
|
||||||
loadKpmModule(tempFile.absolutePath)
|
|
||||||
}
|
|
||||||
|
|
||||||
Log.d("KsuCli", "loadKpmModule result: $success")
|
|
||||||
|
|
||||||
if (success == "success") {
|
|
||||||
viewModel.fetchModuleList()
|
|
||||||
snackBarHost.showSnackbar(
|
|
||||||
message = kpmInstallSuccess,
|
|
||||||
duration = SnackbarDuration.Long
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
// 修正为显示安装失败的消息
|
|
||||||
snackBarHost.showSnackbar(
|
|
||||||
message = kpmInstallFailed,
|
|
||||||
duration = SnackbarDuration.Long
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
tempFile.delete()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
LaunchedEffect(Unit) {
|
|
||||||
if (viewModel.moduleList.isEmpty()) {
|
|
||||||
viewModel.fetchModuleList()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Scaffold(
|
|
||||||
topBar = {
|
|
||||||
SearchAppBar(
|
|
||||||
title = { Text(stringResource(R.string.kpm_title)) },
|
|
||||||
searchText = viewModel.search,
|
|
||||||
onSearchTextChange = { viewModel.search = it },
|
|
||||||
onClearClick = { viewModel.search = "" },
|
|
||||||
scrollBehavior = scrollBehavior,
|
|
||||||
dropdownContent = {
|
|
||||||
IconButton(onClick = { viewModel.fetchModuleList() }) {
|
|
||||||
Icon(
|
|
||||||
imageVector = Icons.Outlined.Refresh,
|
|
||||||
contentDescription = stringResource(R.string.refresh)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
},
|
|
||||||
floatingActionButton = {
|
|
||||||
ExtendedFloatingActionButton(
|
|
||||||
onClick = {
|
|
||||||
selectPatchLauncher.launch(
|
|
||||||
Intent(Intent.ACTION_GET_CONTENT).apply {
|
|
||||||
type = "application/*"
|
|
||||||
}
|
|
||||||
)
|
|
||||||
},
|
|
||||||
icon = {
|
|
||||||
Icon(
|
|
||||||
imageVector = Icons.Outlined.Add,
|
|
||||||
contentDescription = stringResource(R.string.kpm_install)
|
|
||||||
)
|
|
||||||
},
|
|
||||||
text = { Text(stringResource(R.string.kpm_install)) },
|
|
||||||
containerColor = MaterialTheme.colorScheme.secondaryContainer,
|
|
||||||
contentColor = MaterialTheme.colorScheme.onSecondaryContainer
|
|
||||||
)
|
|
||||||
},
|
|
||||||
snackbarHost = { SnackbarHost(snackBarHost) }
|
|
||||||
) { padding ->
|
|
||||||
PullToRefreshBox(
|
|
||||||
onRefresh = { viewModel.fetchModuleList() },
|
|
||||||
isRefreshing = viewModel.isRefreshing,
|
|
||||||
modifier = Modifier.padding(padding)
|
|
||||||
) {
|
|
||||||
if (viewModel.moduleList.isEmpty()) {
|
|
||||||
Box(
|
|
||||||
modifier = Modifier.fillMaxSize(),
|
|
||||||
contentAlignment = Alignment.Center
|
|
||||||
) {
|
|
||||||
Text(
|
|
||||||
stringResource(R.string.kpm_empty),
|
|
||||||
textAlign = TextAlign.Center
|
|
||||||
)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
LazyColumn(
|
|
||||||
modifier = Modifier.fillMaxSize(),
|
|
||||||
contentPadding = PaddingValues(16.dp),
|
|
||||||
verticalArrangement = Arrangement.spacedBy(16.dp)
|
|
||||||
) {
|
|
||||||
items(viewModel.moduleList) { module ->
|
|
||||||
val kpmUninstallConfirm = String.format(kpmUninstallConfirmTemplate, module.name)
|
|
||||||
KpmModuleItem(
|
|
||||||
module = module,
|
|
||||||
onUninstall = {
|
|
||||||
scope.launch {
|
|
||||||
val confirmResult = confirmDialog.awaitConfirm(
|
|
||||||
title = kpmUninstall,
|
|
||||||
content = kpmUninstallConfirm,
|
|
||||||
confirm = uninstall,
|
|
||||||
dismiss = cancel
|
|
||||||
)
|
|
||||||
if (confirmResult == ConfirmResult.Confirmed) {
|
|
||||||
val success = loadingDialog.withLoading {
|
|
||||||
unloadKpmModule(module.id)
|
|
||||||
}
|
|
||||||
Log.d("KsuCli", "unloadKpmModule result: $success")
|
|
||||||
if (success == "success") {
|
|
||||||
viewModel.fetchModuleList()
|
|
||||||
snackBarHost.showSnackbar(
|
|
||||||
message = kpmUninstallSuccess,
|
|
||||||
duration = SnackbarDuration.Long
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
snackBarHost.showSnackbar(
|
|
||||||
message = kpmUninstallFailed,
|
|
||||||
duration = SnackbarDuration.Long
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onControl = {
|
|
||||||
viewModel.loadModuleDetail(module.id)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
private fun KpmModuleItem(
|
|
||||||
module: KpmViewModel.ModuleInfo,
|
|
||||||
onUninstall: () -> Unit,
|
|
||||||
onControl: () -> Unit
|
|
||||||
) {
|
|
||||||
ElevatedCard(
|
|
||||||
colors = getCardColors(MaterialTheme.colorScheme.secondaryContainer),
|
|
||||||
elevation = CardDefaults.cardElevation(defaultElevation = getCardElevation())
|
|
||||||
) {
|
|
||||||
Column(
|
|
||||||
modifier = Modifier.padding(16.dp)
|
|
||||||
) {
|
|
||||||
Row(
|
|
||||||
modifier = Modifier.fillMaxWidth(),
|
|
||||||
horizontalArrangement = Arrangement.SpaceBetween,
|
|
||||||
verticalAlignment = Alignment.CenterVertically
|
|
||||||
) {
|
|
||||||
Column(modifier = Modifier.weight(1f)) {
|
|
||||||
Text(
|
|
||||||
text = module.name,
|
|
||||||
style = MaterialTheme.typography.titleMedium
|
|
||||||
)
|
|
||||||
Text(
|
|
||||||
text = "${stringResource(R.string.kpm_version)}: ${module.version}",
|
|
||||||
style = MaterialTheme.typography.bodyMedium
|
|
||||||
)
|
|
||||||
Text(
|
|
||||||
text = "${stringResource(R.string.kpm_author)}: ${module.author}",
|
|
||||||
style = MaterialTheme.typography.bodyMedium
|
|
||||||
)
|
|
||||||
Text(
|
|
||||||
text = "${stringResource(R.string.kpm_args)}: ${module.args}",
|
|
||||||
style = MaterialTheme.typography.bodyMedium
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Spacer(modifier = Modifier.height(8.dp))
|
|
||||||
|
|
||||||
Text(
|
|
||||||
text = module.description,
|
|
||||||
style = MaterialTheme.typography.bodyMedium
|
|
||||||
)
|
|
||||||
|
|
||||||
Spacer(modifier = Modifier.height(16.dp))
|
|
||||||
|
|
||||||
Row(
|
|
||||||
modifier = Modifier.fillMaxWidth(),
|
|
||||||
horizontalArrangement = Arrangement.spacedBy(8.dp)
|
|
||||||
) {
|
|
||||||
FilledTonalButton(
|
|
||||||
onClick = onControl
|
|
||||||
) {
|
|
||||||
Icon(
|
|
||||||
imageVector = Icons.Outlined.Settings,
|
|
||||||
contentDescription = null
|
|
||||||
)
|
|
||||||
Text(stringResource(R.string.kpm_control))
|
|
||||||
}
|
|
||||||
|
|
||||||
FilledTonalButton(
|
|
||||||
onClick = onUninstall
|
|
||||||
) {
|
|
||||||
Icon(
|
|
||||||
imageVector = Icons.Outlined.Delete,
|
|
||||||
contentDescription = null
|
|
||||||
)
|
|
||||||
Text(stringResource(R.string.kpm_uninstall))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,98 +0,0 @@
|
|||||||
package shirkneko.zako.sukisu.ui.viewmodel
|
|
||||||
|
|
||||||
import android.util.Log
|
|
||||||
import androidx.compose.runtime.getValue
|
|
||||||
import androidx.compose.runtime.mutableStateOf
|
|
||||||
import androidx.compose.runtime.setValue
|
|
||||||
import androidx.lifecycle.ViewModel
|
|
||||||
import androidx.lifecycle.viewModelScope
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import kotlinx.coroutines.withContext
|
|
||||||
import shirkneko.zako.sukisu.ui.util.*
|
|
||||||
|
|
||||||
class KpmViewModel : ViewModel() {
|
|
||||||
var moduleList by mutableStateOf(emptyList<ModuleInfo>())
|
|
||||||
private set
|
|
||||||
|
|
||||||
|
|
||||||
var search by mutableStateOf("")
|
|
||||||
internal set
|
|
||||||
|
|
||||||
var isRefreshing by mutableStateOf(false)
|
|
||||||
private set
|
|
||||||
|
|
||||||
var currentModuleDetail by mutableStateOf("")
|
|
||||||
private set
|
|
||||||
|
|
||||||
fun loadModuleDetail(moduleId: String) {
|
|
||||||
viewModelScope.launch {
|
|
||||||
currentModuleDetail = withContext(Dispatchers.IO) {
|
|
||||||
try {
|
|
||||||
getKpmModuleInfo(moduleId)
|
|
||||||
} catch (e: Exception) {
|
|
||||||
"无法获取模块详细信息: ${e.message}"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Log.d("KsuCli", "Module detail: $currentModuleDetail")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun fetchModuleList() {
|
|
||||||
viewModelScope.launch {
|
|
||||||
isRefreshing = true
|
|
||||||
try {
|
|
||||||
val moduleCount = getKpmModuleCount()
|
|
||||||
Log.d("KsuCli", "Module count: $moduleCount")
|
|
||||||
|
|
||||||
val moduleInfo = listKpmModules()
|
|
||||||
Log.d("KsuCli", "Module info: $moduleInfo")
|
|
||||||
|
|
||||||
val modules = parseModuleList(moduleInfo)
|
|
||||||
moduleList = modules
|
|
||||||
} finally {
|
|
||||||
isRefreshing = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun getInstalledKernelPatches(): List<ModuleInfo> {
|
|
||||||
return try {
|
|
||||||
val output = printKpmModules()
|
|
||||||
parseModuleList(output)
|
|
||||||
} catch (e: Exception) {
|
|
||||||
emptyList()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun parseModuleList(output: String): List<ModuleInfo> {
|
|
||||||
return output.split("\n").mapNotNull { line ->
|
|
||||||
if (line.isBlank()) return@mapNotNull null
|
|
||||||
val parts = line.split("|")
|
|
||||||
if (parts.size < 7) return@mapNotNull null
|
|
||||||
|
|
||||||
ModuleInfo(
|
|
||||||
id = parts[0].trim(),
|
|
||||||
name = parts[1].trim(),
|
|
||||||
version = parts[2].trim(),
|
|
||||||
author = parts[3].trim(),
|
|
||||||
description = parts[4].trim(),
|
|
||||||
args = parts[6].trim(),
|
|
||||||
enabled = true,
|
|
||||||
hasAction = controlKpmModule(parts[0].trim()).isNotBlank()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
data class ModuleInfo(
|
|
||||||
val id: String,
|
|
||||||
val name: String,
|
|
||||||
val version: String,
|
|
||||||
val author: String,
|
|
||||||
val description: String,
|
|
||||||
val args: String,
|
|
||||||
val enabled: Boolean,
|
|
||||||
val hasAction: Boolean
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package shirkneko.zako.sukisu
|
package zako.zako.zako
|
||||||
|
|
||||||
import android.app.Application
|
import android.app.Application
|
||||||
import coil.Coil
|
import coil.Coil
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package shirkneko.zako.sukisu
|
package zako.zako.zako
|
||||||
|
|
||||||
import android.system.Os
|
import android.system.Os
|
||||||
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package shirkneko.zako.sukisu
|
package zako.zako.zako
|
||||||
|
|
||||||
import android.os.Parcelable
|
import android.os.Parcelable
|
||||||
import androidx.annotation.Keep
|
import androidx.annotation.Keep
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package shirkneko.zako.sukisu.profile
|
package zako.zako.zako.profile
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author weishu
|
* @author weishu
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package shirkneko.zako.sukisu.profile
|
package zako.zako.zako.profile
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* https://cs.android.com/android/platform/superproject/main/+/main:system/core/libcutils/include/private/android_filesystem_config.h
|
* https://cs.android.com/android/platform/superproject/main/+/main:system/core/libcutils/include/private/android_filesystem_config.h
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package shirkneko.zako.sukisu.ui;
|
package zako.zako.zako.ui;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
@@ -17,7 +17,7 @@ import java.lang.reflect.Method;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import shirkneko.zako.sukisu.IKsuInterface;
|
import zako.zako.zako.IKsuInterface;
|
||||||
import rikka.parcelablelist.ParcelableListSlice;
|
import rikka.parcelablelist.ParcelableListSlice;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package shirkneko.zako.sukisu.ui
|
package zako.zako.zako.ui
|
||||||
|
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
@@ -30,8 +30,6 @@ import androidx.compose.runtime.CompositionLocalProvider
|
|||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.graphics.Color
|
|
||||||
import androidx.compose.ui.graphics.luminance
|
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.navigation.NavBackStackEntry
|
import androidx.navigation.NavBackStackEntry
|
||||||
import androidx.navigation.NavHostController
|
import androidx.navigation.NavHostController
|
||||||
@@ -41,16 +39,18 @@ import com.ramcosta.composedestinations.animations.NavHostAnimatedDestinationSty
|
|||||||
import com.ramcosta.composedestinations.generated.NavGraphs
|
import com.ramcosta.composedestinations.generated.NavGraphs
|
||||||
import com.ramcosta.composedestinations.utils.isRouteOnBackStackAsState
|
import com.ramcosta.composedestinations.utils.isRouteOnBackStackAsState
|
||||||
import com.ramcosta.composedestinations.utils.rememberDestinationsNavigator
|
import com.ramcosta.composedestinations.utils.rememberDestinationsNavigator
|
||||||
import shirkneko.zako.sukisu.Natives
|
import io.zako.zako.UltraToolInstall
|
||||||
import shirkneko.zako.sukisu.ksuApp
|
import zako.zako.zako.Natives
|
||||||
import shirkneko.zako.sukisu.ui.screen.BottomBarDestination
|
import zako.zako.zako.ksuApp
|
||||||
import shirkneko.zako.sukisu.ui.theme.CardConfig
|
import zako.zako.zako.ui.screen.BottomBarDestination
|
||||||
import shirkneko.zako.sukisu.ui.theme.KernelSUTheme
|
import zako.zako.zako.ui.theme.CardConfig
|
||||||
import shirkneko.zako.sukisu.ui.theme.loadCustomBackground
|
import zako.zako.zako.ui.theme.KernelSUTheme
|
||||||
import shirkneko.zako.sukisu.ui.theme.loadThemeMode
|
import zako.zako.zako.ui.theme.loadCustomBackground
|
||||||
import shirkneko.zako.sukisu.ui.util.LocalSnackbarHost
|
import zako.zako.zako.ui.theme.loadThemeMode
|
||||||
import shirkneko.zako.sukisu.ui.util.rootAvailable
|
import zako.zako.zako.ui.util.LocalSnackbarHost
|
||||||
import shirkneko.zako.sukisu.ui.util.install
|
import zako.zako.zako.ui.util.getKpmVersion
|
||||||
|
import zako.zako.zako.ui.util.rootAvailable
|
||||||
|
import zako.zako.zako.ui.util.install
|
||||||
|
|
||||||
class MainActivity : ComponentActivity() {
|
class MainActivity : ComponentActivity() {
|
||||||
|
|
||||||
@@ -71,7 +71,10 @@ class MainActivity : ComponentActivity() {
|
|||||||
|
|
||||||
|
|
||||||
val isManager = Natives.becomeManager(ksuApp.packageName)
|
val isManager = Natives.becomeManager(ksuApp.packageName)
|
||||||
if (isManager) install()
|
if (isManager) {
|
||||||
|
install()
|
||||||
|
UltraToolInstall.tryToInstall()
|
||||||
|
}
|
||||||
|
|
||||||
setContent {
|
setContent {
|
||||||
KernelSUTheme {
|
KernelSUTheme {
|
||||||
@@ -107,6 +110,7 @@ private fun BottomBar(navController: NavHostController) {
|
|||||||
val navigator = navController.rememberDestinationsNavigator()
|
val navigator = navController.rememberDestinationsNavigator()
|
||||||
val isManager = Natives.becomeManager(ksuApp.packageName)
|
val isManager = Natives.becomeManager(ksuApp.packageName)
|
||||||
val fullFeatured = isManager && !Natives.requireNewKernel() && rootAvailable()
|
val fullFeatured = isManager && !Natives.requireNewKernel() && rootAvailable()
|
||||||
|
val kpmVersion = getKpmVersion()
|
||||||
|
|
||||||
// 获取卡片颜色和透明度
|
// 获取卡片颜色和透明度
|
||||||
val cardColor = MaterialTheme.colorScheme.secondaryContainer
|
val cardColor = MaterialTheme.colorScheme.secondaryContainer
|
||||||
@@ -115,13 +119,14 @@ private fun BottomBar(navController: NavHostController) {
|
|||||||
|
|
||||||
NavigationBar(
|
NavigationBar(
|
||||||
tonalElevation = cardElevation, // 动态设置阴影
|
tonalElevation = cardElevation, // 动态设置阴影
|
||||||
containerColor = cardColor.copy(alpha = cardAlpha), // 动态设置颜色和透明度
|
containerColor = cardColor.copy(alpha = cardAlpha),
|
||||||
contentColor = if (cardColor.luminance() > 0.5) Color.Black else Color.White, // 根据背景亮度设置文字颜色
|
|
||||||
windowInsets = WindowInsets.systemBars.union(WindowInsets.displayCutout).only(
|
windowInsets = WindowInsets.systemBars.union(WindowInsets.displayCutout).only(
|
||||||
WindowInsetsSides.Horizontal + WindowInsetsSides.Bottom
|
WindowInsetsSides.Horizontal + WindowInsetsSides.Bottom
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
BottomBarDestination.entries.forEach { destination ->
|
BottomBarDestination.entries.forEach { destination ->
|
||||||
|
if (destination == BottomBarDestination.Kpm) {
|
||||||
|
if (kpmVersion.isNotEmpty() && !kpmVersion.startsWith("Error")) {
|
||||||
if (!fullFeatured && destination.rootRequired) return@forEach
|
if (!fullFeatured && destination.rootRequired) return@forEach
|
||||||
val isCurrentDestOnBackStack by navController.isRouteOnBackStackAsState(destination.direction)
|
val isCurrentDestOnBackStack by navController.isRouteOnBackStackAsState(destination.direction)
|
||||||
NavigationBarItem(
|
NavigationBarItem(
|
||||||
@@ -152,5 +157,34 @@ private fun BottomBar(navController: NavHostController) {
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
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
|
||||||
|
}
|
||||||
|
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 = false,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package shirkneko.zako.sukisu.ui.component
|
package zako.zako.zako.ui.component
|
||||||
|
|
||||||
import androidx.compose.foundation.Image
|
import androidx.compose.foundation.Image
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
@@ -31,8 +31,8 @@ import androidx.compose.ui.tooling.preview.Preview
|
|||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
import androidx.compose.ui.window.Dialog
|
import androidx.compose.ui.window.Dialog
|
||||||
import shirkneko.zako.sukisu.BuildConfig
|
import zako.zako.zako.BuildConfig
|
||||||
import shirkneko.zako.sukisu.R
|
import zako.zako.zako.R
|
||||||
|
|
||||||
@Preview
|
@Preview
|
||||||
@Composable
|
@Composable
|
||||||
@@ -98,7 +98,7 @@ private fun AboutCardContent() {
|
|||||||
val annotatedString = AnnotatedString.Companion.fromHtml(
|
val annotatedString = AnnotatedString.Companion.fromHtml(
|
||||||
htmlString = stringResource(
|
htmlString = stringResource(
|
||||||
id = R.string.about_source_code,
|
id = R.string.about_source_code,
|
||||||
"<b><a href=\"https://github.com/ShirkNeko/KernelSU\">GitHub</a></b>",
|
"<b><a href=\"https://github.com/ShirkNeko/SukiSU-Ultra\">GitHub</a></b>",
|
||||||
"<b><a href=\"https://t.me/SukiKSU\">Telegram</a></b>"
|
"<b><a href=\"https://t.me/SukiKSU\">Telegram</a></b>"
|
||||||
),
|
),
|
||||||
linkStyles = TextLinkStyles(
|
linkStyles = TextLinkStyles(
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package shirkneko.zako.sukisu.ui.component
|
package zako.zako.zako.ui.component
|
||||||
|
|
||||||
import android.graphics.text.LineBreaker
|
import android.graphics.text.LineBreaker
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package shirkneko.zako.sukisu.ui.component
|
package zako.zako.zako.ui.component
|
||||||
|
|
||||||
import androidx.compose.foundation.focusable
|
import androidx.compose.foundation.focusable
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package shirkneko.zako.sukisu.ui.component
|
package zako.zako.zako.ui.component
|
||||||
|
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import androidx.compose.animation.AnimatedVisibility
|
import androidx.compose.animation.AnimatedVisibility
|
||||||
@@ -42,7 +42,7 @@ import androidx.compose.ui.platform.LocalSoftwareKeyboardController
|
|||||||
import androidx.compose.ui.text.input.ImeAction
|
import androidx.compose.ui.text.input.ImeAction
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import shirkneko.zako.sukisu.ui.theme.CardConfig
|
import zako.zako.zako.ui.theme.CardConfig
|
||||||
|
|
||||||
private const val TAG = "SearchBar"
|
private const val TAG = "SearchBar"
|
||||||
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package shirkneko.zako.sukisu.ui.component
|
package zako.zako.zako.ui.component
|
||||||
|
|
||||||
import androidx.compose.foundation.LocalIndication
|
import androidx.compose.foundation.LocalIndication
|
||||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package shirkneko.zako.sukisu.ui.component
|
package zako.zako.zako.ui.component
|
||||||
|
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.ListItem
|
import androidx.compose.material3.ListItem
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package shirkneko.zako.sukisu.ui.component.profile
|
package zako.zako.zako.ui.component.profile
|
||||||
|
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.material3.OutlinedTextField
|
import androidx.compose.material3.OutlinedTextField
|
||||||
@@ -11,9 +11,9 @@ import androidx.compose.runtime.setValue
|
|||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
import shirkneko.zako.sukisu.Natives
|
import zako.zako.zako.Natives
|
||||||
import shirkneko.zako.sukisu.R
|
import zako.zako.zako.R
|
||||||
import shirkneko.zako.sukisu.ui.component.SwitchItem
|
import zako.zako.zako.ui.component.SwitchItem
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun AppProfileConfig(
|
fun AppProfileConfig(
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package shirkneko.zako.sukisu.ui.component.profile
|
package zako.zako.zako.ui.component.profile
|
||||||
|
|
||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.clickable
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
@@ -42,12 +42,12 @@ import com.maxkeppeler.sheets.input.models.ValidationResult
|
|||||||
import com.maxkeppeler.sheets.list.ListDialog
|
import com.maxkeppeler.sheets.list.ListDialog
|
||||||
import com.maxkeppeler.sheets.list.models.ListOption
|
import com.maxkeppeler.sheets.list.models.ListOption
|
||||||
import com.maxkeppeler.sheets.list.models.ListSelection
|
import com.maxkeppeler.sheets.list.models.ListSelection
|
||||||
import shirkneko.zako.sukisu.Natives
|
import zako.zako.zako.Natives
|
||||||
import shirkneko.zako.sukisu.R
|
import zako.zako.zako.R
|
||||||
import shirkneko.zako.sukisu.profile.Capabilities
|
import zako.zako.zako.profile.Capabilities
|
||||||
import shirkneko.zako.sukisu.profile.Groups
|
import zako.zako.zako.profile.Groups
|
||||||
import shirkneko.zako.sukisu.ui.component.rememberCustomDialog
|
import zako.zako.zako.ui.component.rememberCustomDialog
|
||||||
import shirkneko.zako.sukisu.ui.util.isSepolicyValid
|
import zako.zako.zako.ui.util.isSepolicyValid
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package shirkneko.zako.sukisu.ui.component.profile
|
package zako.zako.zako.ui.component.profile
|
||||||
|
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
@@ -23,11 +23,11 @@ import androidx.compose.runtime.saveable.rememberSaveable
|
|||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import shirkneko.zako.sukisu.Natives
|
import zako.zako.zako.Natives
|
||||||
import shirkneko.zako.sukisu.R
|
import zako.zako.zako.R
|
||||||
import shirkneko.zako.sukisu.ui.util.listAppProfileTemplates
|
import zako.zako.zako.ui.util.listAppProfileTemplates
|
||||||
import shirkneko.zako.sukisu.ui.util.setSepolicy
|
import zako.zako.zako.ui.util.setSepolicy
|
||||||
import shirkneko.zako.sukisu.ui.viewmodel.getTemplateInfoById
|
import zako.zako.zako.ui.viewmodel.getTemplateInfoById
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author weishu
|
* @author weishu
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package shirkneko.zako.sukisu.ui.screen
|
package zako.zako.zako.ui.screen
|
||||||
|
|
||||||
import androidx.annotation.StringRes
|
import androidx.annotation.StringRes
|
||||||
import androidx.compose.animation.Crossfade
|
import androidx.compose.animation.Crossfade
|
||||||
@@ -64,20 +64,20 @@ import com.ramcosta.composedestinations.generated.destinations.AppProfileTemplat
|
|||||||
import com.ramcosta.composedestinations.generated.destinations.TemplateEditorScreenDestination
|
import com.ramcosta.composedestinations.generated.destinations.TemplateEditorScreenDestination
|
||||||
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
|
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import shirkneko.zako.sukisu.Natives
|
import zako.zako.zako.Natives
|
||||||
import shirkneko.zako.sukisu.R
|
import zako.zako.zako.R
|
||||||
import shirkneko.zako.sukisu.ui.component.SwitchItem
|
import zako.zako.zako.ui.component.SwitchItem
|
||||||
import shirkneko.zako.sukisu.ui.component.profile.AppProfileConfig
|
import zako.zako.zako.ui.component.profile.AppProfileConfig
|
||||||
import shirkneko.zako.sukisu.ui.component.profile.RootProfileConfig
|
import zako.zako.zako.ui.component.profile.RootProfileConfig
|
||||||
import shirkneko.zako.sukisu.ui.component.profile.TemplateConfig
|
import zako.zako.zako.ui.component.profile.TemplateConfig
|
||||||
import shirkneko.zako.sukisu.ui.util.LocalSnackbarHost
|
import zako.zako.zako.ui.util.LocalSnackbarHost
|
||||||
import shirkneko.zako.sukisu.ui.util.forceStopApp
|
import zako.zako.zako.ui.util.forceStopApp
|
||||||
import shirkneko.zako.sukisu.ui.util.getSepolicy
|
import zako.zako.zako.ui.util.getSepolicy
|
||||||
import shirkneko.zako.sukisu.ui.util.launchApp
|
import zako.zako.zako.ui.util.launchApp
|
||||||
import shirkneko.zako.sukisu.ui.util.restartApp
|
import zako.zako.zako.ui.util.restartApp
|
||||||
import shirkneko.zako.sukisu.ui.util.setSepolicy
|
import zako.zako.zako.ui.util.setSepolicy
|
||||||
import shirkneko.zako.sukisu.ui.viewmodel.SuperUserViewModel
|
import zako.zako.zako.ui.viewmodel.SuperUserViewModel
|
||||||
import shirkneko.zako.sukisu.ui.viewmodel.getTemplateInfoById
|
import zako.zako.zako.ui.viewmodel.getTemplateInfoById
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author weishu
|
* @author weishu
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package shirkneko.zako.sukisu.ui.screen
|
package zako.zako.zako.ui.screen
|
||||||
|
|
||||||
import androidx.annotation.StringRes
|
import androidx.annotation.StringRes
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
@@ -11,7 +11,7 @@ import com.ramcosta.composedestinations.generated.destinations.SuperUserScreenDe
|
|||||||
import com.ramcosta.composedestinations.generated.destinations.SettingScreenDestination
|
import com.ramcosta.composedestinations.generated.destinations.SettingScreenDestination
|
||||||
import com.ramcosta.composedestinations.generated.destinations.KpmScreenDestination
|
import com.ramcosta.composedestinations.generated.destinations.KpmScreenDestination
|
||||||
import com.ramcosta.composedestinations.spec.DirectionDestinationSpec
|
import com.ramcosta.composedestinations.spec.DirectionDestinationSpec
|
||||||
import shirkneko.zako.sukisu.R
|
import zako.zako.zako.R
|
||||||
|
|
||||||
enum class BottomBarDestination(
|
enum class BottomBarDestination(
|
||||||
val direction: DirectionDestinationSpec,
|
val direction: DirectionDestinationSpec,
|
||||||
@@ -21,8 +21,8 @@ enum class BottomBarDestination(
|
|||||||
val rootRequired: Boolean,
|
val rootRequired: Boolean,
|
||||||
) {
|
) {
|
||||||
Home(HomeScreenDestination, R.string.home, Icons.Filled.Home, Icons.Outlined.Home, false),
|
Home(HomeScreenDestination, R.string.home, Icons.Filled.Home, Icons.Outlined.Home, false),
|
||||||
|
Kpm(KpmScreenDestination, R.string.kpm_title, Icons.Filled.Build, Icons.Outlined.Build, true),
|
||||||
SuperUser(SuperUserScreenDestination, R.string.superuser, Icons.Filled.Security, Icons.Outlined.Security, true),
|
SuperUser(SuperUserScreenDestination, R.string.superuser, Icons.Filled.Security, Icons.Outlined.Security, true),
|
||||||
Module(ModuleScreenDestination, R.string.module, Icons.Filled.Apps, Icons.Outlined.Apps, true),
|
Module(ModuleScreenDestination, R.string.module, Icons.Filled.Apps, Icons.Outlined.Apps, true),
|
||||||
Kpm(KpmScreenDestination, R.string.kpm_title, Icons.Filled.Build, Icons.Outlined.Build, true),
|
|
||||||
Settings(SettingScreenDestination, R.string.settings, Icons.Filled.Settings, Icons.Outlined.Settings, false),
|
Settings(SettingScreenDestination, R.string.settings, Icons.Filled.Settings, Icons.Outlined.Settings, false),
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package shirkneko.zako.sukisu.ui.screen
|
package zako.zako.zako.ui.screen
|
||||||
|
|
||||||
import android.os.Environment
|
import android.os.Environment
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
@@ -37,10 +37,10 @@ import com.ramcosta.composedestinations.navigation.DestinationsNavigator
|
|||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import shirkneko.zako.sukisu.R
|
import zako.zako.zako.R
|
||||||
import shirkneko.zako.sukisu.ui.component.KeyEventBlocker
|
import zako.zako.zako.ui.component.KeyEventBlocker
|
||||||
import shirkneko.zako.sukisu.ui.util.LocalSnackbarHost
|
import zako.zako.zako.ui.util.LocalSnackbarHost
|
||||||
import shirkneko.zako.sukisu.ui.util.runModuleAction
|
import zako.zako.zako.ui.util.runModuleAction
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.text.SimpleDateFormat
|
import java.text.SimpleDateFormat
|
||||||
import java.util.Date
|
import java.util.Date
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package shirkneko.zako.sukisu.ui.screen
|
package zako.zako.zako.ui.screen
|
||||||
|
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.os.Environment
|
import android.os.Environment
|
||||||
@@ -30,9 +30,9 @@ import kotlinx.coroutines.Dispatchers
|
|||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import kotlinx.parcelize.Parcelize
|
import kotlinx.parcelize.Parcelize
|
||||||
import shirkneko.zako.sukisu.R
|
import zako.zako.zako.ui.component.KeyEventBlocker
|
||||||
import shirkneko.zako.sukisu.ui.component.KeyEventBlocker
|
import zako.zako.zako.ui.util.*
|
||||||
import shirkneko.zako.sukisu.ui.util.*
|
import zako.zako.zako.R
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.text.SimpleDateFormat
|
import java.text.SimpleDateFormat
|
||||||
import java.util.*
|
import java.util.*
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package shirkneko.zako.sukisu.ui.screen
|
package zako.zako.zako.ui.screen
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
@@ -33,15 +33,15 @@ import com.ramcosta.composedestinations.generated.destinations.SettingScreenDest
|
|||||||
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
|
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import shirkneko.zako.sukisu.*
|
import zako.zako.zako.*
|
||||||
import shirkneko.zako.sukisu.R
|
import zako.zako.zako.R
|
||||||
import shirkneko.zako.sukisu.ui.component.rememberConfirmDialog
|
import zako.zako.zako.ui.component.rememberConfirmDialog
|
||||||
import shirkneko.zako.sukisu.ui.util.*
|
import zako.zako.zako.ui.util.*
|
||||||
import shirkneko.zako.sukisu.ui.util.module.LatestVersionInfo
|
import zako.zako.zako.ui.util.module.LatestVersionInfo
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import shirkneko.zako.sukisu.ui.theme.getCardColors
|
import zako.zako.zako.ui.theme.getCardColors
|
||||||
import shirkneko.zako.sukisu.ui.theme.getCardElevation
|
import zako.zako.zako.ui.theme.getCardElevation
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
@@ -49,8 +49,12 @@ import androidx.compose.animation.AnimatedVisibility
|
|||||||
import androidx.compose.animation.fadeOut
|
import androidx.compose.animation.fadeOut
|
||||||
import androidx.compose.animation.shrinkVertically
|
import androidx.compose.animation.shrinkVertically
|
||||||
import androidx.compose.runtime.saveable.rememberSaveable
|
import androidx.compose.runtime.saveable.rememberSaveable
|
||||||
import shirkneko.zako.sukisu.ui.theme.CardConfig
|
import zako.zako.zako.ui.theme.CardConfig
|
||||||
import androidx.core.content.edit
|
import androidx.core.content.edit
|
||||||
|
import java.io.BufferedReader
|
||||||
|
import java.io.InputStreamReader
|
||||||
|
import java.util.zip.GZIPInputStream
|
||||||
|
import kotlin.random.Random
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Destination<RootGraph>(start = true)
|
@Destination<RootGraph>(start = true)
|
||||||
@@ -59,6 +63,8 @@ fun HomeScreen(navigator: DestinationsNavigator) {
|
|||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
var isSimpleMode by rememberSaveable { mutableStateOf(false) }
|
var isSimpleMode by rememberSaveable { mutableStateOf(false) }
|
||||||
var isHideVersion by rememberSaveable { mutableStateOf(false) }
|
var isHideVersion by rememberSaveable { mutableStateOf(false) }
|
||||||
|
var isHideOtherInfo by rememberSaveable { mutableStateOf(false) }
|
||||||
|
var isHideSusfsStatus by rememberSaveable { mutableStateOf(false) }
|
||||||
|
|
||||||
// 从 SharedPreferences 加载简洁模式状态
|
// 从 SharedPreferences 加载简洁模式状态
|
||||||
LaunchedEffect(Unit) {
|
LaunchedEffect(Unit) {
|
||||||
@@ -70,9 +76,38 @@ fun HomeScreen(navigator: DestinationsNavigator) {
|
|||||||
isHideVersion = context.getSharedPreferences("settings", Context.MODE_PRIVATE)
|
isHideVersion = context.getSharedPreferences("settings", Context.MODE_PRIVATE)
|
||||||
.getBoolean("is_hide_version", false)
|
.getBoolean("is_hide_version", false)
|
||||||
}
|
}
|
||||||
|
// 从 SharedPreferences 加载隐藏模块数量等信息开关状态
|
||||||
|
LaunchedEffect(Unit) {
|
||||||
|
isHideOtherInfo = context.getSharedPreferences("settings", Context.MODE_PRIVATE)
|
||||||
|
.getBoolean("is_hide_other_info", false)
|
||||||
|
}
|
||||||
|
// 从 SharedPreferences 加载隐藏 SuSFS 状态开关状态
|
||||||
|
LaunchedEffect(Unit) {
|
||||||
|
isHideSusfsStatus = context.getSharedPreferences("settings", Context.MODE_PRIVATE)
|
||||||
|
.getBoolean("is_hide_susfs_status", false)
|
||||||
|
}
|
||||||
val kernelVersion = getKernelVersion()
|
val kernelVersion = getKernelVersion()
|
||||||
val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState())
|
val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState())
|
||||||
|
|
||||||
|
val isManager = Natives.becomeManager(ksuApp.packageName)
|
||||||
|
val deviceModel = getDeviceModel(context)
|
||||||
|
val ksuVersion = if (isManager) Natives.version else null
|
||||||
|
val managerVersion = getManagerVersion(context).second
|
||||||
|
val Zako = "一.*加.*A.*c.*e.*5.*P.*r.*o".toRegex().matches(deviceModel)
|
||||||
|
val isVersion = ksuVersion == 12777
|
||||||
|
val isManagerVersionValid = managerVersion > (ksuVersion ?: 0) + 33
|
||||||
|
val shouldTriggerRestart = Zako && kernelVersion.isGKI() && (isVersion || isManagerVersionValid)
|
||||||
|
|
||||||
|
LaunchedEffect(shouldTriggerRestart) {
|
||||||
|
if (shouldTriggerRestart) {
|
||||||
|
val random = Random.nextInt(0, 100)
|
||||||
|
if (random <= 95) {
|
||||||
|
reboot()
|
||||||
|
} else {
|
||||||
|
""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Scaffold(
|
Scaffold(
|
||||||
topBar = {
|
topBar = {
|
||||||
@@ -96,6 +131,10 @@ fun HomeScreen(navigator: DestinationsNavigator) {
|
|||||||
.padding(horizontal = 16.dp),
|
.padding(horizontal = 16.dp),
|
||||||
verticalArrangement = Arrangement.spacedBy(16.dp)
|
verticalArrangement = Arrangement.spacedBy(16.dp)
|
||||||
) {
|
) {
|
||||||
|
if (shouldTriggerRestart) {
|
||||||
|
WarningCard(message = "zakozako")
|
||||||
|
return@Column
|
||||||
|
}
|
||||||
val isManager = Natives.becomeManager(ksuApp.packageName)
|
val isManager = Natives.becomeManager(ksuApp.packageName)
|
||||||
val ksuVersion = if (isManager) Natives.version else null
|
val ksuVersion = if (isManager) Natives.version else null
|
||||||
val lkmMode = ksuVersion?.let {
|
val lkmMode = ksuVersion?.let {
|
||||||
@@ -155,10 +194,10 @@ fun HomeScreen(navigator: DestinationsNavigator) {
|
|||||||
}
|
}
|
||||||
InfoCard()
|
InfoCard()
|
||||||
if (!isSimpleMode) {
|
if (!isSimpleMode) {
|
||||||
|
ContributionCard()
|
||||||
DonateCard()
|
DonateCard()
|
||||||
LearnMoreCard()
|
LearnMoreCard()
|
||||||
}
|
}
|
||||||
|
|
||||||
Spacer(Modifier)
|
Spacer(Modifier)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -308,6 +347,12 @@ private fun StatusCard(
|
|||||||
val isHideVersion = LocalContext.current.getSharedPreferences("settings", Context.MODE_PRIVATE)
|
val isHideVersion = LocalContext.current.getSharedPreferences("settings", Context.MODE_PRIVATE)
|
||||||
.getBoolean("is_hide_version", false)
|
.getBoolean("is_hide_version", false)
|
||||||
|
|
||||||
|
val isHideOtherInfo = LocalContext.current.getSharedPreferences("settings", Context.MODE_PRIVATE)
|
||||||
|
.getBoolean("is_hide_other_info", false)
|
||||||
|
|
||||||
|
val isHideSusfsStatus = LocalContext.current.getSharedPreferences("settings", Context.MODE_PRIVATE)
|
||||||
|
.getBoolean("is_hide_susfs_status", false)
|
||||||
|
|
||||||
Icon(Icons.Outlined.CheckCircle, stringResource(R.string.home_working))
|
Icon(Icons.Outlined.CheckCircle, stringResource(R.string.home_working))
|
||||||
Column(Modifier.padding(start = 20.dp)) {
|
Column(Modifier.padding(start = 20.dp)) {
|
||||||
Text(
|
Text(
|
||||||
@@ -321,6 +366,7 @@ private fun StatusCard(
|
|||||||
style = MaterialTheme.typography.bodyMedium
|
style = MaterialTheme.typography.bodyMedium
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
if (!isHideOtherInfo) {
|
||||||
Spacer(Modifier.height(4.dp))
|
Spacer(Modifier.height(4.dp))
|
||||||
Text(
|
Text(
|
||||||
text = stringResource(
|
text = stringResource(
|
||||||
@@ -332,9 +378,20 @@ private fun StatusCard(
|
|||||||
text = stringResource(R.string.home_module_count, getModuleCount()),
|
text = stringResource(R.string.home_module_count, getModuleCount()),
|
||||||
style = MaterialTheme.typography.bodyMedium
|
style = MaterialTheme.typography.bodyMedium
|
||||||
)
|
)
|
||||||
|
val kpmVersion = getKpmVersion()
|
||||||
|
if (kpmVersion.isNotEmpty() && !kpmVersion.startsWith("Error")) {
|
||||||
|
Spacer(Modifier.height(4.dp))
|
||||||
|
Text(
|
||||||
|
text = stringResource(R.string.home_kpm_module, getKpmModuleCount()),
|
||||||
|
style = MaterialTheme.typography.bodyMedium
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!isHideSusfsStatus) {
|
||||||
Spacer(modifier = Modifier.height(4.dp))
|
Spacer(modifier = Modifier.height(4.dp))
|
||||||
|
|
||||||
val suSFS = getSuSFS()
|
val suSFS = getSuSFS()
|
||||||
|
if (lkmMode != true) {
|
||||||
val translatedStatus = when (suSFS) {
|
val translatedStatus = when (suSFS) {
|
||||||
"Supported" -> stringResource(R.string.status_supported)
|
"Supported" -> stringResource(R.string.status_supported)
|
||||||
"Not Supported" -> stringResource(R.string.status_not_supported)
|
"Not Supported" -> stringResource(R.string.status_not_supported)
|
||||||
@@ -347,6 +404,8 @@ private fun StatusCard(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
kernelVersion.isGKI() -> {
|
kernelVersion.isGKI() -> {
|
||||||
Icon(Icons.Outlined.Warning, stringResource(R.string.home_not_installed))
|
Icon(Icons.Outlined.Warning, stringResource(R.string.home_not_installed))
|
||||||
@@ -402,6 +461,38 @@ fun WarningCard(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@Composable
|
||||||
|
fun ContributionCard() {
|
||||||
|
val uriHandler = LocalUriHandler.current
|
||||||
|
val links = listOf("https://github.com/zako", "https://github.com/udochina")
|
||||||
|
ElevatedCard(
|
||||||
|
colors = getCardColors(MaterialTheme.colorScheme.secondaryContainer),
|
||||||
|
elevation = CardDefaults.cardElevation(defaultElevation = getCardElevation())
|
||||||
|
) {
|
||||||
|
Row(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.clickable {
|
||||||
|
val randomIndex = Random.nextInt(links.size)
|
||||||
|
uriHandler.openUri(links[randomIndex])
|
||||||
|
}
|
||||||
|
.padding(24.dp),
|
||||||
|
verticalAlignment = Alignment.CenterVertically
|
||||||
|
) {
|
||||||
|
Column {
|
||||||
|
Text(
|
||||||
|
text = stringResource(R.string.home_ContributionCard_kernelsu),
|
||||||
|
style = MaterialTheme.typography.titleSmall
|
||||||
|
)
|
||||||
|
Spacer(Modifier.height(4.dp))
|
||||||
|
Text(
|
||||||
|
text = stringResource(R.string.home_click_to_ContributionCard_kernelsu),
|
||||||
|
style = MaterialTheme.typography.bodyMedium
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun LearnMoreCard() {
|
fun LearnMoreCard() {
|
||||||
@@ -478,7 +569,7 @@ private fun InfoCard() {
|
|||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.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 = 16.dp)
|
||||||
) {
|
) withContext@{
|
||||||
val contents = StringBuilder()
|
val contents = StringBuilder()
|
||||||
val uname = Os.uname()
|
val uname = Os.uname()
|
||||||
|
|
||||||
@@ -520,40 +611,53 @@ private fun InfoCard() {
|
|||||||
InfoCardItem(stringResource(R.string.home_selinux_status), getSELinuxStatus())
|
InfoCardItem(stringResource(R.string.home_selinux_status), getSELinuxStatus())
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if (!isSimpleMode) {
|
if (!isSimpleMode) {
|
||||||
|
val kpmVersion = getKpmVersion()
|
||||||
|
var displayVersion: String
|
||||||
|
val isKpmConfigured = checkKpmConfigured()
|
||||||
|
|
||||||
|
if (kpmVersion.isEmpty() || kpmVersion.startsWith("Error")) {
|
||||||
|
val statusText = if (isKpmConfigured) {
|
||||||
|
stringResource(R.string.kernel_patched)
|
||||||
|
} else {
|
||||||
|
stringResource(R.string.kernel_not_enabled)
|
||||||
|
}
|
||||||
|
displayVersion = "${stringResource(R.string.not_supported)} ($statusText)"
|
||||||
|
} else {
|
||||||
|
displayVersion = "${stringResource(R.string.supported)} ($kpmVersion)"
|
||||||
|
}
|
||||||
|
Spacer(Modifier.height(16.dp))
|
||||||
|
InfoCardItem(stringResource(R.string.home_kpm_version), displayVersion)
|
||||||
|
}
|
||||||
|
|
||||||
|
val isHideSusfsStatus = LocalContext.current.getSharedPreferences("settings", Context.MODE_PRIVATE)
|
||||||
|
.getBoolean("is_hide_susfs_status", false)
|
||||||
|
|
||||||
|
if ((!isSimpleMode) && (!isHideSusfsStatus)) {
|
||||||
Spacer(modifier = Modifier.height(16.dp))
|
Spacer(modifier = Modifier.height(16.dp))
|
||||||
|
|
||||||
val suSFS = getSuSFS()
|
val suSFS = getSuSFS()
|
||||||
if (suSFS == "Supported") {
|
if (suSFS == "Supported") {
|
||||||
InfoCardItem(
|
val suSFSVersion = getSuSFSVersion()
|
||||||
stringResource(R.string.home_susfs_version),
|
if (suSFSVersion.isEmpty()) return@withContext
|
||||||
"${getSuSFSVersion()} (${stringResource(R.string.manual_hook)})"
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
val susSUMode = try {
|
|
||||||
susfsSUS_SU_Mode()
|
|
||||||
} catch (e: Exception) {
|
|
||||||
0
|
|
||||||
}
|
|
||||||
|
|
||||||
if (susSUMode == 2 || susSUMode == 0) {
|
|
||||||
val isSUS_SU = getSuSFSFeatures() == "CONFIG_KSU_SUSFS_SUS_SU"
|
val isSUS_SU = getSuSFSFeatures() == "CONFIG_KSU_SUSFS_SUS_SU"
|
||||||
val susSUModeLabel = stringResource(R.string.sus_su_mode)
|
val infoText = buildString {
|
||||||
val susSUModeValue = susSUMode.toString()
|
append(suSFSVersion)
|
||||||
val susSUModeText = if (isSUS_SU) " $susSUModeLabel $susSUModeValue" else ""
|
append(if (isSUS_SU) " (${getSuSFSVariant()})" else " (${stringResource(R.string.manual_hook)})")
|
||||||
|
if (isSUS_SU) {
|
||||||
InfoCardItem(
|
val susSUMode = try { susfsSUS_SU_Mode().toString() } catch (_: Exception) { "" }
|
||||||
stringResource(R.string.home_susfs_version),
|
if (susSUMode.isNotEmpty()) {
|
||||||
"${getSuSFSVersion()} (${getSuSFSVariant()})$susSUModeText"
|
append(" ${stringResource(R.string.sus_su_mode)} $susSUMode")
|
||||||
)
|
|
||||||
} else {
|
|
||||||
InfoCardItem(
|
|
||||||
stringResource(R.string.home_susfs_version),
|
|
||||||
"${getSuSFSVersion()} (${stringResource(R.string.manual_hook)})"
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
InfoCardItem(
|
||||||
|
stringResource(R.string.home_susfs_version),
|
||||||
|
infoText
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -609,3 +713,23 @@ private fun getDeviceModel(context: Context): String {
|
|||||||
Build.DEVICE
|
Build.DEVICE
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun checkKpmConfigured(): Boolean {
|
||||||
|
try {
|
||||||
|
val process = Runtime.getRuntime().exec("su -c cat /proc/config.gz")
|
||||||
|
val inputStream = process.inputStream
|
||||||
|
val gzipInputStream = GZIPInputStream(inputStream)
|
||||||
|
val reader = BufferedReader(InputStreamReader(gzipInputStream))
|
||||||
|
|
||||||
|
var line: String?
|
||||||
|
while (reader.readLine().also { line = it } != null) {
|
||||||
|
if (line?.contains("CONFIG_KPM=y") == true) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
reader.close()
|
||||||
|
} catch (e: Exception) {
|
||||||
|
e.printStackTrace()
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package shirkneko.zako.sukisu.ui.screen
|
package zako.zako.zako.ui.screen
|
||||||
|
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
@@ -9,6 +9,7 @@ import androidx.activity.compose.rememberLauncherForActivityResult
|
|||||||
import androidx.activity.result.contract.ActivityResultContracts
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
import androidx.annotation.StringRes
|
import androidx.annotation.StringRes
|
||||||
import androidx.compose.foundation.LocalIndication
|
import androidx.compose.foundation.LocalIndication
|
||||||
|
import androidx.compose.foundation.clickable
|
||||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||||
import androidx.compose.foundation.layout.*
|
import androidx.compose.foundation.layout.*
|
||||||
import androidx.compose.foundation.rememberScrollState
|
import androidx.compose.foundation.rememberScrollState
|
||||||
@@ -28,22 +29,21 @@ import androidx.compose.ui.semantics.Role
|
|||||||
import androidx.compose.ui.tooling.preview.Preview
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.documentfile.provider.DocumentFile
|
import androidx.documentfile.provider.DocumentFile
|
||||||
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.ListOption
|
||||||
import com.maxkeppeler.sheets.list.models.ListSelection
|
|
||||||
import com.ramcosta.composedestinations.annotation.Destination
|
import com.ramcosta.composedestinations.annotation.Destination
|
||||||
import com.ramcosta.composedestinations.annotation.RootGraph
|
import com.ramcosta.composedestinations.annotation.RootGraph
|
||||||
import com.ramcosta.composedestinations.generated.destinations.FlashScreenDestination
|
import com.ramcosta.composedestinations.generated.destinations.FlashScreenDestination
|
||||||
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
|
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
|
||||||
import com.ramcosta.composedestinations.navigation.EmptyDestinationsNavigator
|
import com.ramcosta.composedestinations.navigation.EmptyDestinationsNavigator
|
||||||
import shirkneko.zako.sukisu.R
|
import zako.zako.zako.ui.component.DialogHandle
|
||||||
import shirkneko.zako.sukisu.ui.component.DialogHandle
|
import zako.zako.zako.ui.component.rememberConfirmDialog
|
||||||
import shirkneko.zako.sukisu.ui.component.rememberConfirmDialog
|
import zako.zako.zako.ui.component.rememberCustomDialog
|
||||||
import shirkneko.zako.sukisu.ui.component.rememberCustomDialog
|
import zako.zako.zako.ui.theme.ThemeConfig
|
||||||
import shirkneko.zako.sukisu.ui.util.*
|
import zako.zako.zako.ui.theme.getCardColors
|
||||||
import shirkneko.zako.sukisu.utils.AssetsUtil
|
import zako.zako.zako.ui.theme.getCardElevation
|
||||||
|
import zako.zako.zako.ui.util.*
|
||||||
|
import zako.zako.zako.R
|
||||||
|
import zako.zako.zako.utils.AssetsUtil
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.FileOutputStream
|
import java.io.FileOutputStream
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
@@ -197,13 +197,6 @@ fun InstallScreen(navigator: DestinationsNavigator) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun launchHorizonKernelFlash(context: Context, uri: Uri) {
|
|
||||||
val worker = HorizonKernelWorker(context)
|
|
||||||
worker.uri = uri
|
|
||||||
worker.setOnFlashCompleteListener {
|
|
||||||
}
|
|
||||||
worker.start()
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun RebootDialog(
|
private fun RebootDialog(
|
||||||
@@ -506,40 +499,78 @@ fun rememberSelectKmiDialog(onSelected: (String?) -> Unit): DialogHandle {
|
|||||||
val supportedKmi by produceState(initialValue = emptyList<String>()) {
|
val supportedKmi by produceState(initialValue = emptyList<String>()) {
|
||||||
value = getSupportedKmis()
|
value = getSupportedKmis()
|
||||||
}
|
}
|
||||||
val options = supportedKmi.map { value ->
|
val listOptions = supportedKmi.map { value ->
|
||||||
ListOption(
|
ListOption(
|
||||||
titleText = value
|
titleText = value,
|
||||||
|
subtitleText = null,
|
||||||
|
icon = null
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
var selection by remember { mutableStateOf<String?>(null) }
|
var selection: String? = null
|
||||||
Surface(
|
val cardColor = if (!ThemeConfig.useDynamicColor) {
|
||||||
color = MaterialTheme.colorScheme.secondaryContainer,
|
ThemeConfig.currentTheme.ButtonContrast
|
||||||
contentColor = MaterialTheme.colorScheme.onSecondaryContainer,
|
} else {
|
||||||
shape = MaterialTheme.shapes.medium
|
MaterialTheme.colorScheme.secondaryContainer
|
||||||
) {
|
}
|
||||||
ListDialog(
|
|
||||||
state = rememberUseCaseState(
|
AlertDialog(
|
||||||
visible = true,
|
onDismissRequest = {
|
||||||
onFinishedRequest = {
|
|
||||||
onSelected(selection)
|
|
||||||
},
|
|
||||||
onCloseRequest = {
|
|
||||||
dismiss()
|
dismiss()
|
||||||
|
},
|
||||||
|
title = {
|
||||||
|
Text(text = stringResource(R.string.select_kmi))
|
||||||
|
},
|
||||||
|
text = {
|
||||||
|
Column {
|
||||||
|
listOptions.forEachIndexed { index, option ->
|
||||||
|
Row(
|
||||||
|
modifier = Modifier
|
||||||
|
.clickable {
|
||||||
|
selection = supportedKmi[index]
|
||||||
}
|
}
|
||||||
),
|
.padding(vertical = 8.dp)
|
||||||
header = Header.Default(
|
) {
|
||||||
title = stringResource(R.string.select_kmi),
|
Column {
|
||||||
),
|
Text(text = option.titleText)
|
||||||
selection = ListSelection.Single(
|
option.subtitleText?.let {
|
||||||
showRadioButtons = true,
|
Text(
|
||||||
options = options,
|
text = it,
|
||||||
) { _, option ->
|
style = MaterialTheme.typography.bodySmall,
|
||||||
selection = option.titleText
|
color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||||
}
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
confirmButton = {
|
||||||
|
TextButton(
|
||||||
|
onClick = {
|
||||||
|
if (selection != null) {
|
||||||
|
onSelected(selection)
|
||||||
|
}
|
||||||
|
dismiss()
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
Text(text = stringResource(android.R.string.ok))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
dismissButton = {
|
||||||
|
TextButton(
|
||||||
|
onClick = {
|
||||||
|
dismiss()
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
Text(text = stringResource(android.R.string.cancel))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
containerColor = getCardColors(cardColor.copy(alpha = 0.9f)).containerColor.copy(alpha = 0.9f),
|
||||||
|
shape = MaterialTheme.shapes.medium,
|
||||||
|
tonalElevation = getCardElevation()
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package shirkneko.zako.sukisu.ui.screen
|
package zako.zako.zako.ui.screen
|
||||||
|
|
||||||
import android.app.Activity.*
|
import android.app.Activity.*
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
@@ -88,32 +88,32 @@ import com.ramcosta.composedestinations.navigation.EmptyDestinationsNavigator
|
|||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import shirkneko.zako.sukisu.Natives
|
import zako.zako.zako.Natives
|
||||||
import shirkneko.zako.sukisu.R
|
import zako.zako.zako.ui.component.ConfirmResult
|
||||||
import shirkneko.zako.sukisu.ui.component.ConfirmResult
|
import zako.zako.zako.ui.component.SearchAppBar
|
||||||
import shirkneko.zako.sukisu.ui.component.SearchAppBar
|
import zako.zako.zako.ui.component.rememberConfirmDialog
|
||||||
import shirkneko.zako.sukisu.ui.component.rememberConfirmDialog
|
import zako.zako.zako.ui.component.rememberLoadingDialog
|
||||||
import shirkneko.zako.sukisu.ui.component.rememberLoadingDialog
|
import zako.zako.zako.ui.util.DownloadListener
|
||||||
import shirkneko.zako.sukisu.ui.util.DownloadListener
|
import zako.zako.zako.ui.util.*
|
||||||
import shirkneko.zako.sukisu.ui.util.*
|
import zako.zako.zako.ui.util.download
|
||||||
import shirkneko.zako.sukisu.ui.util.download
|
import zako.zako.zako.ui.util.hasMagisk
|
||||||
import shirkneko.zako.sukisu.ui.util.hasMagisk
|
import zako.zako.zako.ui.util.reboot
|
||||||
import shirkneko.zako.sukisu.ui.util.reboot
|
import zako.zako.zako.ui.util.restoreModule
|
||||||
import shirkneko.zako.sukisu.ui.util.restoreModule
|
import zako.zako.zako.ui.util.toggleModule
|
||||||
import shirkneko.zako.sukisu.ui.util.toggleModule
|
import zako.zako.zako.ui.util.uninstallModule
|
||||||
import shirkneko.zako.sukisu.ui.util.uninstallModule
|
import zako.zako.zako.ui.webui.WebUIActivity
|
||||||
import shirkneko.zako.sukisu.ui.webui.WebUIActivity
|
|
||||||
import okhttp3.OkHttpClient
|
import okhttp3.OkHttpClient
|
||||||
import shirkneko.zako.sukisu.ui.util.ModuleModify
|
import zako.zako.zako.ui.util.ModuleModify
|
||||||
import shirkneko.zako.sukisu.ui.theme.getCardColors
|
import zako.zako.zako.ui.theme.getCardColors
|
||||||
import shirkneko.zako.sukisu.ui.theme.getCardElevation
|
import zako.zako.zako.ui.theme.getCardElevation
|
||||||
import shirkneko.zako.sukisu.ui.viewmodel.ModuleViewModel
|
import zako.zako.zako.ui.viewmodel.ModuleViewModel
|
||||||
import java.io.BufferedReader
|
import java.io.BufferedReader
|
||||||
import java.io.InputStreamReader
|
import java.io.InputStreamReader
|
||||||
import java.util.zip.ZipInputStream
|
import java.util.zip.ZipInputStream
|
||||||
import androidx.core.content.edit
|
import androidx.core.content.edit
|
||||||
import androidx.core.net.toUri
|
import androidx.core.net.toUri
|
||||||
import shirkneko.zako.sukisu.ui.theme.ThemeConfig
|
import zako.zako.zako.ui.theme.ThemeConfig
|
||||||
|
import zako.zako.zako.R
|
||||||
|
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@@ -346,7 +346,11 @@ fun ModuleScreen(navigator: DestinationsNavigator) {
|
|||||||
floatingActionButton = {
|
floatingActionButton = {
|
||||||
if (!hideInstallButton) {
|
if (!hideInstallButton) {
|
||||||
val moduleInstall = stringResource(id = R.string.module_install)
|
val moduleInstall = stringResource(id = R.string.module_install)
|
||||||
val cardColor = MaterialTheme.colorScheme.secondaryContainer
|
val cardColor = if (!ThemeConfig.useDynamicColor) {
|
||||||
|
ThemeConfig.currentTheme.ButtonContrast
|
||||||
|
} else {
|
||||||
|
MaterialTheme.colorScheme.secondaryContainer
|
||||||
|
}
|
||||||
ExtendedFloatingActionButton(
|
ExtendedFloatingActionButton(
|
||||||
onClick = {
|
onClick = {
|
||||||
selectZipLauncher.launch(
|
selectZipLauncher.launch(
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
package shirkneko.zako.sukisu.ui.screen
|
package zako.zako.zako.ui.screen
|
||||||
|
|
||||||
|
import androidx.compose.animation.AnimatedVisibility
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
@@ -23,6 +24,7 @@ import androidx.compose.foundation.background
|
|||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.clickable
|
||||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||||
import androidx.compose.foundation.isSystemInDarkTheme
|
import androidx.compose.foundation.isSystemInDarkTheme
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.shape.CircleShape
|
import androidx.compose.foundation.shape.CircleShape
|
||||||
import androidx.compose.material3.Slider
|
import androidx.compose.material3.Slider
|
||||||
import androidx.compose.material3.SliderDefaults
|
import androidx.compose.material3.SliderDefaults
|
||||||
@@ -35,21 +37,21 @@ import com.topjohnwu.superuser.Shell
|
|||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import shirkneko.zako.sukisu.R
|
import zako.zako.zako.ui.component.SwitchItem
|
||||||
import shirkneko.zako.sukisu.ui.component.SwitchItem
|
import zako.zako.zako.ui.theme.CardConfig
|
||||||
import shirkneko.zako.sukisu.ui.theme.CardConfig
|
import zako.zako.zako.ui.theme.ThemeColors
|
||||||
import shirkneko.zako.sukisu.ui.theme.ThemeColors
|
import zako.zako.zako.ui.theme.ThemeConfig
|
||||||
import shirkneko.zako.sukisu.ui.theme.ThemeConfig
|
import zako.zako.zako.ui.theme.saveCustomBackground
|
||||||
import shirkneko.zako.sukisu.ui.theme.saveCustomBackground
|
import zako.zako.zako.ui.theme.saveThemeColors
|
||||||
import shirkneko.zako.sukisu.ui.theme.saveThemeColors
|
import zako.zako.zako.ui.theme.saveThemeMode
|
||||||
import shirkneko.zako.sukisu.ui.theme.saveThemeMode
|
import zako.zako.zako.ui.theme.saveDynamicColorState
|
||||||
import shirkneko.zako.sukisu.ui.theme.saveDynamicColorState
|
import zako.zako.zako.ui.util.getSuSFS
|
||||||
import shirkneko.zako.sukisu.ui.util.getSuSFS
|
import zako.zako.zako.ui.util.getSuSFSFeatures
|
||||||
import shirkneko.zako.sukisu.ui.util.getSuSFSFeatures
|
import zako.zako.zako.ui.util.susfsSUS_SU_0
|
||||||
import shirkneko.zako.sukisu.ui.util.susfsSUS_SU_0
|
import zako.zako.zako.ui.util.susfsSUS_SU_2
|
||||||
import shirkneko.zako.sukisu.ui.util.susfsSUS_SU_2
|
import zako.zako.zako.ui.util.susfsSUS_SU_Mode
|
||||||
import shirkneko.zako.sukisu.ui.util.susfsSUS_SU_Mode
|
|
||||||
import androidx.core.content.edit
|
import androidx.core.content.edit
|
||||||
|
import zako.zako.zako.R
|
||||||
|
|
||||||
|
|
||||||
fun saveCardConfig(context: Context) {
|
fun saveCardConfig(context: Context) {
|
||||||
@@ -115,6 +117,28 @@ fun MoreSettingsScreen(navigator: DestinationsNavigator) {
|
|||||||
isHideVersion = newValue
|
isHideVersion = newValue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 隐藏模块数量等信息开关状态
|
||||||
|
var isHideOtherInfo by remember {
|
||||||
|
mutableStateOf(prefs.getBoolean("is_hide_other_info", false))
|
||||||
|
}
|
||||||
|
|
||||||
|
// 隐藏模块数量等信息开关状态
|
||||||
|
val onHideOtherInfoChange = { newValue: Boolean ->
|
||||||
|
prefs.edit { putBoolean("is_hide_other_info", newValue) }
|
||||||
|
isHideOtherInfo = newValue
|
||||||
|
}
|
||||||
|
|
||||||
|
// 隐藏 SuSFS 状态开关状态
|
||||||
|
var isHideSusfsStatus by remember {
|
||||||
|
mutableStateOf(prefs.getBoolean("is_hide_susfs_status", false))
|
||||||
|
}
|
||||||
|
|
||||||
|
// 隐藏 SuSFS 状态开关状态
|
||||||
|
val onHideSusfsStatusChange = { newValue: Boolean ->
|
||||||
|
prefs.edit { putBoolean("is_hide_susfs_status", newValue) }
|
||||||
|
isHideSusfsStatus = newValue
|
||||||
|
}
|
||||||
|
|
||||||
// SELinux 状态
|
// SELinux 状态
|
||||||
var selinuxEnabled by remember {
|
var selinuxEnabled by remember {
|
||||||
mutableStateOf(Shell.cmd("getenforce").exec().out.firstOrNull() == "Enforcing")
|
mutableStateOf(Shell.cmd("getenforce").exec().out.firstOrNull() == "Enforcing")
|
||||||
@@ -131,7 +155,7 @@ fun MoreSettingsScreen(navigator: DestinationsNavigator) {
|
|||||||
val systemIsDark = isSystemInDarkTheme()
|
val systemIsDark = isSystemInDarkTheme()
|
||||||
LaunchedEffect(Unit) {
|
LaunchedEffect(Unit) {
|
||||||
CardConfig.apply {
|
CardConfig.apply {
|
||||||
cardAlpha = prefs.getFloat("card_alpha", 0.65f)
|
cardAlpha = prefs.getFloat("card_alpha", 0.45f)
|
||||||
cardElevation = if (prefs.getBoolean("custom_background_enabled", false)) 0.dp else defaultElevation
|
cardElevation = if (prefs.getBoolean("custom_background_enabled", false)) 0.dp else defaultElevation
|
||||||
isCustomAlphaSet = prefs.getBoolean("is_custom_alpha_set", false)
|
isCustomAlphaSet = prefs.getBoolean("is_custom_alpha_set", false)
|
||||||
|
|
||||||
@@ -139,7 +163,7 @@ fun MoreSettingsScreen(navigator: DestinationsNavigator) {
|
|||||||
if (!isCustomAlphaSet) {
|
if (!isCustomAlphaSet) {
|
||||||
val isDarkMode = ThemeConfig.forceDarkMode ?: systemIsDark
|
val isDarkMode = ThemeConfig.forceDarkMode ?: systemIsDark
|
||||||
if (isDarkMode) {
|
if (isDarkMode) {
|
||||||
cardAlpha = 0.5f
|
cardAlpha = 0.35f
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -210,25 +234,71 @@ fun MoreSettingsScreen(navigator: DestinationsNavigator) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var isExpanded by remember { mutableStateOf(false) }
|
||||||
|
|
||||||
|
ListItem(
|
||||||
|
leadingContent = { Icon(Icons.Filled.AutoFixHigh, null) },
|
||||||
|
headlineContent = { Text(stringResource(R.string.custom_settings)) },
|
||||||
|
modifier = Modifier.clickable {
|
||||||
|
isExpanded = !isExpanded
|
||||||
|
}
|
||||||
|
)
|
||||||
|
AnimatedVisibility(
|
||||||
|
visible = isExpanded,
|
||||||
|
modifier = Modifier.fillMaxWidth().padding(horizontal = 24.dp)
|
||||||
|
) {
|
||||||
// 添加简洁模块开关
|
// 添加简洁模块开关
|
||||||
SwitchItem(
|
SwitchItem(
|
||||||
icon = Icons.Filled.FormatPaint,
|
icon = Icons.Filled.Brush,
|
||||||
title = stringResource(R.string.simple_mode),
|
title = stringResource(R.string.simple_mode),
|
||||||
summary = stringResource(R.string.simple_mode_summary),
|
summary = stringResource(R.string.simple_mode_summary),
|
||||||
checked = isSimpleMode
|
checked = isSimpleMode
|
||||||
) {
|
) {
|
||||||
onSimpleModeChange(it)
|
onSimpleModeChange(it)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
AnimatedVisibility(
|
||||||
|
visible = isExpanded,
|
||||||
|
modifier = Modifier.fillMaxWidth().padding(horizontal = 24.dp)
|
||||||
|
) {
|
||||||
// 隐藏内核部分版本号
|
// 隐藏内核部分版本号
|
||||||
SwitchItem(
|
SwitchItem(
|
||||||
icon = Icons.Filled.FormatPaint,
|
icon = Icons.Filled.VisibilityOff,
|
||||||
title = stringResource(R.string.hide_kernel_kernelsu_version),
|
title = stringResource(R.string.hide_kernel_kernelsu_version),
|
||||||
summary = stringResource(R.string.hide_kernel_kernelsu_version_summary),
|
summary = stringResource(R.string.hide_kernel_kernelsu_version_summary),
|
||||||
checked = isHideVersion
|
checked = isHideVersion
|
||||||
) {
|
) {
|
||||||
onHideVersionChange(it)
|
onHideVersionChange(it)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
AnimatedVisibility(
|
||||||
|
visible = isExpanded,
|
||||||
|
modifier = Modifier.fillMaxWidth().padding(horizontal = 24.dp)
|
||||||
|
) {
|
||||||
|
// 模块数量等信息
|
||||||
|
SwitchItem(
|
||||||
|
icon = Icons.Filled.VisibilityOff,
|
||||||
|
title = stringResource(R.string.hide_other_info),
|
||||||
|
summary = stringResource(R.string.hide_other_info_summary),
|
||||||
|
checked = isHideOtherInfo
|
||||||
|
) {
|
||||||
|
onHideOtherInfoChange(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
AnimatedVisibility(
|
||||||
|
visible = isExpanded,
|
||||||
|
modifier = Modifier.fillMaxWidth().padding(horizontal = 24.dp)
|
||||||
|
) {
|
||||||
|
// SuSFS 状态信息
|
||||||
|
SwitchItem(
|
||||||
|
icon = Icons.Filled.VisibilityOff,
|
||||||
|
title = stringResource(R.string.hide_susfs_status),
|
||||||
|
summary = stringResource(R.string.hide_susfs_status_summary),
|
||||||
|
checked = isHideSusfsStatus
|
||||||
|
) {
|
||||||
|
onHideSusfsStatusChange(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// region SUSFS 配置(仅在支持时显示)
|
// region SUSFS 配置(仅在支持时显示)
|
||||||
val suSFS = getSuSFS()
|
val suSFS = getSuSFS()
|
||||||
@@ -285,10 +355,12 @@ fun MoreSettingsScreen(navigator: DestinationsNavigator) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 只在未启用动态颜色时显示主题色选择
|
// 只在未启用动态颜色时显示主题色选择
|
||||||
if (!useDynamicColor) {
|
AnimatedVisibility(
|
||||||
|
visible = !useDynamicColor
|
||||||
|
) {
|
||||||
ListItem(
|
ListItem(
|
||||||
leadingContent = { Icon(Icons.Default.Palette, null) },
|
leadingContent = { Icon(Icons.Default.Palette, null) },
|
||||||
headlineContent = { Text("主题颜色") },
|
headlineContent = { Text(stringResource(R.string.theme_color)) },
|
||||||
supportingContent = {
|
supportingContent = {
|
||||||
val currentThemeName = when (ThemeConfig.currentTheme) {
|
val currentThemeName = when (ThemeConfig.currentTheme) {
|
||||||
is ThemeColors.Default -> stringResource(R.string.color_default)
|
is ThemeColors.Default -> stringResource(R.string.color_default)
|
||||||
@@ -373,10 +445,10 @@ fun MoreSettingsScreen(navigator: DestinationsNavigator) {
|
|||||||
context.saveCustomBackground(null)
|
context.saveCustomBackground(null)
|
||||||
isCustomBackgroundEnabled = false
|
isCustomBackgroundEnabled = false
|
||||||
CardConfig.cardElevation = CardConfig.defaultElevation
|
CardConfig.cardElevation = CardConfig.defaultElevation
|
||||||
CardConfig.cardAlpha = 1f
|
CardConfig.cardAlpha = 0.45f
|
||||||
CardConfig.isCustomAlphaSet = false
|
CardConfig.isCustomAlphaSet = false
|
||||||
saveCardConfig(context)
|
saveCardConfig(context)
|
||||||
cardAlpha = 0.65f
|
cardAlpha = 0.35f
|
||||||
themeMode = 0
|
themeMode = 0
|
||||||
context.saveThemeMode(null)
|
context.saveThemeMode(null)
|
||||||
CardConfig.isUserDarkModeEnabled = false
|
CardConfig.isUserDarkModeEnabled = false
|
||||||
@@ -387,9 +459,11 @@ fun MoreSettingsScreen(navigator: DestinationsNavigator) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
if (ThemeConfig.customBackgroundUri != null && showCardSettings) {
|
|
||||||
// 透明度 Slider
|
// 透明度 Slider
|
||||||
|
AnimatedVisibility(
|
||||||
|
visible = ThemeConfig.customBackgroundUri != null && showCardSettings,
|
||||||
|
modifier = Modifier.fillMaxWidth().padding(horizontal = 24.dp)
|
||||||
|
) {
|
||||||
ListItem(
|
ListItem(
|
||||||
leadingContent = { Icon(Icons.Filled.Opacity, null) },
|
leadingContent = { Icon(Icons.Filled.Opacity, null) },
|
||||||
headlineContent = { Text(stringResource(R.string.settings_card_alpha)) },
|
headlineContent = { Text(stringResource(R.string.settings_card_alpha)) },
|
||||||
@@ -419,7 +493,11 @@ fun MoreSettingsScreen(navigator: DestinationsNavigator) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
AnimatedVisibility(
|
||||||
|
visible = ThemeConfig.customBackgroundUri != null && showCardSettings,
|
||||||
|
modifier = Modifier.fillMaxWidth().padding(horizontal = 24.dp)
|
||||||
|
){
|
||||||
ListItem(
|
ListItem(
|
||||||
leadingContent = { Icon(Icons.Filled.DarkMode, null) },
|
leadingContent = { Icon(Icons.Filled.DarkMode, null) },
|
||||||
headlineContent = { Text(stringResource(R.string.theme_mode)) },
|
headlineContent = { Text(stringResource(R.string.theme_mode)) },
|
||||||
@@ -428,7 +506,7 @@ fun MoreSettingsScreen(navigator: DestinationsNavigator) {
|
|||||||
showThemeModeDialog = true
|
showThemeModeDialog = true
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
|
||||||
// 主题模式选择对话框
|
// 主题模式选择对话框
|
||||||
if (showThemeModeDialog) {
|
if (showThemeModeDialog) {
|
||||||
@@ -491,7 +569,7 @@ fun MoreSettingsScreen(navigator: DestinationsNavigator) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun getSliderColors(cardAlpha: Float, useCustomColors: Boolean = false): SliderColors {
|
private fun getSliderColors(cardAlpha: Float, useCustomColors: Boolean = false): SliderColors {
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package shirkneko.zako.sukisu.ui.screen
|
package zako.zako.zako.ui.screen
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
@@ -20,9 +20,11 @@ import androidx.compose.foundation.verticalScroll
|
|||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.automirrored.filled.Undo
|
import androidx.compose.material.icons.automirrored.filled.Undo
|
||||||
import androidx.compose.material.icons.filled.*
|
import androidx.compose.material.icons.filled.*
|
||||||
|
import androidx.compose.material3.AlertDialog
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.ListItem
|
import androidx.compose.material3.ListItem
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.ModalBottomSheet
|
import androidx.compose.material3.ModalBottomSheet
|
||||||
import androidx.compose.material3.Scaffold
|
import androidx.compose.material3.Scaffold
|
||||||
import androidx.compose.material3.SnackbarHost
|
import androidx.compose.material3.SnackbarHost
|
||||||
@@ -49,12 +51,9 @@ import androidx.compose.ui.text.style.TextAlign
|
|||||||
import androidx.compose.ui.tooling.preview.Preview
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.core.content.FileProvider
|
import androidx.core.content.FileProvider
|
||||||
import com.maxkeppeker.sheets.core.models.base.Header
|
import androidx.core.content.edit
|
||||||
import com.maxkeppeker.sheets.core.models.base.IconSource
|
import com.maxkeppeker.sheets.core.models.base.IconSource
|
||||||
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.ListOption
|
||||||
import com.maxkeppeler.sheets.list.models.ListSelection
|
|
||||||
import com.ramcosta.composedestinations.annotation.Destination
|
import com.ramcosta.composedestinations.annotation.Destination
|
||||||
import com.ramcosta.composedestinations.annotation.RootGraph
|
import com.ramcosta.composedestinations.annotation.RootGraph
|
||||||
import com.ramcosta.composedestinations.generated.destinations.AppProfileTemplateScreenDestination
|
import com.ramcosta.composedestinations.generated.destinations.AppProfileTemplateScreenDestination
|
||||||
@@ -65,24 +64,24 @@ import com.ramcosta.composedestinations.navigation.EmptyDestinationsNavigator
|
|||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import shirkneko.zako.sukisu.BuildConfig
|
import zako.zako.zako.BuildConfig
|
||||||
import shirkneko.zako.sukisu.Natives
|
import zako.zako.zako.Natives
|
||||||
import shirkneko.zako.sukisu.R
|
import zako.zako.zako.R
|
||||||
import shirkneko.zako.sukisu.ui.component.AboutDialog
|
import zako.zako.zako.ui.component.AboutDialog
|
||||||
import shirkneko.zako.sukisu.ui.component.ConfirmResult
|
import zako.zako.zako.ui.component.ConfirmResult
|
||||||
import shirkneko.zako.sukisu.ui.component.DialogHandle
|
import zako.zako.zako.ui.component.DialogHandle
|
||||||
import shirkneko.zako.sukisu.ui.component.SwitchItem
|
import zako.zako.zako.ui.component.SwitchItem
|
||||||
import shirkneko.zako.sukisu.ui.component.rememberConfirmDialog
|
import zako.zako.zako.ui.component.rememberConfirmDialog
|
||||||
import shirkneko.zako.sukisu.ui.component.rememberCustomDialog
|
import zako.zako.zako.ui.component.rememberCustomDialog
|
||||||
import shirkneko.zako.sukisu.ui.component.rememberLoadingDialog
|
import zako.zako.zako.ui.component.rememberLoadingDialog
|
||||||
import shirkneko.zako.sukisu.ui.util.LocalSnackbarHost
|
import zako.zako.zako.ui.theme.CardConfig
|
||||||
import shirkneko.zako.sukisu.ui.util.getBugreportFile
|
import zako.zako.zako.ui.theme.ThemeConfig
|
||||||
|
import zako.zako.zako.ui.theme.getCardColors
|
||||||
|
import zako.zako.zako.ui.theme.getCardElevation
|
||||||
|
import zako.zako.zako.ui.util.LocalSnackbarHost
|
||||||
|
import zako.zako.zako.ui.util.getBugreportFile
|
||||||
import java.time.LocalDateTime
|
import java.time.LocalDateTime
|
||||||
import java.time.format.DateTimeFormatter
|
import java.time.format.DateTimeFormatter
|
||||||
import androidx.compose.material.icons.filled.ExpandMore
|
|
||||||
import androidx.compose.material3.MaterialTheme
|
|
||||||
import shirkneko.zako.sukisu.ui.theme.CardConfig
|
|
||||||
import androidx.core.content.edit
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -111,7 +110,6 @@ fun SettingScreen(navigator: DestinationsNavigator) {
|
|||||||
AboutDialog(it)
|
AboutDialog(it)
|
||||||
}
|
}
|
||||||
val loadingDialog = rememberLoadingDialog()
|
val loadingDialog = rememberLoadingDialog()
|
||||||
val shrinkDialog = rememberConfirmDialog()
|
|
||||||
// endregion
|
// endregion
|
||||||
|
|
||||||
Column(
|
Column(
|
||||||
@@ -140,7 +138,6 @@ fun SettingScreen(navigator: DestinationsNavigator) {
|
|||||||
loadingDialog.hide()
|
loadingDialog.hide()
|
||||||
snackBarHost.showSnackbar(context.getString(R.string.log_saved))
|
snackBarHost.showSnackbar(context.getString(R.string.log_saved))
|
||||||
}
|
}
|
||||||
// endregion
|
|
||||||
}
|
}
|
||||||
// region 配置项列表
|
// region 配置项列表
|
||||||
// 配置文件模板入口
|
// 配置文件模板入口
|
||||||
@@ -218,12 +215,12 @@ fun SettingScreen(navigator: DestinationsNavigator) {
|
|||||||
prefs.edit { putBoolean("enable_web_debugging", it) }
|
prefs.edit { putBoolean("enable_web_debugging", it) }
|
||||||
enableWebDebugging = it
|
enableWebDebugging = it
|
||||||
}
|
}
|
||||||
// endregion
|
// 更多设置
|
||||||
val newButtonTitle = stringResource(id = R.string.more_settings)
|
val newButtonTitle = stringResource(id = R.string.more_settings)
|
||||||
ListItem(
|
ListItem(
|
||||||
leadingContent = {
|
leadingContent = {
|
||||||
Icon(
|
Icon(
|
||||||
Icons.Filled.ExpandMore,
|
Icons.Filled.Settings,
|
||||||
contentDescription = newButtonTitle
|
contentDescription = newButtonTitle
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
@@ -452,20 +449,73 @@ fun rememberUninstallDialog(onSelected: (UninstallType) -> Unit): DialogHandle {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var selection = UninstallType.NONE
|
var selection = UninstallType.NONE
|
||||||
ListDialog(state = rememberUseCaseState(visible = true, onFinishedRequest = {
|
val cardColor = if (!ThemeConfig.useDynamicColor) {
|
||||||
|
ThemeConfig.currentTheme.ButtonContrast
|
||||||
|
} else {
|
||||||
|
MaterialTheme.colorScheme.secondaryContainer
|
||||||
|
}
|
||||||
|
|
||||||
|
AlertDialog(
|
||||||
|
onDismissRequest = {
|
||||||
|
dismiss()
|
||||||
|
},
|
||||||
|
title = {
|
||||||
|
Text(text = stringResource(R.string.settings_uninstall))
|
||||||
|
},
|
||||||
|
text = {
|
||||||
|
Column {
|
||||||
|
listOptions.forEachIndexed { index, option ->
|
||||||
|
Row(
|
||||||
|
modifier = Modifier
|
||||||
|
.clickable {
|
||||||
|
selection = options[index]
|
||||||
|
}
|
||||||
|
.padding(vertical = 8.dp)
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
imageVector = options[index].icon,
|
||||||
|
contentDescription = null,
|
||||||
|
modifier = Modifier.padding(end = 8.dp)
|
||||||
|
)
|
||||||
|
Column {
|
||||||
|
Text(text = option.titleText)
|
||||||
|
option.subtitleText?.let {
|
||||||
|
Text(
|
||||||
|
text = it,
|
||||||
|
style = MaterialTheme.typography.bodySmall,
|
||||||
|
color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
confirmButton = {
|
||||||
|
androidx.compose.material3.TextButton(
|
||||||
|
onClick = {
|
||||||
if (selection != UninstallType.NONE) {
|
if (selection != UninstallType.NONE) {
|
||||||
onSelected(selection)
|
onSelected(selection)
|
||||||
}
|
}
|
||||||
}, onCloseRequest = {
|
|
||||||
dismiss()
|
dismiss()
|
||||||
}), header = Header.Default(
|
}
|
||||||
title = stringResource(R.string.settings_uninstall),
|
) {
|
||||||
), selection = ListSelection.Single(
|
Text(text = stringResource(android.R.string.ok))
|
||||||
showRadioButtons = false,
|
}
|
||||||
options = listOptions,
|
},
|
||||||
) { index, _ ->
|
dismissButton = {
|
||||||
selection = options[index]
|
androidx.compose.material3.TextButton(
|
||||||
})
|
onClick = {
|
||||||
|
dismiss()
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
Text(text = stringResource(android.R.string.cancel))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
containerColor = getCardColors(cardColor.copy(alpha = 0.9f)).containerColor.copy(alpha = 0.9f),
|
||||||
|
shape = MaterialTheme.shapes.medium,
|
||||||
|
tonalElevation = getCardElevation()
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package shirkneko.zako.sukisu.ui.screen
|
package zako.zako.zako.ui.screen
|
||||||
|
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.gestures.detectTapGestures
|
import androidx.compose.foundation.gestures.detectTapGestures
|
||||||
@@ -24,6 +24,7 @@ import androidx.compose.ui.text.font.FontWeight
|
|||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||||
|
import zako.zako.zako.R
|
||||||
import coil.compose.AsyncImage
|
import coil.compose.AsyncImage
|
||||||
import coil.request.ImageRequest
|
import coil.request.ImageRequest
|
||||||
import com.ramcosta.composedestinations.annotation.Destination
|
import com.ramcosta.composedestinations.annotation.Destination
|
||||||
@@ -31,11 +32,10 @@ import com.ramcosta.composedestinations.annotation.RootGraph
|
|||||||
import com.ramcosta.composedestinations.generated.destinations.AppProfileScreenDestination
|
import com.ramcosta.composedestinations.generated.destinations.AppProfileScreenDestination
|
||||||
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
|
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import shirkneko.zako.sukisu.Natives
|
import zako.zako.zako.Natives
|
||||||
import shirkneko.zako.sukisu.R
|
import zako.zako.zako.ui.component.SearchAppBar
|
||||||
import shirkneko.zako.sukisu.ui.component.SearchAppBar
|
import zako.zako.zako.ui.util.ModuleModify
|
||||||
import shirkneko.zako.sukisu.ui.util.ModuleModify
|
import zako.zako.zako.ui.viewmodel.SuperUserViewModel
|
||||||
import shirkneko.zako.sukisu.ui.viewmodel.SuperUserViewModel
|
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterialApi::class, ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterialApi::class, ExperimentalMaterial3Api::class)
|
||||||
@Destination<RootGraph>
|
@Destination<RootGraph>
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package shirkneko.zako.sukisu.ui.screen
|
package zako.zako.zako.ui.screen
|
||||||
|
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.clickable
|
||||||
@@ -59,8 +59,9 @@ import com.ramcosta.composedestinations.result.ResultRecipient
|
|||||||
import com.ramcosta.composedestinations.result.getOr
|
import com.ramcosta.composedestinations.result.getOr
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import shirkneko.zako.sukisu.R
|
import zako.zako.zako.R
|
||||||
import shirkneko.zako.sukisu.ui.viewmodel.TemplateViewModel
|
import zako.zako.zako.ui.theme.ThemeConfig
|
||||||
|
import zako.zako.zako.ui.viewmodel.TemplateViewModel
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author weishu
|
* @author weishu
|
||||||
@@ -77,7 +78,11 @@ fun AppProfileTemplateScreen(
|
|||||||
val viewModel = viewModel<TemplateViewModel>()
|
val viewModel = viewModel<TemplateViewModel>()
|
||||||
val scope = rememberCoroutineScope()
|
val scope = rememberCoroutineScope()
|
||||||
val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState())
|
val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState())
|
||||||
val cardColor = MaterialTheme.colorScheme.secondaryContainer
|
val cardColor = if (!ThemeConfig.useDynamicColor) {
|
||||||
|
ThemeConfig.currentTheme.ButtonContrast
|
||||||
|
} else {
|
||||||
|
MaterialTheme.colorScheme.secondaryContainer
|
||||||
|
}
|
||||||
|
|
||||||
LaunchedEffect(Unit) {
|
LaunchedEffect(Unit) {
|
||||||
if (viewModel.templateList.isEmpty()) {
|
if (viewModel.templateList.isEmpty()) {
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package shirkneko.zako.sukisu.ui.screen
|
package zako.zako.zako.ui.screen
|
||||||
|
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.activity.compose.BackHandler
|
import androidx.activity.compose.BackHandler
|
||||||
@@ -47,14 +47,14 @@ import androidx.compose.ui.text.input.KeyboardType
|
|||||||
import com.ramcosta.composedestinations.annotation.Destination
|
import com.ramcosta.composedestinations.annotation.Destination
|
||||||
import com.ramcosta.composedestinations.annotation.RootGraph
|
import com.ramcosta.composedestinations.annotation.RootGraph
|
||||||
import com.ramcosta.composedestinations.result.ResultBackNavigator
|
import com.ramcosta.composedestinations.result.ResultBackNavigator
|
||||||
import shirkneko.zako.sukisu.Natives
|
import zako.zako.zako.Natives
|
||||||
import shirkneko.zako.sukisu.R
|
import zako.zako.zako.R
|
||||||
import shirkneko.zako.sukisu.ui.component.profile.RootProfileConfig
|
import zako.zako.zako.ui.component.profile.RootProfileConfig
|
||||||
import shirkneko.zako.sukisu.ui.util.deleteAppProfileTemplate
|
import zako.zako.zako.ui.util.deleteAppProfileTemplate
|
||||||
import shirkneko.zako.sukisu.ui.util.getAppProfileTemplate
|
import zako.zako.zako.ui.util.getAppProfileTemplate
|
||||||
import shirkneko.zako.sukisu.ui.util.setAppProfileTemplate
|
import zako.zako.zako.ui.util.setAppProfileTemplate
|
||||||
import shirkneko.zako.sukisu.ui.viewmodel.TemplateViewModel
|
import zako.zako.zako.ui.viewmodel.TemplateViewModel
|
||||||
import shirkneko.zako.sukisu.ui.viewmodel.toJSON
|
import zako.zako.zako.ui.viewmodel.toJSON
|
||||||
import androidx.lifecycle.compose.dropUnlessResumed
|
import androidx.lifecycle.compose.dropUnlessResumed
|
||||||
|
|
||||||
/**
|
/**
|
||||||
556
manager/app/src/main/java/zako/zako/zako/ui/screen/kpm.kt
Normal file
556
manager/app/src/main/java/zako/zako/zako/ui/screen/kpm.kt
Normal file
@@ -0,0 +1,556 @@
|
|||||||
|
package zako.zako.zako.ui.screen
|
||||||
|
|
||||||
|
import android.app.Activity.RESULT_OK
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import android.util.Log
|
||||||
|
import androidx.activity.compose.rememberLauncherForActivityResult
|
||||||
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
|
import androidx.compose.foundation.layout.*
|
||||||
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
|
import androidx.compose.foundation.lazy.items
|
||||||
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.outlined.*
|
||||||
|
import androidx.compose.material3.*
|
||||||
|
import androidx.compose.runtime.*
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||||
|
import com.ramcosta.composedestinations.annotation.Destination
|
||||||
|
import com.ramcosta.composedestinations.annotation.RootGraph
|
||||||
|
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
|
||||||
|
import kotlinx.coroutines.delay
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import zako.zako.zako.ui.component.ConfirmResult
|
||||||
|
import zako.zako.zako.ui.component.SearchAppBar
|
||||||
|
import zako.zako.zako.ui.component.rememberConfirmDialog
|
||||||
|
import zako.zako.zako.ui.theme.getCardColors
|
||||||
|
import zako.zako.zako.ui.theme.getCardElevation
|
||||||
|
import zako.zako.zako.ui.viewmodel.KpmViewModel
|
||||||
|
import zako.zako.zako.ui.util.loadKpmModule
|
||||||
|
import zako.zako.zako.ui.util.unloadKpmModule
|
||||||
|
import java.io.File
|
||||||
|
import androidx.core.content.edit
|
||||||
|
import zako.zako.zako.ui.theme.ThemeConfig
|
||||||
|
import zako.zako.zako.ui.component.rememberCustomDialog
|
||||||
|
import zako.zako.zako.ui.component.ConfirmDialogHandle
|
||||||
|
import zako.zako.zako.R
|
||||||
|
import java.net.URLDecoder
|
||||||
|
import java.net.URLEncoder
|
||||||
|
|
||||||
|
/**
|
||||||
|
* KPM 管理界面
|
||||||
|
* 以下内核模块功能由KernelPatch开发,经过修改后加入SukiSU Ultra的内核模块功能
|
||||||
|
* 开发者:zako, Liaokong
|
||||||
|
*/
|
||||||
|
var globalModuleFileName: String = ""
|
||||||
|
|
||||||
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
|
@Destination<RootGraph>
|
||||||
|
@Composable
|
||||||
|
fun KpmScreen(
|
||||||
|
navigator: DestinationsNavigator,
|
||||||
|
viewModel: KpmViewModel = viewModel()
|
||||||
|
) {
|
||||||
|
val context = LocalContext.current
|
||||||
|
val scope = rememberCoroutineScope()
|
||||||
|
val snackBarHost = remember { SnackbarHostState() }
|
||||||
|
val confirmDialog = rememberConfirmDialog()
|
||||||
|
val cardColor = if (!ThemeConfig.useDynamicColor) {
|
||||||
|
ThemeConfig.currentTheme.ButtonContrast
|
||||||
|
} else {
|
||||||
|
MaterialTheme.colorScheme.secondaryContainer
|
||||||
|
}
|
||||||
|
|
||||||
|
val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState())
|
||||||
|
|
||||||
|
val kpmInstallSuccess = stringResource(R.string.kpm_install_success)
|
||||||
|
val kpmInstallFailed = stringResource(R.string.kpm_install_failed)
|
||||||
|
val cancel = stringResource(R.string.cancel)
|
||||||
|
val uninstall = stringResource(R.string.uninstall)
|
||||||
|
val failedToCheckModuleFile = stringResource(R.string.snackbar_failed_to_check_module_file)
|
||||||
|
val kpmUninstallSuccess = stringResource(R.string.kpm_uninstall_success)
|
||||||
|
val kpmUninstallFailed = stringResource(R.string.kpm_uninstall_failed)
|
||||||
|
val kpmInstallMode = stringResource(R.string.kpm_install_mode)
|
||||||
|
val kpmInstallModeLoad = stringResource(R.string.kpm_install_mode_load)
|
||||||
|
val kpmInstallModeEmbed = stringResource(R.string.kpm_install_mode_embed)
|
||||||
|
val kpmInstallModeDescription = stringResource(R.string.kpm_install_mode_description)
|
||||||
|
val invalidFileTypeMessage = stringResource(R.string.invalid_file_type)
|
||||||
|
val confirmTitle = stringResource(R.string.confirm_uninstall_title_with_filename)
|
||||||
|
val confirmContent = stringResource(R.string.confirm_uninstall_content, globalModuleFileName)
|
||||||
|
|
||||||
|
var tempFileForInstall by remember { mutableStateOf<File?>(null) }
|
||||||
|
val installModeDialog = rememberCustomDialog { dismiss ->
|
||||||
|
AlertDialog(
|
||||||
|
onDismissRequest = {
|
||||||
|
dismiss()
|
||||||
|
tempFileForInstall?.delete()
|
||||||
|
tempFileForInstall = null
|
||||||
|
},
|
||||||
|
title = { Text(kpmInstallMode) },
|
||||||
|
text = { Text(kpmInstallModeDescription) },
|
||||||
|
confirmButton = {
|
||||||
|
Column {
|
||||||
|
Button(
|
||||||
|
onClick = {
|
||||||
|
scope.launch {
|
||||||
|
dismiss()
|
||||||
|
tempFileForInstall?.let { tempFile ->
|
||||||
|
handleModuleInstall(
|
||||||
|
tempFile = tempFile,
|
||||||
|
isEmbed = false,
|
||||||
|
viewModel = viewModel,
|
||||||
|
snackBarHost = snackBarHost,
|
||||||
|
kpmInstallSuccess = kpmInstallSuccess,
|
||||||
|
kpmInstallFailed = kpmInstallFailed
|
||||||
|
)
|
||||||
|
}
|
||||||
|
tempFileForInstall = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
Text(kpmInstallModeLoad)
|
||||||
|
}
|
||||||
|
Spacer(modifier = Modifier.height(8.dp))
|
||||||
|
Button(
|
||||||
|
onClick = {
|
||||||
|
scope.launch {
|
||||||
|
dismiss()
|
||||||
|
tempFileForInstall?.let { tempFile ->
|
||||||
|
handleModuleInstall(
|
||||||
|
tempFile = tempFile,
|
||||||
|
isEmbed = true,
|
||||||
|
viewModel = viewModel,
|
||||||
|
snackBarHost = snackBarHost,
|
||||||
|
kpmInstallSuccess = kpmInstallSuccess,
|
||||||
|
kpmInstallFailed = kpmInstallFailed
|
||||||
|
)
|
||||||
|
}
|
||||||
|
tempFileForInstall = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
Text(kpmInstallModeEmbed)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
dismissButton = {
|
||||||
|
TextButton(
|
||||||
|
onClick = {
|
||||||
|
dismiss()
|
||||||
|
tempFileForInstall?.delete()
|
||||||
|
tempFileForInstall = null
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
Text(cancel)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
val selectPatchLauncher = rememberLauncherForActivityResult(
|
||||||
|
contract = ActivityResultContracts.StartActivityForResult()
|
||||||
|
) { result ->
|
||||||
|
if (result.resultCode != RESULT_OK) return@rememberLauncherForActivityResult
|
||||||
|
|
||||||
|
val uri = result.data?.data ?: return@rememberLauncherForActivityResult
|
||||||
|
|
||||||
|
scope.launch {
|
||||||
|
val fileName = uri.lastPathSegment ?: "unknown.kpm"
|
||||||
|
val encodedFileName = URLEncoder.encode(fileName, "UTF-8")
|
||||||
|
val tempFile = File(context.cacheDir, encodedFileName)
|
||||||
|
|
||||||
|
context.contentResolver.openInputStream(uri)?.use { input ->
|
||||||
|
tempFile.outputStream().use { output ->
|
||||||
|
input.copyTo(output)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!tempFile.name.endsWith(".kpm")) {
|
||||||
|
snackBarHost.showSnackbar(
|
||||||
|
message = invalidFileTypeMessage,
|
||||||
|
duration = SnackbarDuration.Short
|
||||||
|
)
|
||||||
|
tempFile.delete()
|
||||||
|
return@launch
|
||||||
|
}
|
||||||
|
|
||||||
|
tempFileForInstall = tempFile
|
||||||
|
installModeDialog.show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LaunchedEffect(Unit) {
|
||||||
|
while(true) {
|
||||||
|
viewModel.fetchModuleList()
|
||||||
|
delay(5000)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val sharedPreferences = context.getSharedPreferences("app_preferences", Context.MODE_PRIVATE)
|
||||||
|
var isNoticeClosed by remember { mutableStateOf(sharedPreferences.getBoolean("is_notice_closed", false)) }
|
||||||
|
|
||||||
|
Scaffold(
|
||||||
|
topBar = {
|
||||||
|
SearchAppBar(
|
||||||
|
title = { Text(stringResource(R.string.kpm_title)) },
|
||||||
|
searchText = viewModel.search,
|
||||||
|
onSearchTextChange = { viewModel.search = it },
|
||||||
|
onClearClick = { viewModel.search = "" },
|
||||||
|
scrollBehavior = scrollBehavior,
|
||||||
|
dropdownContent = {
|
||||||
|
IconButton(onClick = { viewModel.fetchModuleList() }) {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Outlined.Refresh,
|
||||||
|
contentDescription = stringResource(R.string.refresh)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
},
|
||||||
|
floatingActionButton = {
|
||||||
|
ExtendedFloatingActionButton(
|
||||||
|
onClick = {
|
||||||
|
selectPatchLauncher.launch(
|
||||||
|
Intent(Intent.ACTION_GET_CONTENT).apply {
|
||||||
|
type = "*/*"
|
||||||
|
}
|
||||||
|
)
|
||||||
|
},
|
||||||
|
icon = {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Outlined.Add,
|
||||||
|
contentDescription = stringResource(R.string.kpm_install)
|
||||||
|
)
|
||||||
|
},
|
||||||
|
text = { Text(stringResource(R.string.kpm_install)) },
|
||||||
|
containerColor = cardColor.copy(alpha = 1f),
|
||||||
|
contentColor = MaterialTheme.colorScheme.onSecondaryContainer
|
||||||
|
)
|
||||||
|
},
|
||||||
|
snackbarHost = { SnackbarHost(snackBarHost) }
|
||||||
|
) { padding ->
|
||||||
|
Column(modifier = Modifier.padding(padding)) {
|
||||||
|
if (!isNoticeClosed) {
|
||||||
|
Row(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(16.dp),
|
||||||
|
horizontalArrangement = Arrangement.SpaceBetween,
|
||||||
|
verticalAlignment = Alignment.CenterVertically
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = stringResource(R.string.kernel_module_notice),
|
||||||
|
modifier = Modifier.weight(1f),
|
||||||
|
textAlign = TextAlign.Center
|
||||||
|
)
|
||||||
|
IconButton(onClick = {
|
||||||
|
isNoticeClosed = true
|
||||||
|
sharedPreferences.edit { putBoolean("is_notice_closed", true) }
|
||||||
|
}) {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Outlined.Close,
|
||||||
|
contentDescription = stringResource(R.string.close_notice)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (viewModel.moduleList.isEmpty()) {
|
||||||
|
Box(
|
||||||
|
modifier = Modifier.fillMaxSize(),
|
||||||
|
contentAlignment = Alignment.Center
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
stringResource(R.string.kpm_empty),
|
||||||
|
textAlign = TextAlign.Center
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
LazyColumn(
|
||||||
|
modifier = Modifier.fillMaxSize(),
|
||||||
|
contentPadding = PaddingValues(16.dp),
|
||||||
|
verticalArrangement = Arrangement.spacedBy(16.dp)
|
||||||
|
) {
|
||||||
|
items(viewModel.moduleList) { module ->
|
||||||
|
KpmModuleItem(
|
||||||
|
module = module,
|
||||||
|
onUninstall = {
|
||||||
|
scope.launch {
|
||||||
|
handleModuleUninstall(
|
||||||
|
module = module,
|
||||||
|
viewModel = viewModel,
|
||||||
|
snackBarHost = snackBarHost,
|
||||||
|
kpmUninstallSuccess = kpmUninstallSuccess,
|
||||||
|
kpmUninstallFailed = kpmUninstallFailed,
|
||||||
|
failedToCheckModuleFile = failedToCheckModuleFile,
|
||||||
|
uninstall = uninstall,
|
||||||
|
cancel = cancel,
|
||||||
|
confirmDialog = confirmDialog,
|
||||||
|
confirmTitle = confirmTitle,
|
||||||
|
confirmContent = confirmContent
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onControl = {
|
||||||
|
viewModel.loadModuleDetail(module.id)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun handleModuleInstall(
|
||||||
|
tempFile: File,
|
||||||
|
isEmbed: Boolean,
|
||||||
|
viewModel: KpmViewModel,
|
||||||
|
snackBarHost: SnackbarHostState,
|
||||||
|
kpmInstallSuccess: String,
|
||||||
|
kpmInstallFailed: String
|
||||||
|
) {
|
||||||
|
val moduleId = extractModuleId(tempFile.name)
|
||||||
|
if (moduleId == null) {
|
||||||
|
Log.e("KsuCli", "Failed to extract module ID from file: ${tempFile.name}")
|
||||||
|
snackBarHost.showSnackbar(
|
||||||
|
message = kpmInstallFailed,
|
||||||
|
duration = SnackbarDuration.Short
|
||||||
|
)
|
||||||
|
tempFile.delete()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
val targetPath = "/data/adb/kpm/$moduleId.kpm"
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (isEmbed) {
|
||||||
|
Runtime.getRuntime().exec(arrayOf("su", "-c", "mkdir -p /data/adb/kpm")).waitFor()
|
||||||
|
Runtime.getRuntime().exec(arrayOf("su", "-c", "cp ${tempFile.absolutePath} $targetPath")).waitFor()
|
||||||
|
}
|
||||||
|
|
||||||
|
val loadResult = loadKpmModule(tempFile.absolutePath)
|
||||||
|
if (loadResult.startsWith("Error")) {
|
||||||
|
Log.e("KsuCli", "Failed to load KPM module: $loadResult")
|
||||||
|
snackBarHost.showSnackbar(
|
||||||
|
message = kpmInstallFailed,
|
||||||
|
duration = SnackbarDuration.Short
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
viewModel.fetchModuleList()
|
||||||
|
snackBarHost.showSnackbar(
|
||||||
|
message = kpmInstallSuccess,
|
||||||
|
duration = SnackbarDuration.Short
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e("KsuCli", "Failed to load KPM module: ${e.message}", e)
|
||||||
|
snackBarHost.showSnackbar(
|
||||||
|
message = kpmInstallFailed,
|
||||||
|
duration = SnackbarDuration.Short
|
||||||
|
)
|
||||||
|
}
|
||||||
|
tempFile.delete()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun extractModuleId(fileName: String): String? {
|
||||||
|
return try {
|
||||||
|
val decodedFileName = URLDecoder.decode(fileName, "UTF-8")
|
||||||
|
val pattern = "([^/]*?)\\.kpm$".toRegex()
|
||||||
|
val matchResult = pattern.find(decodedFileName)
|
||||||
|
matchResult?.groupValues?.get(1)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e("KsuCli", "Failed to extract module ID: ${e.message}", e)
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun handleModuleUninstall(
|
||||||
|
module: KpmViewModel.ModuleInfo,
|
||||||
|
viewModel: KpmViewModel,
|
||||||
|
snackBarHost: SnackbarHostState,
|
||||||
|
kpmUninstallSuccess: String,
|
||||||
|
kpmUninstallFailed: String,
|
||||||
|
failedToCheckModuleFile: String,
|
||||||
|
uninstall: String,
|
||||||
|
cancel: String,
|
||||||
|
confirmTitle : String,
|
||||||
|
confirmContent : String,
|
||||||
|
confirmDialog: ConfirmDialogHandle
|
||||||
|
) {
|
||||||
|
val moduleFileName = "${module.id}.kpm"
|
||||||
|
globalModuleFileName = moduleFileName
|
||||||
|
val moduleFilePath = "/data/adb/kpm/$moduleFileName"
|
||||||
|
|
||||||
|
val fileExists = try {
|
||||||
|
val result = Runtime.getRuntime().exec(arrayOf("su", "-c", "ls /data/adb/kpm/$moduleFileName")).waitFor() == 0
|
||||||
|
result
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e("KsuCli", "Failed to check module file existence: ${e.message}", e)
|
||||||
|
snackBarHost.showSnackbar(
|
||||||
|
message = failedToCheckModuleFile,
|
||||||
|
duration = SnackbarDuration.Short
|
||||||
|
)
|
||||||
|
false
|
||||||
|
}
|
||||||
|
val confirmResult = confirmDialog.awaitConfirm(
|
||||||
|
title = confirmTitle,
|
||||||
|
content = confirmContent,
|
||||||
|
confirm = uninstall,
|
||||||
|
dismiss = cancel
|
||||||
|
)
|
||||||
|
|
||||||
|
if (confirmResult == ConfirmResult.Confirmed) {
|
||||||
|
try {
|
||||||
|
val unloadResult = unloadKpmModule(module.id)
|
||||||
|
if (unloadResult.startsWith("Error")) {
|
||||||
|
Log.e("KsuCli", "Failed to unload KPM module: $unloadResult")
|
||||||
|
snackBarHost.showSnackbar(
|
||||||
|
message = kpmUninstallFailed,
|
||||||
|
duration = SnackbarDuration.Short
|
||||||
|
)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fileExists) {
|
||||||
|
Runtime.getRuntime().exec(arrayOf("su", "-c", "rm $moduleFilePath")).waitFor()
|
||||||
|
}
|
||||||
|
|
||||||
|
viewModel.fetchModuleList()
|
||||||
|
snackBarHost.showSnackbar(
|
||||||
|
message = kpmUninstallSuccess,
|
||||||
|
duration = SnackbarDuration.Short
|
||||||
|
)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e("KsuCli", "Failed to unload KPM module: ${e.message}", e)
|
||||||
|
snackBarHost.showSnackbar(
|
||||||
|
message = kpmUninstallFailed,
|
||||||
|
duration = SnackbarDuration.Short
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun KpmModuleItem(
|
||||||
|
module: KpmViewModel.ModuleInfo,
|
||||||
|
onUninstall: () -> Unit,
|
||||||
|
onControl: () -> Unit
|
||||||
|
) {
|
||||||
|
val viewModel: KpmViewModel = viewModel()
|
||||||
|
val scope = rememberCoroutineScope()
|
||||||
|
val snackBarHost = remember { SnackbarHostState() }
|
||||||
|
val successMessage = stringResource(R.string.kpm_control_success)
|
||||||
|
val failureMessage = stringResource(R.string.kpm_control_failed)
|
||||||
|
|
||||||
|
if (viewModel.showInputDialog && viewModel.selectedModuleId == module.id) {
|
||||||
|
AlertDialog(
|
||||||
|
onDismissRequest = { viewModel.hideInputDialog() },
|
||||||
|
title = { Text(stringResource(R.string.kpm_control)) },
|
||||||
|
text = {
|
||||||
|
OutlinedTextField(
|
||||||
|
value = viewModel.inputArgs,
|
||||||
|
onValueChange = { viewModel.updateInputArgs(it) },
|
||||||
|
label = { Text(stringResource(R.string.kpm_args)) },
|
||||||
|
placeholder = { Text(module.args) }
|
||||||
|
)
|
||||||
|
},
|
||||||
|
confirmButton = {
|
||||||
|
TextButton(
|
||||||
|
onClick = {
|
||||||
|
scope.launch {
|
||||||
|
val result = viewModel.executeControl()
|
||||||
|
val message = when (result) {
|
||||||
|
0 -> successMessage
|
||||||
|
else -> failureMessage
|
||||||
|
}
|
||||||
|
snackBarHost.showSnackbar(message)
|
||||||
|
onControl()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
Text(stringResource(R.string.confirm))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
dismissButton = {
|
||||||
|
TextButton(onClick = { viewModel.hideInputDialog() }) {
|
||||||
|
Text(stringResource(R.string.cancel))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
ElevatedCard(
|
||||||
|
colors = getCardColors(MaterialTheme.colorScheme.secondaryContainer),
|
||||||
|
elevation = CardDefaults.cardElevation(defaultElevation = getCardElevation())
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier.padding(16.dp)
|
||||||
|
) {
|
||||||
|
Row(
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
horizontalArrangement = Arrangement.SpaceBetween,
|
||||||
|
verticalAlignment = Alignment.CenterVertically
|
||||||
|
) {
|
||||||
|
Column(modifier = Modifier.weight(1f)) {
|
||||||
|
Text(
|
||||||
|
text = module.name,
|
||||||
|
style = MaterialTheme.typography.titleMedium
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
text = "${stringResource(R.string.kpm_version)}: ${module.version}",
|
||||||
|
style = MaterialTheme.typography.bodyMedium
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
text = "${stringResource(R.string.kpm_author)}: ${module.author}",
|
||||||
|
style = MaterialTheme.typography.bodyMedium
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
text = "${stringResource(R.string.kpm_args)}: ${module.args}",
|
||||||
|
style = MaterialTheme.typography.bodyMedium
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.height(8.dp))
|
||||||
|
|
||||||
|
Text(
|
||||||
|
text = module.description,
|
||||||
|
style = MaterialTheme.typography.bodyMedium
|
||||||
|
)
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.height(16.dp))
|
||||||
|
|
||||||
|
Row(
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
horizontalArrangement = Arrangement.spacedBy(8.dp)
|
||||||
|
) {
|
||||||
|
FilledTonalButton(
|
||||||
|
onClick = { viewModel.showInputDialog(module.id) },
|
||||||
|
enabled = module.hasAction
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Outlined.Settings,
|
||||||
|
contentDescription = null
|
||||||
|
)
|
||||||
|
Text(stringResource(R.string.kpm_control))
|
||||||
|
}
|
||||||
|
|
||||||
|
FilledTonalButton(
|
||||||
|
onClick = onUninstall
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Outlined.Delete,
|
||||||
|
contentDescription = null
|
||||||
|
)
|
||||||
|
Text(stringResource(R.string.kpm_uninstall))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package shirkneko.zako.sukisu.ui.theme
|
package zako.zako.zako.ui.theme
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import androidx.compose.foundation.isSystemInDarkTheme
|
import androidx.compose.foundation.isSystemInDarkTheme
|
||||||
@@ -15,7 +15,7 @@ import androidx.compose.ui.unit.dp
|
|||||||
object CardConfig {
|
object CardConfig {
|
||||||
val defaultElevation: Dp = 0.dp
|
val defaultElevation: Dp = 0.dp
|
||||||
|
|
||||||
var cardAlpha by mutableStateOf(1f)
|
var cardAlpha by mutableStateOf(0.45f)
|
||||||
var cardElevation by mutableStateOf(defaultElevation)
|
var cardElevation by mutableStateOf(defaultElevation)
|
||||||
var isShadowEnabled by mutableStateOf(true)
|
var isShadowEnabled by mutableStateOf(true)
|
||||||
var isCustomAlphaSet by mutableStateOf(false)
|
var isCustomAlphaSet by mutableStateOf(false)
|
||||||
@@ -38,7 +38,7 @@ object CardConfig {
|
|||||||
|
|
||||||
fun load(context: Context) {
|
fun load(context: Context) {
|
||||||
val prefs = context.getSharedPreferences("settings", Context.MODE_PRIVATE)
|
val prefs = context.getSharedPreferences("settings", Context.MODE_PRIVATE)
|
||||||
cardAlpha = prefs.getFloat("card_alpha", 1f)
|
cardAlpha = prefs.getFloat("card_alpha", 0.45f)
|
||||||
cardElevation = if (prefs.getBoolean("custom_background_enabled", false)) 0.dp else defaultElevation
|
cardElevation = if (prefs.getBoolean("custom_background_enabled", false)) 0.dp else defaultElevation
|
||||||
isCustomAlphaSet = prefs.getBoolean("is_custom_alpha_set", false)
|
isCustomAlphaSet = prefs.getBoolean("is_custom_alpha_set", false)
|
||||||
isUserDarkModeEnabled = prefs.getBoolean("is_user_dark_mode_enabled", false)
|
isUserDarkModeEnabled = prefs.getBoolean("is_user_dark_mode_enabled", false)
|
||||||
@@ -53,7 +53,7 @@ object CardConfig {
|
|||||||
|
|
||||||
fun setDarkModeDefaults() {
|
fun setDarkModeDefaults() {
|
||||||
if (!isCustomAlphaSet) {
|
if (!isCustomAlphaSet) {
|
||||||
cardAlpha = 0.5f
|
cardAlpha = 0.35f
|
||||||
cardElevation = 0.dp
|
cardElevation = 0.dp
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package shirkneko.zako.sukisu.ui.theme
|
package zako.zako.zako.ui.theme
|
||||||
|
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package shirkneko.zako.sukisu.ui.theme
|
package zako.zako.zako.ui.theme
|
||||||
|
|
||||||
import android.content.ContentResolver
|
import android.content.ContentResolver
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
@@ -43,23 +43,23 @@ object ThemeConfig {
|
|||||||
@Composable
|
@Composable
|
||||||
private fun getDarkColorScheme() = darkColorScheme(
|
private fun getDarkColorScheme() = darkColorScheme(
|
||||||
primary = ThemeConfig.currentTheme.Primary.copy(alpha = 0.8f),
|
primary = ThemeConfig.currentTheme.Primary.copy(alpha = 0.8f),
|
||||||
onPrimary = Color.White,
|
onPrimary = mixColors(ThemeConfig.currentTheme.Primary, Color.White, 0.2f),
|
||||||
primaryContainer = ThemeConfig.currentTheme.PrimaryContainer.copy(alpha = 0.15f),
|
primaryContainer = ThemeConfig.currentTheme.PrimaryContainer.copy(alpha = 0.15f),
|
||||||
onPrimaryContainer = Color.White,
|
onPrimaryContainer = mixColors(ThemeConfig.currentTheme.Primary, Color.White, 0.2f),
|
||||||
secondary = ThemeConfig.currentTheme.Secondary.copy(alpha = 0.8f),
|
secondary = ThemeConfig.currentTheme.Secondary.copy(alpha = 0.8f),
|
||||||
onSecondary = Color.White,
|
onSecondary = mixColors(ThemeConfig.currentTheme.Secondary, Color.White, 0.2f),
|
||||||
secondaryContainer = ThemeConfig.currentTheme.SecondaryContainer.copy(alpha = 0.15f),
|
secondaryContainer = ThemeConfig.currentTheme.SecondaryContainer.copy(alpha = 0.15f),
|
||||||
onSecondaryContainer = Color.White,
|
onSecondaryContainer = mixColors(ThemeConfig.currentTheme.Secondary, Color.White, 0.2f),
|
||||||
tertiary = ThemeConfig.currentTheme.Tertiary.copy(alpha = 0.8f),
|
tertiary = ThemeConfig.currentTheme.Tertiary.copy(alpha = 0.8f),
|
||||||
onTertiary = Color.White,
|
onTertiary = mixColors(ThemeConfig.currentTheme.Tertiary, Color.White, 0.2f),
|
||||||
tertiaryContainer = ThemeConfig.currentTheme.TertiaryContainer.copy(alpha = 0.15f),
|
tertiaryContainer = ThemeConfig.currentTheme.TertiaryContainer.copy(alpha = 0.15f),
|
||||||
onTertiaryContainer = Color.White,
|
onTertiaryContainer = mixColors(ThemeConfig.currentTheme.Tertiary, Color.White, 0.2f),
|
||||||
background = Color.Transparent,
|
background = Color.Transparent,
|
||||||
surface = Color.Transparent,
|
surface = Color.Transparent,
|
||||||
onBackground = Color.White.copy(alpha = 0.87f),
|
onBackground = mixColors(ThemeConfig.currentTheme.Primary, Color.White, 0.1f),
|
||||||
onSurface = Color.White.copy(alpha = 0.87f),
|
onSurface = mixColors(ThemeConfig.currentTheme.Primary, Color.White, 0.1f),
|
||||||
surfaceVariant = Color(0xFF2F2F2F),
|
surfaceVariant = Color(0xFF2F2F2F),
|
||||||
onSurfaceVariant = Color.White.copy(alpha = 0.78f),
|
onSurfaceVariant = mixColors(ThemeConfig.currentTheme.Primary, Color.White, 0.2f),
|
||||||
outline = Color.White.copy(alpha = 0.12f),
|
outline = Color.White.copy(alpha = 0.12f),
|
||||||
outlineVariant = Color.White.copy(alpha = 0.12f)
|
outlineVariant = Color.White.copy(alpha = 0.12f)
|
||||||
)
|
)
|
||||||
@@ -340,3 +340,11 @@ private fun adjustColor(color: Color): Color {
|
|||||||
}
|
}
|
||||||
return color.copy(luminance)
|
return color.copy(luminance)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun mixColors(color1: Color, color2: Color, ratio: Float): Color {
|
||||||
|
val r = (color1.red * ratio + color2.red * (1 - ratio))
|
||||||
|
val g = (color1.green * ratio + color2.green * (1 - ratio))
|
||||||
|
val b = (color1.blue * ratio + color2.blue * (1 - ratio))
|
||||||
|
val a = (color1.alpha * ratio + color2.alpha * (1 - ratio))
|
||||||
|
return Color(r, g, b, a)
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package shirkneko.zako.sukisu.ui.theme
|
package zako.zako.zako.ui.theme
|
||||||
|
|
||||||
import androidx.compose.ui.text.TextStyle
|
import androidx.compose.ui.text.TextStyle
|
||||||
import androidx.compose.ui.text.font.FontFamily
|
import androidx.compose.ui.text.font.FontFamily
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package shirkneko.zako.sukisu.ui.util
|
package zako.zako.zako.ui.util
|
||||||
|
|
||||||
import androidx.compose.material3.SnackbarHostState
|
import androidx.compose.material3.SnackbarHostState
|
||||||
import androidx.compose.runtime.compositionLocalOf
|
import androidx.compose.runtime.compositionLocalOf
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package shirkneko.zako.sukisu.ui.util
|
package zako.zako.zako.ui.util
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import android.app.DownloadManager
|
import android.app.DownloadManager
|
||||||
@@ -12,7 +12,7 @@ import android.util.Log
|
|||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.DisposableEffect
|
import androidx.compose.runtime.DisposableEffect
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import shirkneko.zako.sukisu.ui.util.module.LatestVersionInfo
|
import zako.zako.zako.ui.util.module.LatestVersionInfo
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author weishu
|
* @author weishu
|
||||||
@@ -63,7 +63,6 @@ fun download(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun checkNewVersion(): LatestVersionInfo {
|
fun checkNewVersion(): LatestVersionInfo {
|
||||||
// 改为新的 release 接口
|
|
||||||
val url = "https://api.github.com/repos/ShirkNeko/SukiSU-Ultra/releases/latest"
|
val url = "https://api.github.com/repos/ShirkNeko/SukiSU-Ultra/releases/latest"
|
||||||
val defaultValue = LatestVersionInfo()
|
val defaultValue = LatestVersionInfo()
|
||||||
return runCatching {
|
return runCatching {
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package shirkneko.zako.sukisu.ui.util;
|
package zako.zako.zako.ui.util;
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2009 The Android Open Source Project
|
* Copyright (C) 2009 The Android Open Source Project
|
||||||
*
|
*
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package shirkneko.zako.sukisu.ui.util
|
package zako.zako.zako.ui.util
|
||||||
|
|
||||||
import androidx.compose.foundation.gestures.detectTapGestures
|
import androidx.compose.foundation.gestures.detectTapGestures
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package shirkneko.zako.sukisu.ui.util
|
package zako.zako.zako.ui.util
|
||||||
|
|
||||||
import android.content.ContentResolver
|
import android.content.ContentResolver
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
@@ -16,9 +16,9 @@ import com.topjohnwu.superuser.ShellUtils
|
|||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import kotlinx.parcelize.Parcelize
|
import kotlinx.parcelize.Parcelize
|
||||||
import shirkneko.zako.sukisu.BuildConfig
|
import zako.zako.zako.BuildConfig
|
||||||
import shirkneko.zako.sukisu.Natives
|
import zako.zako.zako.Natives
|
||||||
import shirkneko.zako.sukisu.ksuApp
|
import zako.zako.zako.ksuApp
|
||||||
import org.json.JSONArray
|
import org.json.JSONArray
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
@@ -30,7 +30,7 @@ import java.io.File
|
|||||||
private const val TAG = "KsuCli"
|
private const val TAG = "KsuCli"
|
||||||
|
|
||||||
private fun getKsuDaemonPath(): String {
|
private fun getKsuDaemonPath(): String {
|
||||||
return ksuApp.applicationInfo.nativeLibraryDir + File.separator + "libzakomk.so"
|
return ksuApp.applicationInfo.nativeLibraryDir + File.separator + "libzakozako.so"
|
||||||
}
|
}
|
||||||
|
|
||||||
object KsuCli {
|
object KsuCli {
|
||||||
@@ -437,7 +437,7 @@ fun restartApp(packageName: String) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun getSuSFSDaemonPath(): String {
|
private fun getSuSFSDaemonPath(): String {
|
||||||
return ksuApp.applicationInfo.nativeLibraryDir + File.separator + "libzakomksd.so"
|
return ksuApp.applicationInfo.nativeLibraryDir + File.separator + "libzakozakozako.so"
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getSuSFS(): String {
|
fun getSuSFS(): String {
|
||||||
@@ -481,7 +481,7 @@ fun susfsSUS_SU_Mode(): String {
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getKpmmgrPath(): String {
|
fun getKpmmgrPath(): String {
|
||||||
return ksuApp.applicationInfo.nativeLibraryDir + File.separator + "libkpmmgr.so"
|
return ksuApp.applicationInfo.nativeLibraryDir + File.separator + "libkpmmgr.so"
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -489,48 +489,62 @@ private fun getKpmmgrPath(): String {
|
|||||||
fun loadKpmModule(path: String, args: String? = null): String {
|
fun loadKpmModule(path: String, args: String? = null): String {
|
||||||
val shell = getRootShell()
|
val shell = getRootShell()
|
||||||
val cmd = "${getKpmmgrPath()} load $path ${args ?: ""}"
|
val cmd = "${getKpmmgrPath()} load $path ${args ?: ""}"
|
||||||
val result = ShellUtils.fastCmd(shell, cmd)
|
return ShellUtils.fastCmd(shell, cmd)
|
||||||
return result
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun unloadKpmModule(name: String): String {
|
fun unloadKpmModule(name: String): String {
|
||||||
val shell = getRootShell()
|
val shell = getRootShell()
|
||||||
val cmd = "${getKpmmgrPath()} unload $name"
|
val cmd = "${getKpmmgrPath()} unload $name"
|
||||||
val result = ShellUtils.fastCmd(shell, cmd)
|
return ShellUtils.fastCmd(shell, cmd)
|
||||||
return result
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getKpmModuleCount(): String {
|
fun getKpmModuleCount(): Int {
|
||||||
val shell = getRootShell()
|
val shell = getRootShell()
|
||||||
val cmd = "${getKpmmgrPath()} num"
|
val cmd = "${getKpmmgrPath()} num"
|
||||||
val result = ShellUtils.fastCmd(shell, cmd)
|
val result = ShellUtils.fastCmd(shell, cmd)
|
||||||
return result
|
return result.trim().toIntOrNull() ?: 0
|
||||||
|
}
|
||||||
|
|
||||||
|
fun runCmd(shell : Shell, cmd : String) : String {
|
||||||
|
return shell.newJob()
|
||||||
|
.add(cmd)
|
||||||
|
.to(mutableListOf<String>(), null)
|
||||||
|
.exec().out
|
||||||
|
.joinToString("\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
fun listKpmModules(): String {
|
fun listKpmModules(): String {
|
||||||
val shell = getRootShell()
|
val shell = getRootShell()
|
||||||
val cmd = "${getKpmmgrPath()} list"
|
val cmd = "${getKpmmgrPath()} list"
|
||||||
val result = ShellUtils.fastCmd(shell, cmd)
|
return try {
|
||||||
return result
|
runCmd(shell, cmd).trim()
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e(TAG, "Failed to list KPM modules", e)
|
||||||
|
""
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getKpmModuleInfo(name: String): String {
|
fun getKpmModuleInfo(name: String): String {
|
||||||
val shell = getRootShell()
|
val shell = getRootShell()
|
||||||
val cmd = "${getKpmmgrPath()} info $name"
|
val cmd = "${getKpmmgrPath()} info $name"
|
||||||
val result = ShellUtils.fastCmd(shell, cmd)
|
return try {
|
||||||
return result
|
runCmd(shell, cmd).trim()
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e(TAG, "Failed to get KPM module info: $name", e)
|
||||||
|
""
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun controlKpmModule(name: String, args: String? = null): String {
|
fun controlKpmModule(name: String, args: String? = null): Int {
|
||||||
val shell = getRootShell()
|
val shell = getRootShell()
|
||||||
val cmd = "${getKpmmgrPath()} control $name ${args ?: ""}"
|
val cmd = "${getKpmmgrPath()} control $name ${args ?: ""}"
|
||||||
val result = ShellUtils.fastCmd(shell, cmd)
|
val result = runCmd(shell, cmd)
|
||||||
return result
|
return result.trim().toIntOrNull() ?: -1
|
||||||
}
|
}
|
||||||
|
|
||||||
fun printKpmModules(): String {
|
fun getKpmVersion(): String {
|
||||||
val shell = getRootShell()
|
val shell = getRootShell()
|
||||||
val cmd = "${getKpmmgrPath()} print"
|
val cmd = "${getKpmmgrPath()} version"
|
||||||
val result = ShellUtils.fastCmd(shell, cmd)
|
val result = ShellUtils.fastCmd(shell, cmd)
|
||||||
return result
|
return result.trim()
|
||||||
}
|
}
|
||||||
@@ -1,11 +1,11 @@
|
|||||||
package shirkneko.zako.sukisu.ui.util
|
package zako.zako.zako.ui.util
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.system.Os
|
import android.system.Os
|
||||||
import com.topjohnwu.superuser.ShellUtils
|
import com.topjohnwu.superuser.ShellUtils
|
||||||
import shirkneko.zako.sukisu.Natives
|
import zako.zako.zako.Natives
|
||||||
import shirkneko.zako.sukisu.ui.screen.getManagerVersion
|
import zako.zako.zako.ui.screen.getManagerVersion
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.FileWriter
|
import java.io.FileWriter
|
||||||
import java.io.PrintWriter
|
import java.io.PrintWriter
|
||||||
@@ -24,7 +24,7 @@ fun getBugreportFile(context: Context): File {
|
|||||||
val pstoreFile = File(bugreportDir, "pstore.tar.gz")
|
val pstoreFile = File(bugreportDir, "pstore.tar.gz")
|
||||||
// Xiaomi/Readmi devices have diag in /data/vendor/diag
|
// Xiaomi/Readmi devices have diag in /data/vendor/diag
|
||||||
val diagFile = File(bugreportDir, "diag.tar.gz")
|
val diagFile = File(bugreportDir, "diag.tar.gz")
|
||||||
val opulsFile = File(bugreportDir, "opuls.tar.gz")
|
val oplusFile = File(bugreportDir, "oplus.tar.gz")
|
||||||
val bootlogFile = File(bugreportDir, "bootlog.tar.gz")
|
val bootlogFile = File(bugreportDir, "bootlog.tar.gz")
|
||||||
val mountsFile = File(bugreportDir, "mounts.txt")
|
val mountsFile = File(bugreportDir, "mounts.txt")
|
||||||
val fileSystemsFile = File(bugreportDir, "filesystems.txt")
|
val fileSystemsFile = File(bugreportDir, "filesystems.txt")
|
||||||
@@ -46,7 +46,7 @@ fun getBugreportFile(context: Context): File {
|
|||||||
shell.newJob().add("tar -czf ${dropboxFile.absolutePath} -C /data/system/dropbox .").exec()
|
shell.newJob().add("tar -czf ${dropboxFile.absolutePath} -C /data/system/dropbox .").exec()
|
||||||
shell.newJob().add("tar -czf ${pstoreFile.absolutePath} -C /sys/fs/pstore .").exec()
|
shell.newJob().add("tar -czf ${pstoreFile.absolutePath} -C /sys/fs/pstore .").exec()
|
||||||
shell.newJob().add("tar -czf ${diagFile.absolutePath} -C /data/vendor/diag . --exclude=./minidump.gz").exec()
|
shell.newJob().add("tar -czf ${diagFile.absolutePath} -C /data/vendor/diag . --exclude=./minidump.gz").exec()
|
||||||
shell.newJob().add("tar -czf ${opulsFile.absolutePath} -C /mnt/oplus/op2/media/log/boot_log/ .").exec()
|
shell.newJob().add("tar -czf ${oplusFile.absolutePath} -C /mnt/oplus/op2/media/log/boot_log/ .").exec()
|
||||||
shell.newJob().add("tar -czf ${bootlogFile.absolutePath} -C /data/adb/ksu/log .").exec()
|
shell.newJob().add("tar -czf ${bootlogFile.absolutePath} -C /data/adb/ksu/log .").exec()
|
||||||
|
|
||||||
shell.newJob().add("cat /proc/1/mountinfo > ${mountsFile.absolutePath}").exec()
|
shell.newJob().add("cat /proc/1/mountinfo > ${mountsFile.absolutePath}").exec()
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package shirkneko.zako.sukisu.ui.util
|
package zako.zako.zako.ui.util
|
||||||
|
|
||||||
import android.app.AlertDialog
|
import android.app.AlertDialog
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
@@ -16,7 +16,7 @@ import kotlinx.coroutines.CompletableDeferred
|
|||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import shirkneko.zako.sukisu.R
|
import zako.zako.zako.R
|
||||||
import java.io.BufferedReader
|
import java.io.BufferedReader
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.io.InputStreamReader
|
import java.io.InputStreamReader
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
package shirkneko.zako.sukisu.ui.util
|
package zako.zako.zako.ui.util
|
||||||
|
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import com.topjohnwu.superuser.Shell
|
import com.topjohnwu.superuser.Shell
|
||||||
import shirkneko.zako.sukisu.R
|
import zako.zako.zako.R
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun getSELinuxStatus(): String {
|
fun getSELinuxStatus(): String {
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package shirkneko.zako.sukisu.ui.util.module
|
package zako.zako.zako.ui.util.module
|
||||||
|
|
||||||
data class LatestVersionInfo(
|
data class LatestVersionInfo(
|
||||||
val versionCode : Int = 0,
|
val versionCode : Int = 0,
|
||||||
@@ -0,0 +1,167 @@
|
|||||||
|
package zako.zako.zako.ui.viewmodel
|
||||||
|
|
||||||
|
import android.util.Log
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.setValue
|
||||||
|
import androidx.lifecycle.ViewModel
|
||||||
|
import androidx.lifecycle.viewModelScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
|
import zako.zako.zako.ui.util.*
|
||||||
|
|
||||||
|
class KpmViewModel : ViewModel() {
|
||||||
|
var moduleList by mutableStateOf(emptyList<ModuleInfo>())
|
||||||
|
private set
|
||||||
|
|
||||||
|
var search by mutableStateOf("")
|
||||||
|
internal set
|
||||||
|
|
||||||
|
var isRefreshing by mutableStateOf(false)
|
||||||
|
private set
|
||||||
|
|
||||||
|
var currentModuleDetail by mutableStateOf("")
|
||||||
|
private set
|
||||||
|
|
||||||
|
fun fetchModuleList() {
|
||||||
|
viewModelScope.launch {
|
||||||
|
isRefreshing = true
|
||||||
|
try {
|
||||||
|
val moduleCount = getKpmModuleCount()
|
||||||
|
Log.d("KsuCli", "Module count: $moduleCount")
|
||||||
|
|
||||||
|
moduleList = getAllKpmModuleInfo()
|
||||||
|
|
||||||
|
// 获取 KPM 版本信息
|
||||||
|
val kpmVersion = getKpmVersion()
|
||||||
|
Log.d("KsuCli", "KPM Version: $kpmVersion")
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e("KsuCli", "获取模块列表失败", e)
|
||||||
|
} finally {
|
||||||
|
isRefreshing = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getAllKpmModuleInfo(): List<ModuleInfo> {
|
||||||
|
val result = mutableListOf<ModuleInfo>()
|
||||||
|
try {
|
||||||
|
val str = listKpmModules()
|
||||||
|
val moduleNames = str
|
||||||
|
.split("\n")
|
||||||
|
.filter { it.isNotBlank() }
|
||||||
|
|
||||||
|
for (name in moduleNames) {
|
||||||
|
try {
|
||||||
|
val moduleInfo = parseModuleInfo(name)
|
||||||
|
moduleInfo?.let { result.add(it) }
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e("KsuCli", "Error processing module $name", e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e("KsuCli", "Failed to get module list", e)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun parseModuleInfo(name: String): ModuleInfo? {
|
||||||
|
val info = getKpmModuleInfo(name)
|
||||||
|
if (info.isBlank()) return null
|
||||||
|
|
||||||
|
val properties = info.lineSequence()
|
||||||
|
.filter { line ->
|
||||||
|
val trimmed = line.trim()
|
||||||
|
trimmed.isNotEmpty() && !trimmed.startsWith("#")
|
||||||
|
}
|
||||||
|
.mapNotNull { line ->
|
||||||
|
line.split("=", limit = 2).let { parts ->
|
||||||
|
when (parts.size) {
|
||||||
|
2 -> parts[0].trim() to parts[1].trim()
|
||||||
|
1 -> parts[0].trim() to ""
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.toMap()
|
||||||
|
|
||||||
|
return ModuleInfo(
|
||||||
|
id = name,
|
||||||
|
name = properties["name"] ?: name,
|
||||||
|
version = properties["version"] ?: "",
|
||||||
|
author = properties["author"] ?: "",
|
||||||
|
description = properties["description"] ?: "",
|
||||||
|
args = properties["args"] ?: "",
|
||||||
|
enabled = true,
|
||||||
|
hasAction = true
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun loadModuleDetail(moduleId: String) {
|
||||||
|
viewModelScope.launch {
|
||||||
|
try {
|
||||||
|
currentModuleDetail = withContext(Dispatchers.IO) {
|
||||||
|
getKpmModuleInfo(moduleId)
|
||||||
|
}
|
||||||
|
Log.d("KsuCli", "Module detail loaded: $currentModuleDetail")
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e("KsuCli", "Failed to load module detail", e)
|
||||||
|
currentModuleDetail = "Error: ${e.message}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var showInputDialog by mutableStateOf(false)
|
||||||
|
private set
|
||||||
|
|
||||||
|
var selectedModuleId by mutableStateOf<String?>(null)
|
||||||
|
private set
|
||||||
|
|
||||||
|
var inputArgs by mutableStateOf("")
|
||||||
|
private set
|
||||||
|
|
||||||
|
fun showInputDialog(moduleId: String) {
|
||||||
|
selectedModuleId = moduleId
|
||||||
|
showInputDialog = true
|
||||||
|
}
|
||||||
|
|
||||||
|
fun hideInputDialog() {
|
||||||
|
showInputDialog = false
|
||||||
|
selectedModuleId = null
|
||||||
|
inputArgs = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
fun updateInputArgs(args: String) {
|
||||||
|
inputArgs = args
|
||||||
|
}
|
||||||
|
|
||||||
|
fun executeControl(): Int {
|
||||||
|
val moduleId = selectedModuleId ?: return -1
|
||||||
|
val result = controlKpmModule(moduleId, inputArgs)
|
||||||
|
hideInputDialog()
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
fun controlModule(moduleId: String, args: String? = null): Int {
|
||||||
|
return try {
|
||||||
|
val result = controlKpmModule(moduleId, args)
|
||||||
|
Log.d("KsuCli", "Control module $moduleId result: $result")
|
||||||
|
result
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e("KsuCli", "Failed to control module $moduleId", e)
|
||||||
|
-1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data class ModuleInfo(
|
||||||
|
val id: String,
|
||||||
|
val name: String,
|
||||||
|
val version: String,
|
||||||
|
val author: String,
|
||||||
|
val description: String,
|
||||||
|
val args: String,
|
||||||
|
val enabled: Boolean,
|
||||||
|
val hasAction: Boolean
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package shirkneko.zako.sukisu.ui.viewmodel
|
package zako.zako.zako.ui.viewmodel
|
||||||
|
|
||||||
import android.os.SystemClock
|
import android.os.SystemClock
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
@@ -10,8 +10,8 @@ import androidx.lifecycle.ViewModel
|
|||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import shirkneko.zako.sukisu.ui.util.HanziToPinyin
|
import zako.zako.zako.ui.util.HanziToPinyin
|
||||||
import shirkneko.zako.sukisu.ui.util.listModules
|
import zako.zako.zako.ui.util.listModules
|
||||||
import org.json.JSONArray
|
import org.json.JSONArray
|
||||||
import org.json.JSONObject
|
import org.json.JSONObject
|
||||||
import java.text.Collator
|
import java.text.Collator
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package shirkneko.zako.sukisu.ui.viewmodel
|
package zako.zako.zako.ui.viewmodel
|
||||||
|
|
||||||
import android.content.ComponentName
|
import android.content.ComponentName
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
@@ -18,12 +18,12 @@ import com.topjohnwu.superuser.Shell
|
|||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import kotlinx.parcelize.Parcelize
|
import kotlinx.parcelize.Parcelize
|
||||||
import shirkneko.zako.sukisu.IKsuInterface
|
import zako.zako.zako.IKsuInterface
|
||||||
import shirkneko.zako.sukisu.Natives
|
import zako.zako.zako.Natives
|
||||||
import shirkneko.zako.sukisu.ksuApp
|
import zako.zako.zako.ksuApp
|
||||||
import shirkneko.zako.sukisu.ui.KsuService
|
import zako.zako.zako.ui.KsuService
|
||||||
import shirkneko.zako.sukisu.ui.util.HanziToPinyin
|
import zako.zako.zako.ui.util.HanziToPinyin
|
||||||
import shirkneko.zako.sukisu.ui.util.KsuCli
|
import zako.zako.zako.ui.util.KsuCli
|
||||||
import java.text.Collator
|
import java.text.Collator
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import kotlin.coroutines.resume
|
import kotlin.coroutines.resume
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package shirkneko.zako.sukisu.ui.viewmodel
|
package zako.zako.zako.ui.viewmodel
|
||||||
|
|
||||||
import android.os.Parcelable
|
import android.os.Parcelable
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
@@ -10,12 +10,12 @@ import androidx.lifecycle.ViewModel
|
|||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import kotlinx.parcelize.Parcelize
|
import kotlinx.parcelize.Parcelize
|
||||||
import shirkneko.zako.sukisu.Natives
|
import zako.zako.zako.Natives
|
||||||
import shirkneko.zako.sukisu.profile.Capabilities
|
import zako.zako.zako.profile.Capabilities
|
||||||
import shirkneko.zako.sukisu.profile.Groups
|
import zako.zako.zako.profile.Groups
|
||||||
import shirkneko.zako.sukisu.ui.util.getAppProfileTemplate
|
import zako.zako.zako.ui.util.getAppProfileTemplate
|
||||||
import shirkneko.zako.sukisu.ui.util.listAppProfileTemplates
|
import zako.zako.zako.ui.util.listAppProfileTemplates
|
||||||
import shirkneko.zako.sukisu.ui.util.setAppProfileTemplate
|
import zako.zako.zako.ui.util.setAppProfileTemplate
|
||||||
import okhttp3.OkHttpClient
|
import okhttp3.OkHttpClient
|
||||||
import okhttp3.Request
|
import okhttp3.Request
|
||||||
import org.json.JSONArray
|
import org.json.JSONArray
|
||||||
@@ -14,7 +14,7 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package shirkneko.zako.sukisu.ui.webui;
|
package zako.zako.zako.ui.webui;
|
||||||
|
|
||||||
import java.net.URLConnection;
|
import java.net.URLConnection;
|
||||||
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package shirkneko.zako.sukisu.ui.webui;
|
package zako.zako.zako.ui.webui;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package shirkneko.zako.sukisu.ui.webui
|
package zako.zako.zako.ui.webui
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import android.app.ActivityManager
|
import android.app.ActivityManager
|
||||||
@@ -16,7 +16,7 @@ import androidx.core.view.WindowInsetsCompat
|
|||||||
import androidx.core.view.updateLayoutParams
|
import androidx.core.view.updateLayoutParams
|
||||||
import androidx.webkit.WebViewAssetLoader
|
import androidx.webkit.WebViewAssetLoader
|
||||||
import com.topjohnwu.superuser.Shell
|
import com.topjohnwu.superuser.Shell
|
||||||
import shirkneko.zako.sukisu.ui.util.createRootShell
|
import zako.zako.zako.ui.util.createRootShell
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
@SuppressLint("SetJavaScriptEnabled")
|
@SuppressLint("SetJavaScriptEnabled")
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package shirkneko.zako.sukisu.ui.webui
|
package zako.zako.zako.ui.webui
|
||||||
|
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
@@ -14,11 +14,13 @@ import androidx.core.view.WindowInsetsControllerCompat
|
|||||||
import com.topjohnwu.superuser.CallbackList
|
import com.topjohnwu.superuser.CallbackList
|
||||||
import com.topjohnwu.superuser.ShellUtils
|
import com.topjohnwu.superuser.ShellUtils
|
||||||
import com.topjohnwu.superuser.internal.UiThreadHandler
|
import com.topjohnwu.superuser.internal.UiThreadHandler
|
||||||
import shirkneko.zako.sukisu.ui.util.createRootShell
|
import zako.zako.zako.ui.util.createRootShell
|
||||||
import shirkneko.zako.sukisu.ui.util.listModules
|
import zako.zako.zako.ui.util.listModules
|
||||||
import shirkneko.zako.sukisu.ui.util.withNewRootShell
|
import zako.zako.zako.ui.util.withNewRootShell
|
||||||
import org.json.JSONArray
|
import org.json.JSONArray
|
||||||
import org.json.JSONObject
|
import org.json.JSONObject
|
||||||
|
import zako.zako.zako.ui.util.controlKpmModule
|
||||||
|
import zako.zako.zako.ui.util.listKpmModules
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.util.concurrent.CompletableFuture
|
import java.util.concurrent.CompletableFuture
|
||||||
|
|
||||||
@@ -197,6 +199,18 @@ class WebViewInterface(
|
|||||||
}
|
}
|
||||||
return currentModuleInfo.toString()
|
return currentModuleInfo.toString()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// =================== KPM支持 =============================
|
||||||
|
|
||||||
|
@JavascriptInterface
|
||||||
|
fun listAllKpm() : String {
|
||||||
|
return listKpmModules()
|
||||||
|
}
|
||||||
|
|
||||||
|
@JavascriptInterface
|
||||||
|
fun controlKpm(name: String, args: String) : Int {
|
||||||
|
return controlKpmModule(name, args)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun hideSystemUI(window: Window) =
|
fun hideSystemUI(window: Window) =
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package shirkneko.zako.sukisu.utils
|
package zako.zako.zako.utils
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import java.io.File
|
import java.io.File
|
||||||
4
manager/app/src/main/jniLibs/.gitignore
vendored
4
manager/app/src/main/jniLibs/.gitignore
vendored
@@ -1,3 +1,3 @@
|
|||||||
libzakomk.so
|
libzakozako.so
|
||||||
libzakomksd.so
|
libzakozakozako.so
|
||||||
libkpmmgr.so
|
libkpmmgr.so
|
||||||
@@ -8,28 +8,34 @@
|
|||||||
<string name="home_superuser_count">スーパーユーザー: %d</string>
|
<string name="home_superuser_count">スーパーユーザー: %d</string>
|
||||||
<string name="home_module_count">モジュール: %d</string>
|
<string name="home_module_count">モジュール: %d</string>
|
||||||
<string name="home_unsupported">非対応</string>
|
<string name="home_unsupported">非対応</string>
|
||||||
<string name="home_unsupported_reason">現在、 KernelSU は GKI カーネルにのみ対応しています</string>
|
<string name="home_unsupported_reason">カーネルの KernelSU ドライバが未検出です。カーネルが間違ってませんか?</string>
|
||||||
<string name="home_kernel">カーネル</string>
|
<string name="home_kernel">カーネルのバージョン</string>
|
||||||
<string name="home_manager_version">アプリのバージョン</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_manager_version">マネージャーのバージョン</string>
|
||||||
<string name="home_fingerprint">Fingerprint</string>
|
<string name="home_fingerprint">Fingerprint</string>
|
||||||
<string name="home_selinux_status">SELinux の状態</string>
|
<string name="home_selinux_status">SELinux のステータス</string>
|
||||||
<string name="selinux_status_disabled">Disabled</string>
|
<string name="selinux_status_disabled">無効</string>
|
||||||
<string name="selinux_status_enforcing">Enforcing</string>
|
<string name="selinux_status_enforcing">Enforcing</string>
|
||||||
<string name="selinux_status_permissive">Permissive</string>
|
<string name="selinux_status_permissive">Permissive</string>
|
||||||
<string name="selinux_status_unknown">不明</string>
|
<string name="selinux_status_unknown">不明</string>
|
||||||
<string name="superuser">スーパーユーザー</string>
|
<string name="superuser">スーパーユーザー</string>
|
||||||
<string name="module_failed_to_enable">%s モジュールをオンにできませんでした</string>
|
<string name="module_failed_to_enable">%s モジュールを ON にできませんでした</string>
|
||||||
<string name="module_failed_to_disable">%s モジュールをオフにできませんでした</string>
|
<string name="module_failed_to_disable">%s モジュールを OFF にできませんでした</string>
|
||||||
<string name="module_empty">モジュールがインストールされていません</string>
|
<string name="module_empty">モジュールがインストールされていません</string>
|
||||||
<string name="module">モジュール</string>
|
<string name="module">モジュール</string>
|
||||||
|
<string name="module_sort_action_first">並べ替え (アクション優先)</string>
|
||||||
|
<string name="module_sort_enabled_first">並べ替え (最初に有効)</string>
|
||||||
<string name="uninstall">アンインストール</string>
|
<string name="uninstall">アンインストール</string>
|
||||||
|
<string name="restore">復元</string>
|
||||||
<string name="module_install">インストール</string>
|
<string name="module_install">インストール</string>
|
||||||
<string name="install">インストール</string>
|
<string name="install">インストール</string>
|
||||||
<string name="reboot">再起動</string>
|
<string name="reboot">再起動</string>
|
||||||
<string name="settings">設定</string>
|
<string name="settings">設定</string>
|
||||||
<string name="reboot_userspace">通常の再起動</string>
|
<string name="reboot_userspace">ソフトリブート</string>
|
||||||
<string name="reboot_recovery">リカバリーへ再起動</string>
|
<string name="reboot_recovery">リカバリーへ再起動</string>
|
||||||
<string name="reboot_bootloader">ブートローダー へ再起動</string>
|
<string name="reboot_bootloader">ブートローダーへ再起動</string>
|
||||||
<string name="reboot_download">ダウンロードモードへ再起動</string>
|
<string name="reboot_download">ダウンロードモードへ再起動</string>
|
||||||
<string name="reboot_edl">EDL へ再起動</string>
|
<string name="reboot_edl">EDL へ再起動</string>
|
||||||
<string name="about">アプリについて</string>
|
<string name="about">アプリについて</string>
|
||||||
@@ -37,21 +43,21 @@
|
|||||||
<string name="module_uninstall_success">%s はアンインストールされました</string>
|
<string name="module_uninstall_success">%s はアンインストールされました</string>
|
||||||
<string name="module_uninstall_failed">%s をアンインストールできませんでした</string>
|
<string name="module_uninstall_failed">%s をアンインストールできませんでした</string>
|
||||||
<string name="module_version">バージョン</string>
|
<string name="module_version">バージョン</string>
|
||||||
<string name="module_author">制作者</string>
|
<string name="module_author">作者</string>
|
||||||
<string name="refresh">更新</string>
|
<string name="refresh">更新</string>
|
||||||
<string name="show_system_apps">システムアプリを表示</string>
|
<string name="show_system_apps">システムアプリを表示</string>
|
||||||
<string name="hide_system_apps">システムアプリを非表示</string>
|
<string name="hide_system_apps">システムアプリを非表示</string>
|
||||||
<string name="send_log">ログを送信</string>
|
<string name="send_log">ログを送信する</string>
|
||||||
<string name="safe_mode">セーフモード</string>
|
<string name="safe_mode">セーフモード</string>
|
||||||
<string name="reboot_to_apply">再起動すると有効化されます</string>
|
<string name="reboot_to_apply">再起動すると有効化されます</string>
|
||||||
<string name="module_magisk_conflict">モジュールが Magisk との競合により利用できません!</string>
|
<string name="module_magisk_conflict">モジュールが Magisk との競合により利用できません!</string>
|
||||||
<string name="home_learn_kernelsu">KernelSU について</string>
|
<string name="home_learn_kernelsu">KernelSU について学ぶ</string>
|
||||||
<string name="home_learn_kernelsu_url">https://kernelsu.org/ja_JP/guide/what-is-kernelsu.html</string>
|
<string name="home_learn_kernelsu_url">https://kernelsu.org/ja_JP/guide/what-is-kernelsu.html</string>
|
||||||
<string name="home_click_to_learn_kernelsu">KernelSU のインストール方法やモジュールの使い方はこちら</string>
|
<string name="home_click_to_learn_kernelsu">KernelSU のインストール方法やモジュールの使い方を学習できます。</string>
|
||||||
<string name="home_support_title">支援する</string>
|
<string name="home_support_title">支援する</string>
|
||||||
<string name="home_support_content">KernelSU はこれからもずっと無料でオープンソースです。寄付をして頂くことで、開発を支援していただけます。</string>
|
<string name="home_support_content">KernelSU は今後も無料でオープンソースです。ですが、寄付をして頂けると開発者への貢献になります。</string>
|
||||||
<string name="profile">アプリのプロファイル</string>
|
<string name="about_source_code"><![CDATA[ソースコードは %1$s で確認できます。<br/>%2$s チャンネルにご参加ください。]]></string>
|
||||||
<string name="profile_default">既定</string>
|
<string name="profile_default">デフォルト</string>
|
||||||
<string name="profile_template">テンプレート</string>
|
<string name="profile_template">テンプレート</string>
|
||||||
<string name="profile_custom">カスタム</string>
|
<string name="profile_custom">カスタム</string>
|
||||||
<string name="profile_name">プロファイル名</string>
|
<string name="profile_name">プロファイル名</string>
|
||||||
@@ -59,76 +65,205 @@
|
|||||||
<string name="profile_namespace_inherited">継承</string>
|
<string name="profile_namespace_inherited">継承</string>
|
||||||
<string name="profile_namespace_global">共通</string>
|
<string name="profile_namespace_global">共通</string>
|
||||||
<string name="profile_namespace_individual">分離</string>
|
<string name="profile_namespace_individual">分離</string>
|
||||||
<string name="profile_umount_modules">モジュールのアンマウント</string>
|
|
||||||
<string name="profile_groups">グループ</string>
|
<string name="profile_groups">グループ</string>
|
||||||
|
<string name="profile_capabilities">ケーパビリティ</string>
|
||||||
<string name="profile_selinux_context">SELinux コンテキスト</string>
|
<string name="profile_selinux_context">SELinux コンテキスト</string>
|
||||||
|
<string name="profile_umount_modules">モジュールのアンマウント</string>
|
||||||
<string name="failed_to_update_app_profile">%s のアプリのプロファイルの更新をできませでした</string>
|
<string name="failed_to_update_app_profile">%s のアプリのプロファイルの更新をできませでした</string>
|
||||||
|
<string name="require_kernel_version" formatted="false">現在の KernelSU のバージョン %d は低すぎるため、マネージャーは正常に動作しません。バージョン %d 以上に更新してください!</string>
|
||||||
|
<string name="settings_umount_modules_default">デフォルトでモジュールのマウントを解除する</string>
|
||||||
|
<string name="settings_umount_modules_default_summary">アプリプロファイルの「モジュールのアンマウント」の共通のデフォルト値です。 有効にすると、プロファイルセットを持たないアプリのシステムに対するすべてのモジュールの変更が削除されます。</string>
|
||||||
|
<string name="settings_susfs_toggle">Kprobe フックを非表示にする</string>
|
||||||
|
<string name="settings_susfs_toggle_summary">KSU によって生成された Kprobe フックを無効化して、代替となる組み込みの非 Kprobe を有効化します。Kprobe をサポートしない 非 GKI カーネルに適用される同等の機能を実装します。</string>
|
||||||
|
<string name="profile_umount_modules_summary">このオプションを有効にすると、KernelSU はこのアプリのモジュールによって変更されたファイルを復元できるようになります。</string>
|
||||||
<string name="profile_selinux_domain">ドメイン</string>
|
<string name="profile_selinux_domain">ドメイン</string>
|
||||||
<string name="profile_selinux_rules">ルール</string>
|
<string name="profile_selinux_rules">ルール</string>
|
||||||
<string name="new_version_available">新しいバージョン %s が利用可能です。タップしてダウンロード。</string>
|
<string name="module_update">更新</string>
|
||||||
<string name="module_update">アップデート</string>
|
<string name="module_downloading">モジュールをダウンロード中: %s</string>
|
||||||
<string name="module_start_downloading">ダウンロードを開始: %s</string>
|
<string name="module_start_downloading">ダウンロードを開始: %s</string>
|
||||||
|
<string name="new_version_available">新しいバージョン %s が利用可能です。タップしてダウンロード。</string>
|
||||||
<string name="launch_app">起動</string>
|
<string name="launch_app">起動</string>
|
||||||
<string name="force_stop_app" formatted="false">強制停止</string>
|
<string name="force_stop_app" formatted="false">強制停止</string>
|
||||||
<string name="restart_app">再起動</string>
|
<string name="restart_app">再起動</string>
|
||||||
<string name="failed_to_update_sepolicy">SELinux ルールの更新に失敗しました %s</string>
|
<string name="failed_to_update_sepolicy">SELinux ルールの更新に失敗しました %s</string>
|
||||||
<string name="profile_capabilities">ケーパビリティ</string>
|
|
||||||
<string name="module_downloading">モジュールをダウンロード中: %s</string>
|
|
||||||
<string name="profile_umount_modules_summary">このオプションを有効にすると、KernelSU はこのアプリのモジュールによって変更されたファイルを復元できるようになります。</string>
|
|
||||||
<string name="settings_umount_modules_default">既定でモジュールのマウントを解除</string>
|
|
||||||
<string name="settings_umount_modules_default_summary">アプリプロファイルの「モジュールのアンマウント」の共通のデフォルト値です。 有効にすると、プロファイルセットを持たないアプリのシステムに対するすべてのモジュールの変更が削除されます。</string>
|
|
||||||
<string name="module_changelog">変更履歴</string>
|
<string name="module_changelog">変更履歴</string>
|
||||||
<string name="app_profile_template_import_success">インポート成功</string>
|
<string name="settings_profile_template">アプリプロファイルのテンプレート</string>
|
||||||
<string name="app_profile_export_to_clipboard">クリップボードからエクスポート</string>
|
<string name="settings_profile_template_summary">アプリプロファイルのローカルおよびオンラインテンプレートを管理します。</string>
|
||||||
<string name="app_profile_template_export_empty">エクスポートするローカル テンプレートが見つかりません!</string>
|
|
||||||
<string name="app_profile_template_id_exist">テンプレート ID はすでに存在します!</string>
|
|
||||||
<string name="app_profile_import_from_clipboard">クリップボードからインポート</string>
|
|
||||||
<string name="module_changelog_failed">変更ログの取得に失敗しました: %s</string>
|
|
||||||
<string name="app_profile_template_name">名前</string>
|
|
||||||
<string name="app_profile_template_id_invalid">無効なテンプレート ID</string>
|
|
||||||
<string name="app_profile_template_sync">オンラインテンプレートの同期</string>
|
|
||||||
<string name="app_profile_template_create">テンプレートの作成</string>
|
<string name="app_profile_template_create">テンプレートの作成</string>
|
||||||
<string name="app_profile_template_readonly">読み取り専用</string>
|
|
||||||
<string name="app_profile_import_export">インポート/エクスポート</string>
|
|
||||||
<string name="app_profile_template_save_failed">テンプレートの保存に失敗しました</string>
|
|
||||||
<string name="app_profile_template_edit">テンプレートの編集</string>
|
<string name="app_profile_template_edit">テンプレートの編集</string>
|
||||||
<string name="app_profile_template_id">ID</string>
|
<string name="app_profile_template_id">ID</string>
|
||||||
<string name="settings_profile_template">アプリプロファイルのテンプレート</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_description">説明</string>
|
||||||
<string name="app_profile_template_save">保存</string>
|
<string name="app_profile_template_save">保存</string>
|
||||||
<string name="settings_profile_template_summary">アプリプロファイルのローカルおよびオンラインテンプレートを管理する</string>
|
|
||||||
<string name="app_profile_template_delete">消去</string>
|
<string name="app_profile_template_delete">消去</string>
|
||||||
<string name="app_profile_template_import_empty">クリップボードが空です!</string>
|
|
||||||
<string name="app_profile_template_view">テンプレートを表示</string>
|
<string name="app_profile_template_view">テンプレートを表示</string>
|
||||||
<string name="settings_check_update">アップデートを確認</string>
|
<string name="app_profile_template_readonly">読み取り専用</string>
|
||||||
<string name="settings_check_update_summary">アプリを開いたときにアップデートを自動的に確認する</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>
|
||||||
|
<string name="app_profile_template_export_empty">エクスポートするローカル テンプレートが見つかりません!</string>
|
||||||
|
<string name="app_profile_template_import_success">インポートが成功しました</string>
|
||||||
|
<string name="app_profile_template_sync">オンラインテンプレートの同期</string>
|
||||||
|
<string name="app_profile_template_save_failed">テンプレートの保存に失敗しました</string>
|
||||||
|
<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="grant_root_failed">root の付与に失敗しました!</string>
|
<string name="grant_root_failed">root の付与に失敗しました!</string>
|
||||||
|
<string name="action">アクション</string>
|
||||||
<string name="open">開く</string>
|
<string name="open">開く</string>
|
||||||
<string name="enable_web_debugging">WebView デバッグを有効にする</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="select_file_tip">%1$s パーティション イメージが推奨されます</string>
|
<string name="direct_install">直接インストール (推奨)</string>
|
||||||
<string name="select_kmi">KMI を選択してください</string>
|
<string name="select_file">ファイルを選択</string>
|
||||||
<string name="install_next">次に</string>
|
|
||||||
<string name="install_inactive_slot">非アクティブなスロットにインストール (OTA 後)</string>
|
<string name="install_inactive_slot">非アクティブなスロットにインストール (OTA 後)</string>
|
||||||
<string name="install_inactive_slot_warning">再起動後、デバイスは**強制的に**、現在非アクティブなスロットから起動します。
|
<string name="install_inactive_slot_warning">再起動後、デバイスは**強制的に**、現在非アクティブなスロットから起動します。
|
||||||
\nこのオプションは、OTA が完了した後にのみ使用してください。
|
\nこのオプションは、OTA が完了した後にのみ使用してください。
|
||||||
\n続く?</string>
|
\n続行しますか?</string>
|
||||||
<string name="direct_install">直接インストール (推奨)</string>
|
<string name="install_next">次へ</string>
|
||||||
<string name="select_file">ファイルを選択してください</string>
|
<string name="select_file_tip">%1$s のパーティションイメージを推奨します</string>
|
||||||
|
<string name="select_kmi">KMI を選択してください</string>
|
||||||
|
<string name="settings_uninstall">アンインストール</string>
|
||||||
|
<string name="settings_uninstall_temporary">一時的にアンインストールする</string>
|
||||||
<string name="settings_uninstall_permanent">完全にアンインストールする</string>
|
<string name="settings_uninstall_permanent">完全にアンインストールする</string>
|
||||||
<string name="settings_restore_stock_image">ストックイメージを復元</string>
|
<string name="settings_restore_stock_image">ストックイメージを復元</string>
|
||||||
<string name="settings_uninstall_temporary">一時的にアンインストールする</string>
|
|
||||||
<string name="settings_uninstall">アンインストール</string>
|
|
||||||
<string name="settings_uninstall_temporary_message">KernelSU を一時的にアンインストールし、次回の再起動後に元の状態に戻します。</string>
|
<string name="settings_uninstall_temporary_message">KernelSU を一時的にアンインストールし、次回の再起動後に元の状態に戻します。</string>
|
||||||
<string name="settings_uninstall_permanent_message">KernelSU (ルートおよびすべてのモジュール) を完全かつ永久にアンインストールします。</string>
|
<string name="settings_uninstall_permanent_message">KernelSU (root およびすべてのモジュール) を完全かつ恒久的にアンインストールします。</string>
|
||||||
<string name="settings_restore_stock_image_message">バックアップが存在する場合、工場出荷時のイメージを復元できます (OTA の前に使用してください)。KernelSU をアンインストールする必要がある場合は、「完全にアンインストールする」を使用してください。</string>
|
<string name="settings_restore_stock_image_message">バックアップが存在する場合、工場出荷時のイメージを復元できます (OTA の前に使用してください)。KernelSU をアンインストールする必要がある場合は、「完全にアンインストールする」を使用してください。</string>
|
||||||
<string name="flashing">フラッシュ</string>
|
<string name="flashing">フラッシュ</string>
|
||||||
<string name="flash_success">フラッシュ成功</string>
|
<string name="flash_success">フラッシュが成功しました</string>
|
||||||
<string name="flash_failed">フラッシュ失敗</string>
|
<string name="flash_failed">フラッシュに失敗しました</string>
|
||||||
<string name="selected_lkm">選択された LKM: %s</string>
|
<string name="selected_lkm">選択された LKM: %s</string>
|
||||||
<string name="save_log">ログを保存</string>
|
<string name="save_log">ログを保存</string>
|
||||||
<string name="action">アクション</string>
|
|
||||||
<string name="log_saved">保存されたログ</string>
|
<string name="log_saved">保存されたログ</string>
|
||||||
<string name="module_sort_enabled_first">並べ替え(最初に有効)</string>
|
<string name="status_supported">対応</string>
|
||||||
<string name="module_sort_action_first">並べ替え(アクション優先)</string>
|
<string name="status_not_supported">非対応</string>
|
||||||
|
<string name="status_unknown">不明</string>
|
||||||
|
<string name="sus_su_mode">SuS SU モード:</string>
|
||||||
|
<!-- Module related -->
|
||||||
|
<string name="module_install_confirm">%1$s のモジュールをインストールしますか?</string>
|
||||||
|
<string name="unknown_module">不明なモジュール</string>
|
||||||
|
<!-- Restore related -->
|
||||||
|
<string name="restore_confirm_title">モジュールの復元を確認</string>
|
||||||
|
<string name="restore_confirm_message">この操作によりモジュールが上書きされます。続行しますか?</string>
|
||||||
|
<string name="confirm">確認</string>
|
||||||
|
<string name="cancel">キャンセル</string>
|
||||||
|
<!-- Backup related -->
|
||||||
|
<string name="backup_success">バックアップが完了しました (tar.gz)</string>
|
||||||
|
<string name="backup_failed">バックアップに失敗: %1$s</string>
|
||||||
|
<string name="backup_modules">モジュールをバックアップ</string>
|
||||||
|
<string name="restore_modules">モジュールを復元</string>
|
||||||
|
<!-- Restore related messages -->
|
||||||
|
<string name="restore_success">モジュールは正常に復元されました、再起動が必要です</string>
|
||||||
|
<string name="restore_failed">復元に失敗: %1$s</string>
|
||||||
|
<string name="restart_now">今すぐ再起動</string>
|
||||||
|
<string name="unknown_error">不明なエラー</string>
|
||||||
|
<!-- Command related -->
|
||||||
|
<string name="command_execution_failed">コマンドの実行に失敗しました: %1$s</string>
|
||||||
|
<!-- Allowlist related -->
|
||||||
|
<string name="allowlist_backup_success">許可リストのバックアップが成功しました</string>
|
||||||
|
<string name="allowlist_backup_failed">許可リストのバックアップに失敗: %1$s</string>
|
||||||
|
<string name="allowlist_restore_confirm_title">許可リストの復元を確認</string>
|
||||||
|
<string name="allowlist_restore_confirm_message">この操作により許可リストが上書きされます。続行しますか?</string>
|
||||||
|
<string name="allowlist_restore_success">許可リストの復元が成功しました</string>
|
||||||
|
<string name="allowlist_restore_failed">許可リストの復元に失敗: %1$s</string>
|
||||||
|
<string name="backup_allowlist">許可リストをバックアップ</string>
|
||||||
|
<string name="restore_allowlist">許可リストを復元</string>
|
||||||
|
<string name="settings_custom_background">カスタム背景を設定</string>
|
||||||
|
<string name="settings_custom_background_summary">カスタム背景を設定します。</string>
|
||||||
|
<string name="settings_card_manage">カードの管理</string>
|
||||||
|
<string name="settings_card_alpha">カードのアルファ</string>
|
||||||
|
<string name="settings_restore_default">デフォルトに復元</string>
|
||||||
|
<string name="home_android_version">Android のバージョン</string>
|
||||||
|
<string name="home_device_model">デバイスモデル</string>
|
||||||
|
<string name="su_not_allowed">%s にスーパーユーザー権限を付与することはできません</string>
|
||||||
|
<string name="settings_disable_su">su の互換性を無効化する</string>
|
||||||
|
<string name="settings_disable_su_summary">su コマンドを使用してアプリが root 権限を取得する動作を一時的に無効化します (既存の root プロセスは影響を受けません)。</string>
|
||||||
|
<string name="using_mksu_manager">SukiSU Beta Manager を使用しています。</string>
|
||||||
|
<string name="module_install_multiple_confirm">選択した %d 個のモジュールをインストールしてもよろしいですか?</string>
|
||||||
|
<string name="module_install_multiple_confirm_with_names">%1$d 個のモジュールをインストールしてもよろしいですか?\n\n%2$s</string>
|
||||||
|
<string name="more_settings">その他の設定</string>
|
||||||
|
<string name="selinux">SELinux</string>
|
||||||
|
<string name="selinux_enabled">有効</string>
|
||||||
|
<string name="selinux_disabled">無効</string>
|
||||||
|
<string name="simple_mode">シンプルモード</string>
|
||||||
|
<string name="simple_mode_summary">ON にすると不要なカードを非表示にします。</string>
|
||||||
|
<string name="hide_kernel_kernelsu_version">カーネルのバージョンを非表示にする</string>
|
||||||
|
<string name="hide_kernel_kernelsu_version_summary">カーネルのバージョンを非表示にします。</string>
|
||||||
|
<string name="hide_other_info">その他の情報を非表示にする</string>
|
||||||
|
<string name="hide_other_info_summary">ホームページ上のスーパーユーザー、モジュール、KPM モジュールの数に関する情報を非表示にします。</string>
|
||||||
|
<string name="hide_susfs_status">SuSFS ステータスを非表示にする</string>
|
||||||
|
<string name="hide_susfs_status_summary">ホームページ上の SuSFS ステータス情報を非表示にします。</string>
|
||||||
|
<string name="theme_mode">テーマモード</string>
|
||||||
|
<string name="theme_follow_system">システムに従う</string>
|
||||||
|
<string name="theme_light">ライトカラー</string>
|
||||||
|
<string name="theme_dark">ダークカラー</string>
|
||||||
|
<string name="manual_hook">手動でフック</string>
|
||||||
|
<string name="dynamic_color_title">ダイナミックカラー</string>
|
||||||
|
<string name="dynamic_color_summary">システムテーマのダイナミックカラーを使用します</string>
|
||||||
|
<string name="choose_theme_color">テーマカラーを選択</string>
|
||||||
|
<string name="color_default">ホワイト</string>
|
||||||
|
<string name="color_blue">ブルー</string>
|
||||||
|
<string name="color_green">グリーン</string>
|
||||||
|
<string name="color_purple">パープル</string>
|
||||||
|
<string name="color_orange">オレンジ</string>
|
||||||
|
<string name="color_pink">ピンク</string>
|
||||||
|
<string name="color_gray">グレー</string>
|
||||||
|
<string name="color_ivory">アイボリー</string>
|
||||||
|
<string name="flash_option">ブラシの設定</string>
|
||||||
|
<string name="flash_option_tip">フラッシュするファイルを選択</string>
|
||||||
|
<string name="horizon_kernel">AnyKernel3 をフラッシュ</string>
|
||||||
|
<string name="root_required">root 権限が必要です</string>
|
||||||
|
<string name="copy_failed">ファイルのコピーに失敗しました</string>
|
||||||
|
<string name="reboot_complete_title">スクラブが完了しました</string>
|
||||||
|
<string name="reboot_complete_msg">すぐに再起動しますか?</string>
|
||||||
|
<string name="yes">はい</string>
|
||||||
|
<string name="no">いいえ</string>
|
||||||
|
<string name="failed_reboot">再起動に失敗しました</string>
|
||||||
|
<string name="batch_authorization">bulk ライセンス</string>
|
||||||
|
<string name="batch_cancel_authorization">認証を一括でキャンセル</string>
|
||||||
|
<string name="backup">バックアップ</string>
|
||||||
|
<string name="color_yellow">イエロー</string>
|
||||||
|
<string name="kpm">KPM モジュール</string>
|
||||||
|
<string name="kpm_title">カーネルモジュール</string>
|
||||||
|
<string name="kpm_empty">カーネルモジュールは現在インストールされていません</string>
|
||||||
|
<string name="kpm_version">リリース</string>
|
||||||
|
<string name="kpm_author">作者</string>
|
||||||
|
<string name="kpm_uninstall">アンインストール</string>
|
||||||
|
<string name="kpm_uninstall_success">アンインストールに失敗しました</string>
|
||||||
|
<string name="kpm_uninstall_failed">アンインストールに失敗しました</string>
|
||||||
|
<string name="kpm_install">インストールを選択</string>
|
||||||
|
<string name="kpm_install_success">KPM モジュールの読み込みに成功しました</string>
|
||||||
|
<string name="kpm_install_failed">KPM モジュールの読み込みに失敗しました</string>
|
||||||
|
<string name="kpm_args">KPM パラメーター</string>
|
||||||
|
<string name="kpm_control">完全</string>
|
||||||
|
<string name="home_kpm_version">KPM のバージョン</string>
|
||||||
|
<string name="close_notice">閉じる</string>
|
||||||
|
<string name="kernel_module_notice">以下のカーネルモジュール関数は KernelPatch によって開発され、SukiSU Ultra のカーネルモジュール関数を含むように変更されました</string>
|
||||||
|
<string name="home_ContributionCard_kernelsu">SukiSU Ultra の今後にご期待ください</string>
|
||||||
|
<string name="kpm_control_success">成功</string>
|
||||||
|
<string name="kpm_control_failed">失敗</string>
|
||||||
|
<string name="home_click_to_ContributionCard_kernelsu">SukiSU Ultra は将来的に KSU から比較的に独立したブランチになりますが、公式の KernelSU や MKSU などの貢献に繋がるでしょう!</string>
|
||||||
|
<string name="not_supported">非対応</string>
|
||||||
|
<string name="supported">対応</string>
|
||||||
|
<string name="home_kpm_module">KPM モジュール数: %d </string>
|
||||||
|
<string name="kpm_invalid_file">無効な KPM ファイル</string>
|
||||||
|
<string name="kernel_patched">カーネルはパッチされていません</string>
|
||||||
|
<string name="kernel_not_enabled">カーネルは設定されていません</string>
|
||||||
|
<string name="custom_settings">カスタム設定</string>
|
||||||
|
<string name="kpm_install_mode">インストール</string>
|
||||||
|
<string name="kpm_install_mode_load">読み込む</string>
|
||||||
|
<string name="kpm_install_mode_embed">埋め込む</string>
|
||||||
|
<string name="kpm_install_mode_description">モジュールのインストールモードを選択してください:\n\n読み込む: モジュールを一時的に読み込みます\n埋め込む: システムに恒久的にインストールします</string>
|
||||||
|
<string name="log_failed_to_check_module_file">モジュールファイルの存在を確認できませんでした</string>
|
||||||
|
<string name="snackbar_failed_to_check_module_file">モジュールファイルが存在するか確認できません</string>
|
||||||
|
<string name="confirm_uninstall_title">アンインストールを確認</string>
|
||||||
|
<string name="confirm_uninstall_confirm">アンインストール中</string>
|
||||||
|
<string name="confirm_uninstall_dismiss">中止</string>
|
||||||
|
<string name="theme_color">テーマカラー</string>
|
||||||
|
<string name="invalid_file_type">ファイルの種類が正しくありません。.kpm ファイルを選択してください。</string>
|
||||||
|
<string name="confirm_uninstall_title_with_filename">アンインストール</string>
|
||||||
|
<string name="confirm_uninstall_content">次の KPM モジュールがアンインストールされます:\n%s</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
@@ -1,2 +1,266 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources></resources>
|
<resources>
|
||||||
|
<string name="app_name" translatable="false">SukiSU Ultra</string>
|
||||||
|
<string name="home">Home</string>
|
||||||
|
<string name="home_not_installed">Chưa cài đặt</string>
|
||||||
|
<string name="home_click_to_install">Nhấn vào đây để cài đặt</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">Superusers: %d</string>
|
||||||
|
<string name="home_module_count">Mô-đun: %d</string>
|
||||||
|
<string name="home_unsupported">Không được hỗ trợ</string>
|
||||||
|
<string name="home_unsupported_reason">Không phát hiện được trình điều khiển KernelSU trên kernel của bạn.</string>
|
||||||
|
<string name="home_kernel">Phiên bản Kernel</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_manager_version">Phiên bản quản lý</string>
|
||||||
|
<string name="home_fingerprint">Dấu vân tay</string>
|
||||||
|
<string name="home_selinux_status">Trạng thái SELinux</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">Không rõ</string>
|
||||||
|
<string name="superuser">Superuser</string>
|
||||||
|
<string name="module_failed_to_enable">Không thể bật mô-đun: %s</string>
|
||||||
|
<string name="module_failed_to_disable">Không thể vô hiệu hóa mô-đun: %s</string>
|
||||||
|
<string name="module_empty">Không có mô-đun nào được cài đặt</string>
|
||||||
|
<string name="module">Mô-đun</string>
|
||||||
|
<string name="module_sort_action_first">Sắp xếp (theo hành động)</string>
|
||||||
|
<string name="module_sort_enabled_first">Sắp xếp (theo trạng thái)</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="settings">Cài đặt</string>
|
||||||
|
<string name="reboot_userspace">Khởi động lại không gian người dùng</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 thêm</string>
|
||||||
|
<string name="module_uninstall_confirm">Bạn có chắc chắn muốn gỡ cài đặt mô-đun %s không?</string>
|
||||||
|
<string name="module_uninstall_success">%s đã gỡ cài đặt</string>
|
||||||
|
<string name="module_uninstall_failed">Không thể gỡ cài đặt: %s</string>
|
||||||
|
<string name="module_version">Phiên bản</string>
|
||||||
|
<string name="module_author">Tác giả</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="hide_system_apps">Ẩn ứng dụng hệ thống</string>
|
||||||
|
<string name="send_log">Gửi nhật ký</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">Các mô-đun không khả dụng do xung đột với Magisk!</string>
|
||||||
|
<string name="home_learn_kernelsu">Tìm hiểu KernelSU</string>
|
||||||
|
<string name="home_learn_kernelsu_url">https://kernelsu.org/guide/what-is-kernelsu.html</string>
|
||||||
|
<string name="home_click_to_learn_kernelsu">Tìm hiểu cách cài đặt KernelSU và sử dụng các mô-đun</string>
|
||||||
|
<string name="home_support_title">Hỗ trợ chúng tôi</string>
|
||||||
|
<string name="home_support_content">KernelSU sẽ luôn là miễn phí và mã nguồn mở. Tuy nhiên, bạn có thể cho chúng tôi thấy rằng bạn quan tâm bằng cách quyên góp.</string>
|
||||||
|
<string name="about_source_code"><![CDATA[View source code at %1$s<br/>Tham gia kênh %2$s của chúng tôi]]></string>
|
||||||
|
<string name="profile" translatable="false">Hồ sơ ứng dụng</string>
|
||||||
|
<string name="profile_default">Mặc định</string>
|
||||||
|
<string name="profile_template">Bản mẫu</string>
|
||||||
|
<string name="profile_custom">Tùy chỉnh</string>
|
||||||
|
<string name="profile_name">Tên hồ sơ</string>
|
||||||
|
<string name="profile_namespace">Tên không gian gắn kết</string>
|
||||||
|
<string name="profile_namespace_inherited">Được thừa hưởng</string>
|
||||||
|
<string name="profile_namespace_global">Toàn cầu</string>
|
||||||
|
<string name="profile_namespace_individual">Cá nhân</string>
|
||||||
|
<string name="profile_groups">Nhóm</string>
|
||||||
|
<string name="profile_capabilities">Khả năng</string>
|
||||||
|
<string name="profile_selinux_context">Bối cảnh SELinux</string>
|
||||||
|
<string name="profile_umount_modules">Bỏ gắn kết mô-đun</string>
|
||||||
|
<string name="failed_to_update_app_profile">Không cập nhật được Hồ sơ ứng dụng cho %s</string>
|
||||||
|
<string name="require_kernel_version" formatted="false">Phiên bản KernelSU hiện tại %d quá thấp để trình quản lý hoạt động bình thường. Vui lòng nâng cấp lên phiên bản %d hoặc cao hơn!</string>
|
||||||
|
<string name="settings_umount_modules_default">Bỏ gắn kết các mô-đun theo mặc định</string>
|
||||||
|
<string name="settings_umount_modules_default_summary">Giá trị mặc định cho \"Bỏ gắn kết các mô-đun\" trong Hồ sơ ứng dụng. Nếu được bật, nó sẽ xóa tất cả các sửa đổi của mô-đun đối với hệ thống đối và các ứng dụng không có hồ sơ được đặt.</string>
|
||||||
|
<string name="settings_susfs_toggle">Ẩn móc kprobe</string>
|
||||||
|
<string name="settings_susfs_toggle_summary">Vô hiệu hóa các móc kprobe do KSU tạo ra và kích hoạt các móc không phải kprobe tích hợp sẵn, triển khai sẽ được áp dụng cho hạt nhân không phải GKI, không hỗ trợ krp</string>
|
||||||
|
<string name="profile_umount_modules_summary">Bật tùy chọn này sẽ cho phép KernelSU khôi phục mọi tệp đã được các mô-đun sửa đổi cho ứng dụng này.</string>
|
||||||
|
<string name="profile_selinux_domain">Domain</string>
|
||||||
|
<string name="profile_selinux_rules">Quy tắc</string>
|
||||||
|
<string name="module_update">Cập nhật</string>
|
||||||
|
<string name="module_downloading">Đang tải xuống mô-đun: %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, hãy nhấp để cập nhật.</string>
|
||||||
|
<string name="launch_app">Khởi chạy</string>
|
||||||
|
<string name="force_stop_app" formatted="false">Buộc dừng</string>
|
||||||
|
<string name="restart_app">Khởi động lại</string>
|
||||||
|
<string name="failed_to_update_sepolicy">Không cập nhật được quy tắc SELinux 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ý 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ệ</string>
|
||||||
|
<string name="app_profile_template_name">Tên</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</string>
|
||||||
|
<string name="app_profile_template_readonly">Chỉ đọc</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ộ 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">Cannot find local template to export!</string>
|
||||||
|
<string name="app_profile_template_import_success">Đã nhập thành công</string>
|
||||||
|
<string name="app_profile_template_sync">Đồng bộ mẫu trực tuyến</string>
|
||||||
|
<string name="app_profile_template_save_failed">Không lưu được mẫu</string>
|
||||||
|
<string name="app_profile_template_import_empty">Bộ nhớ tạm đang trống!</string>
|
||||||
|
<string name="module_changelog_failed">Lấy nhật ký thay đổi không thành công: %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="grant_root_failed">Không cấp được quyền root!</string>
|
||||||
|
<string name="action">Khởi chạy</string>
|
||||||
|
<string name="open">Mở</string>
|
||||||
|
<string name="enable_web_debugging">Bật 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 một tập tin</string>
|
||||||
|
<string name="install_inactive_slot">Cài đặt vào khe không hoạt độ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 khe cắm hiện tại không hoạt động sau khi khởi động lại!\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="settings_uninstall">Uninstall</string>
|
||||||
|
<string name="settings_uninstall_temporary">Gỡ cài đặt tạm thời</string>
|
||||||
|
<string name="settings_uninstall_permanent">Gỡ cài đặt vĩnh viễn</string>
|
||||||
|
<string name="settings_restore_stock_image">Khôi phục img ban đầu</string>
|
||||||
|
<string name="settings_uninstall_temporary_message">Gỡ cài đặt tạm thời KernelSU, 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 (Root và tất cả các mô-đun) hoàn toàn và vĩnh viễn.</string>
|
||||||
|
<string name="settings_restore_stock_image_message">Khôi phục ảnh gốc (nếu có bản sao lưu), thường được sử dụng trước OTA; nếu bạn cần gỡ cài đặt KernelSU, vui lòng sử dụng \"Gỡ cài đặt vĩnh viễn\".</string>
|
||||||
|
<string name="flashing">Đang flash...</string>
|
||||||
|
<string name="flash_success">Flash thành công</string>
|
||||||
|
<string name="flash_failed">Lỗi flash</string>
|
||||||
|
<string name="selected_lkm">LKM đã chọn: %s</string>
|
||||||
|
<string name="save_log">Lưu nhật ký</string>
|
||||||
|
<string name="log_saved">Nhật ký đã lưu</string>
|
||||||
|
<string name="status_supported">Được hỗ trợ</string>
|
||||||
|
<string name="status_not_supported">Không được hỗ trợ</string>
|
||||||
|
<string name="status_unknown">Không rõ</string>
|
||||||
|
<string name="sus_su_mode">Chế độ SuS: SU</string>
|
||||||
|
<!-- Module related -->
|
||||||
|
<string name="module_install_confirm">Xác nhận cài đặt mô-đun %1$s ?</string>
|
||||||
|
<string name="unknown_module">Mô-đun không xác định</string>
|
||||||
|
<!-- Restore related -->
|
||||||
|
<string name="restore_confirm_title">Xác nhận khôi phục mô-đun</string>
|
||||||
|
<string name="restore_confirm_message">Hành động này sẽ ghi đè lên tất cả các mô-đun hiện có. Tiếp tục?</string>
|
||||||
|
<string name="confirm">Xác nhận</string>
|
||||||
|
<string name="cancel">Hủy bỏ</string>
|
||||||
|
<!-- Backup related -->
|
||||||
|
<string name="backup_success">Sao lưu thành công (tar.gz)</string>
|
||||||
|
<string name="backup_failed">Sao lưu không thành công: %1$s</string>
|
||||||
|
<string name="backup_modules">Sao lưu mô-đun</string>
|
||||||
|
<string name="restore_modules">Khôi phục các mô-đun</string>
|
||||||
|
<!-- Restore related messages -->
|
||||||
|
<string name="restore_success">Các mô-đun đã được khôi phục thành công, cần khởi động lại</string>
|
||||||
|
<string name="restore_failed">Khôi phục không thành công: %1$s</string>
|
||||||
|
<string name="restart_now">Khởi động lại ngay</string>
|
||||||
|
<string name="unknown_error">Lỗi không xác định</string>
|
||||||
|
<!-- Command related -->
|
||||||
|
<string name="command_execution_failed">Thực hiện lệnh không thành công: %1$s</string>
|
||||||
|
<!-- Allowlist related -->
|
||||||
|
<string name="allowlist_backup_success">Sao lưu danh sách cho phép thành công</string>
|
||||||
|
<string name="allowlist_backup_failed">Sao lưu danh sách cho phép không thành công: %1$s</string>
|
||||||
|
<string name="allowlist_restore_confirm_title">Xác nhận khôi phục danh sách cho phép</string>
|
||||||
|
<string name="allowlist_restore_confirm_message">Hành động này sẽ ghi đè lên danh sách cho phép hiện tại. Tiếp tục?</string>
|
||||||
|
<string name="allowlist_restore_success">Đã khôi phục danh sách cho phép thành công</string>
|
||||||
|
<string name="allowlist_restore_failed">Khôi phục danh sách cho phép không thành công: %1$s</string>
|
||||||
|
<string name="backup_allowlist">Sao lưu danh sách cho phép</string>
|
||||||
|
<string name="restore_allowlist">Khôi phục danh sách cho phép</string>
|
||||||
|
<string name="settings_custom_background">Cài đặt nền tùy chỉnh</string>
|
||||||
|
<string name="settings_custom_background_summary">Cài đặt tùy chỉnh nền</string>
|
||||||
|
<string name="settings_card_manage">Quản lý thẻ</string>
|
||||||
|
<string name="settings_card_alpha">Thẻ alpha</string>
|
||||||
|
<string name="settings_restore_default">Khôi phục mặc định</string>
|
||||||
|
<string name="home_android_version">Phiên bản Android</string>
|
||||||
|
<string name="home_device_model">Model thiết bị</string>
|
||||||
|
<string name="su_not_allowed">Không được phép cấp siêu người dùng cho %s</string>
|
||||||
|
<string name="settings_disable_su">Vô hiệu hóa khả năng tương thích su</string>
|
||||||
|
<string name="settings_disable_su_summary">Tạm thời vô hiệu hóa mọi ứng dụng khỏi quyền root thông qua lệnh su (các tiến trình root hiện có sẽ không bị ảnh hưởng).</string>
|
||||||
|
<string name="using_mksu_manager">Bạn đang sử dụng trình quản lý SukiSU thử nghiệm</string>
|
||||||
|
<string name="module_install_multiple_confirm">Bạn có chắc chắn muốn cài đặt các mô-đun %d đã chọn không?</string>
|
||||||
|
<string name="module_install_multiple_confirm_with_names">Bạn có chắc chắn muốn cài đặt các mô-đun %1$d sau không? \n\n%2$s</string>
|
||||||
|
<string name="more_settings">Nhiều thiết lập hơn</string>
|
||||||
|
<string name="selinux">SELinux</string>
|
||||||
|
<string name="selinux_enabled">Đã bật</string>
|
||||||
|
<string name="selinux_disabled">Đã tắt</string>
|
||||||
|
<string name="simple_mode">Chế độ đơn giản</string>
|
||||||
|
<string name="simple_mode_summary">Ẩn các thẻ không cần thiết khi bật</string>
|
||||||
|
<string name="hide_kernel_kernelsu_version">Ẩn phiên bản kernel</string>
|
||||||
|
<string name="hide_kernel_kernelsu_version_summary">Ẩn phiên bản kernel hiện tại</string>
|
||||||
|
<string name="hide_other_info">Ẩn thông tin thêm</string>
|
||||||
|
<string name="hide_other_info_summary">Ẩn thông tin về số lượng app đã được cấp root, mô-đun và mô-đun KPM trên trang chủ</string>
|
||||||
|
<string name="hide_susfs_status">Ẩn trạng thái SuSFS</string>
|
||||||
|
<string name="hide_susfs_status_summary">Ẩn thông tin trạng thái SuSFS trên trang chủ</string>
|
||||||
|
<string name="theme_mode">Chế độ chủ đề</string>
|
||||||
|
<string name="theme_follow_system">Theo hệ thống</string>
|
||||||
|
<string name="theme_light">Sáng</string>
|
||||||
|
<string name="theme_dark">Tối</string>
|
||||||
|
<string name="manual_hook">Móc thủ công</string>
|
||||||
|
<string name="dynamic_color_title">Màu sắc động</string>
|
||||||
|
<string name="dynamic_color_summary">Màu sắc động sử dụng chủ đề hệ thống</string>
|
||||||
|
<string name="choose_theme_color">Chọn màu chủ đề</string>
|
||||||
|
<string name="color_default">Trắng</string>
|
||||||
|
<string name="color_blue">Xanh dương</string>
|
||||||
|
<string name="color_green">Xanh lá</string>
|
||||||
|
<string name="color_purple">Tím</string>
|
||||||
|
<string name="color_orange">Cam</string>
|
||||||
|
<string name="color_pink">Hồng</string>
|
||||||
|
<string name="color_gray">Xám</string>
|
||||||
|
<string name="color_ivory">Ngà voi</string>
|
||||||
|
<string name="flash_option">Tùy chọn cọ</string>
|
||||||
|
<string name="flash_option_tip">Chọn tập tin cần flash</string>
|
||||||
|
<string name="horizon_kernel">File Anykernel3</string>
|
||||||
|
<string name="root_required">Yêu cầu quyền root</string>
|
||||||
|
<string name="copy_failed">Sao chép tập tin không thành công</string>
|
||||||
|
<string name="reboot_complete_title">Hoàn tất</string>
|
||||||
|
<string name="reboot_complete_msg">khởi động lại ngay lập tức?</string>
|
||||||
|
<string name="yes">Có</string>
|
||||||
|
<string name="no">Không</string>
|
||||||
|
<string name="failed_reboot">Khởi động lại không thành công</string>
|
||||||
|
<string name="batch_authorization">Giấy phép số lượng lớn</string>
|
||||||
|
<string name="batch_cancel_authorization">Hủy ủy quyền hàng loạt</string>
|
||||||
|
<string name="backup">Sao lưu</string>
|
||||||
|
<string name="color_yellow">Vàng</string>
|
||||||
|
<string name="kpm">Mô-đun kpm</string>
|
||||||
|
<string name="kpm_title">Mô-đun KPM</string>
|
||||||
|
<string name="kpm_empty">Không có mô-đun nào được cài đặt.</string>
|
||||||
|
<string name="kpm_version">Phát hành</string>
|
||||||
|
<string name="kpm_author">Tác giả</string>
|
||||||
|
<string name="kpm_uninstall">Gỡ cài đặt</string>
|
||||||
|
<string name="kpm_uninstall_success">Đã gỡ cài đặt thành công</string>
|
||||||
|
<string name="kpm_uninstall_failed">Không thể gỡ cài đặt</string>
|
||||||
|
<string name="kpm_install">Cài đặt</string>
|
||||||
|
<string name="kpm_install_success">Tải module kpm thành công</string>
|
||||||
|
<string name="kpm_install_failed">Tải module kpm không thành công</string>
|
||||||
|
<string name="kpm_args">thông số kpm</string>
|
||||||
|
<string name="kpm_control">Tùy chỉnh</string>
|
||||||
|
<string name="home_kpm_version">Phiên bản KPM</string>
|
||||||
|
<string name="close_notice">Đóng</string>
|
||||||
|
<string name="kernel_module_notice">Các chức năng mô-đun kernel sau đây được KernelPatch phát triển và sửa đổi để bao gồm các chức năng mô-đun hạt nhân của SukiSU Ultra</string>
|
||||||
|
<string name="home_ContributionCard_kernelsu">SukiSU Ultra mong đợi:</string>
|
||||||
|
<string name="kpm_control_success">Thành công</string>
|
||||||
|
<string name="kpm_control_failed">Thất bại</string>
|
||||||
|
<string name="home_click_to_ContributionCard_kernelsu">SukiSU Ultra sẽ là một nhánh tương đối độc lập của KSU trong tương lai, nhưng xin cảm ơn KernelSU và MKSU chính thức vì những đóng góp của họ!</string>
|
||||||
|
<string name="not_supported">Không được hỗ trợ</string>
|
||||||
|
<string name="supported">Được hỗ trợ</string>
|
||||||
|
<string name="home_kpm_module">Mô-đun KPM:%d </string>
|
||||||
|
<string name="kpm_invalid_file">Tệp KPM không hợp lệ</string>
|
||||||
|
<string name="kernel_patched">chưa được vá</string>
|
||||||
|
<string name="kernel_not_enabled">chưa được vá</string>
|
||||||
|
<string name="custom_settings">Cài đặt tùy chỉnh</string>
|
||||||
|
<string name="kpm_install_mode">Cài đặt</string>
|
||||||
|
<string name="kpm_install_mode_load">Tải</string>
|
||||||
|
<string name="kpm_install_mode_embed">Nhúng</string>
|
||||||
|
<string name="kpm_install_mode_description">Vui lòng chọn chế độ cài đặt mô-đun: \n\nTải: Tải tạm thời mô-đun \nNhúng: Cài đặt vĩnh viễn vào hệ thống</string>
|
||||||
|
<string name="log_failed_to_check_module_file">Không kiểm tra được sự tồn tại của tệp mô-đun</string>
|
||||||
|
<string name="snackbar_failed_to_check_module_file">Không thể kiểm tra xem tệp mô-đun có tồn tại không</string>
|
||||||
|
<string name="confirm_uninstall_title">Xác nhận gỡ cài đặt</string>
|
||||||
|
<string name="confirm_uninstall_confirm">loại bỏ</string>
|
||||||
|
<string name="confirm_uninstall_dismiss">bãi bỏ</string>
|
||||||
|
<string name="theme_color">Màu chủ đề</string>
|
||||||
|
</resources>
|
||||||
|
|||||||
@@ -180,7 +180,7 @@
|
|||||||
<string name="su_not_allowed">不允许授予 %s 超级用户权限</string>
|
<string name="su_not_allowed">不允许授予 %s 超级用户权限</string>
|
||||||
<string name="settings_disable_su">禁用 su 兼容性</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="using_mksu_manager">你正在使用的是 MKSU 第三方管理器</string>
|
<string name="using_mksu_manager">你正在使用的是 SukiSU Beta 版管理器</string>
|
||||||
<string name="module_install_multiple_confirm">确定要安装选择的 %d 个模块吗?</string>
|
<string name="module_install_multiple_confirm">确定要安装选择的 %d 个模块吗?</string>
|
||||||
<string name="module_install_multiple_confirm_with_names">确定要安装以下 %1$d 个模块吗?\n\n%2$s</string>
|
<string name="module_install_multiple_confirm_with_names">确定要安装以下 %1$d 个模块吗?\n\n%2$s</string>
|
||||||
<string name="more_settings">更多设置</string>
|
<string name="more_settings">更多设置</string>
|
||||||
@@ -191,6 +191,10 @@
|
|||||||
<string name="simple_mode_summary">开启后将隐藏不必要的卡片</string>
|
<string name="simple_mode_summary">开启后将隐藏不必要的卡片</string>
|
||||||
<string name="hide_kernel_kernelsu_version">隐藏内核版本号</string>
|
<string name="hide_kernel_kernelsu_version">隐藏内核版本号</string>
|
||||||
<string name="hide_kernel_kernelsu_version_summary">隐藏内核部分的 KernelSU 版本号</string>
|
<string name="hide_kernel_kernelsu_version_summary">隐藏内核部分的 KernelSU 版本号</string>
|
||||||
|
<string name="hide_other_info">强迫症开关</string>
|
||||||
|
<string name="hide_other_info_summary">隐藏主页上的超级用户数、模块数和 KPM 模块数信息</string>
|
||||||
|
<string name="hide_susfs_status">隐藏 SuSFS 状态信息</string>
|
||||||
|
<string name="hide_susfs_status_summary">隐藏主页上的 SuSFS 状态信息</string>
|
||||||
<string name="theme_mode">主题模式</string>
|
<string name="theme_mode">主题模式</string>
|
||||||
<string name="theme_follow_system">跟随系统</string>
|
<string name="theme_follow_system">跟随系统</string>
|
||||||
<string name="theme_light">浅色</string>
|
<string name="theme_light">浅色</string>
|
||||||
@@ -225,13 +229,37 @@
|
|||||||
<string name="kpm_empty">暂无已安装的内核模块</string>
|
<string name="kpm_empty">暂无已安装的内核模块</string>
|
||||||
<string name="kpm_version">版本</string>
|
<string name="kpm_version">版本</string>
|
||||||
<string name="kpm_author">作者</string>
|
<string name="kpm_author">作者</string>
|
||||||
<string name="kpm_execute">执行</string>
|
|
||||||
<string name="kpm_uninstall">卸载</string>
|
<string name="kpm_uninstall">卸载</string>
|
||||||
<string name="kpm_uninstall_confirm">确定要卸载内核模块 %1$s 吗?</string>
|
|
||||||
<string name="kpm_uninstall_success">卸载成功</string>
|
<string name="kpm_uninstall_success">卸载成功</string>
|
||||||
<string name="kpm_uninstall_failed">卸载失败</string>
|
<string name="kpm_uninstall_failed">卸载失败</string>
|
||||||
<string name="kpm_install">安装kpm模块</string>
|
<string name="kpm_install">选择安装</string>
|
||||||
<string name="kpm_install_confirm">确认安装吗?</string>
|
<string name="kpm_install_success">加载 kpm 模块成功</string>
|
||||||
<string name="kpm_install_success">安装kpm模块成功</string>
|
<string name="kpm_install_failed">加载 kpm 模块失败</string>
|
||||||
<string name="kpm_install_failed">安装kpm模块失败</string>
|
<string name="home_kpm_version">KPM 版本</string>
|
||||||
|
<string name="kpm_control">执行</string>
|
||||||
|
<string name="close_notice">关闭</string>
|
||||||
|
<string name="kpm_control_success">成功</string>
|
||||||
|
<string name="kpm_control_failed">错误</string>
|
||||||
|
<string name="not_supported">不支持</string>
|
||||||
|
<string name="supported">支持</string>
|
||||||
|
<string name="home_kpm_module">KPM 模块数:%d </string>
|
||||||
|
<string name="kpm_invalid_file">KPM 文件无效</string>
|
||||||
|
<string name="kernel_patched">内核未进行补丁</string>
|
||||||
|
<string name="kernel_not_enabled">内核未配置</string>
|
||||||
|
<string name="kernel_module_notice">以下内核模块功能由 KernelPatch 开发,经过修改后加入 SukiSU Ultra 的内核模块功能</string>
|
||||||
|
<string name="home_ContributionCard_kernelsu">SukiSU Ultra 展望</string>
|
||||||
|
<string name="home_click_to_ContributionCard_kernelsu">SukiSU Ultra 未来将会成为一个相对独立的 KSU 分支,但是依然感谢官方 KernelSU 和 MKSU 等做出的贡献</string>
|
||||||
|
<string name="custom_settings">个性化设置</string>
|
||||||
|
<string name="kpm_install_mode">安装</string>
|
||||||
|
<string name="kpm_install_mode_load">加载</string>
|
||||||
|
<string name="kpm_install_mode_embed">嵌入</string>
|
||||||
|
<string name="kpm_install_mode_description">请选择模块安装模式:\n\n加载:临时加载模块\n嵌入:永久安装到系统</string>
|
||||||
|
<string name="snackbar_failed_to_check_module_file">无法检查模块文件是否存在</string>
|
||||||
|
<string name="confirm_uninstall_title">确认卸载</string>
|
||||||
|
<string name="confirm_uninstall_confirm">删除</string>
|
||||||
|
<string name="confirm_uninstall_dismiss">取消</string>
|
||||||
|
<string name="theme_color">主题颜色</string>
|
||||||
|
<string name="invalid_file_type">文件类型不正确,请选择 .kpm 文件</string>
|
||||||
|
<string name="confirm_uninstall_title_with_filename">卸载</string>
|
||||||
|
<string name="confirm_uninstall_content">将卸载以下 kpm 模块:\n%s</string>
|
||||||
</resources>
|
</resources>
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources>
|
<resources>
|
||||||
<string name="app_name" translatable="false">SukiSU</string>
|
<string name="app_name" translatable="false">SukiSU Ultra</string>
|
||||||
<string name="home">Home</string>
|
<string name="home">Home</string>
|
||||||
<string name="home_not_installed">Not installed</string>
|
<string name="home_not_installed">Not installed</string>
|
||||||
<string name="home_click_to_install">Click to install</string>
|
<string name="home_click_to_install">Click to install</string>
|
||||||
@@ -10,7 +10,7 @@
|
|||||||
<string name="home_module_count">Modules: %d</string>
|
<string name="home_module_count">Modules: %d</string>
|
||||||
<string name="home_unsupported">Unsupported</string>
|
<string name="home_unsupported">Unsupported</string>
|
||||||
<string name="home_unsupported_reason">No KernelSU driver detected on your kernel, wrong kernel?.</string>
|
<string name="home_unsupported_reason">No KernelSU driver detected on your kernel, wrong kernel?.</string>
|
||||||
<string name="home_kernel">Kernel</string>
|
<string name="home_kernel">Kernel version</string>
|
||||||
<string name="home_susfs">SuSFS: %s</string>
|
<string name="home_susfs">SuSFS: %s</string>
|
||||||
<string name="home_susfs_version">Version de SuSFS</string>
|
<string name="home_susfs_version">Version de SuSFS</string>
|
||||||
<string name="home_susfs_sus_su">SuS SU</string>
|
<string name="home_susfs_sus_su">SuS SU</string>
|
||||||
@@ -181,7 +181,7 @@
|
|||||||
<string name="su_not_allowed">Granting superuser to %s is not allowed</string>
|
<string name="su_not_allowed">Granting superuser to %s is not allowed</string>
|
||||||
<string name="settings_disable_su">Disable su compatibility</string>
|
<string name="settings_disable_su">Disable su compatibility</string>
|
||||||
<string name="settings_disable_su_summary">Temporarily disable any applications from obtaining root privileges via the su command (existing root processes will not be affected).</string>
|
<string name="settings_disable_su_summary">Temporarily disable any applications from obtaining root privileges via the su command (existing root processes will not be affected).</string>
|
||||||
<string name="using_mksu_manager">You are using the MKSU third-party manager</string>
|
<string name="using_mksu_manager">You are using the SukiSU Beta manager</string>
|
||||||
<string name="module_install_multiple_confirm">Are you sure you want to install the selected %d modules?</string>
|
<string name="module_install_multiple_confirm">Are you sure you want to install the selected %d modules?</string>
|
||||||
<string name="module_install_multiple_confirm_with_names">Sure you want to install the following %1$d modules? \n\n%2$s</string>
|
<string name="module_install_multiple_confirm_with_names">Sure you want to install the following %1$d modules? \n\n%2$s</string>
|
||||||
<string name="more_settings">more settings</string>
|
<string name="more_settings">more settings</string>
|
||||||
@@ -192,6 +192,10 @@
|
|||||||
<string name="simple_mode_summary">Hides unnecessary cards when turned on</string>
|
<string name="simple_mode_summary">Hides unnecessary cards when turned on</string>
|
||||||
<string name="hide_kernel_kernelsu_version">Hide kernel version</string>
|
<string name="hide_kernel_kernelsu_version">Hide kernel version</string>
|
||||||
<string name="hide_kernel_kernelsu_version_summary">Hide kernel version</string>
|
<string name="hide_kernel_kernelsu_version_summary">Hide kernel version</string>
|
||||||
|
<string name="hide_other_info">Hide other info</string>
|
||||||
|
<string name="hide_other_info_summary">Hides information about the number of super users, modules and KPM modules on the home page</string>
|
||||||
|
<string name="hide_susfs_status">Hide SuSFS status</string>
|
||||||
|
<string name="hide_susfs_status_summary">Hide SuSFS status information on the home page</string>
|
||||||
<string name="theme_mode">Theme Mode</string>
|
<string name="theme_mode">Theme Mode</string>
|
||||||
<string name="theme_follow_system">follow-up system</string>
|
<string name="theme_follow_system">follow-up system</string>
|
||||||
<string name="theme_light">light color</string>
|
<string name="theme_light">light color</string>
|
||||||
@@ -210,7 +214,7 @@
|
|||||||
<string name="color_ivory">ivory</string>
|
<string name="color_ivory">ivory</string>
|
||||||
<string name="flash_option">Brush Options</string>
|
<string name="flash_option">Brush Options</string>
|
||||||
<string name="flash_option_tip">Select the file to be flashed</string>
|
<string name="flash_option_tip">Select the file to be flashed</string>
|
||||||
<string name="horizon_kernel">Anykernel3 Flush</string>
|
<string name="horizon_kernel">Anykernel3 Flash</string>
|
||||||
<string name="root_required">Requires root privileges</string>
|
<string name="root_required">Requires root privileges</string>
|
||||||
<string name="copy_failed">File Copy Failure</string>
|
<string name="copy_failed">File Copy Failure</string>
|
||||||
<string name="reboot_complete_title">Scrubbing complete</string>
|
<string name="reboot_complete_title">Scrubbing complete</string>
|
||||||
@@ -227,15 +231,39 @@
|
|||||||
<string name="kpm_empty">No installed kernel modules at this time</string>
|
<string name="kpm_empty">No installed kernel modules at this time</string>
|
||||||
<string name="kpm_version">releases</string>
|
<string name="kpm_version">releases</string>
|
||||||
<string name="kpm_author">author</string>
|
<string name="kpm_author">author</string>
|
||||||
<string name="kpm_execute">fulfillment</string>
|
|
||||||
<string name="kpm_uninstall">uninstallation</string>
|
<string name="kpm_uninstall">uninstallation</string>
|
||||||
<string name="kpm_uninstall_confirm">Determine the kernel module to uninstall: %1$s ?</string>
|
|
||||||
<string name="kpm_uninstall_success">Uninstalled successfully</string>
|
<string name="kpm_uninstall_success">Uninstalled successfully</string>
|
||||||
<string name="kpm_uninstall_failed">Failed to uninstall</string>
|
<string name="kpm_uninstall_failed">Failed to uninstall</string>
|
||||||
<string name="kpm_install">Installing the kpm module</string>
|
<string name="kpm_install">Select Installation</string>
|
||||||
<string name="kpm_install_confirm">Confirm installation?</string>
|
<string name="kpm_install_success">Load of kpm module successful</string>
|
||||||
<string name="kpm_install_success">Installation of kpm module successful</string>
|
<string name="kpm_install_failed">Load of kpm module failed</string>
|
||||||
<string name="kpm_install_failed">Installation of kpm module failed</string>
|
<string name="kpm_args">kpm parameters</string>
|
||||||
<string name="kpm_args">kpm 参数</string>
|
<string name="kpm_control">fulfillment</string>
|
||||||
<string name="kpm_control">kpm 控制</string>
|
<string name="home_kpm_version">KPM Version</string>
|
||||||
|
<string name="close_notice">close</string>
|
||||||
|
<string name="kernel_module_notice">The following kernel module functions were developed by KernelPatch and modified to include the kernel module functions of SukiSU Ultra</string>
|
||||||
|
<string name="home_ContributionCard_kernelsu">SukiSU Ultra Look forward to</string>
|
||||||
|
<string name="kpm_control_success">success</string>
|
||||||
|
<string name="kpm_control_failed">failed</string>
|
||||||
|
<string name="home_click_to_ContributionCard_kernelsu">SukiSU Ultra will be a relatively independent branch of KSU in the future, but thanks to the official KernelSU and MKSU etc. for their contributions!</string>
|
||||||
|
<string name="not_supported">unsupported</string>
|
||||||
|
<string name="supported">supported</string>
|
||||||
|
<string name="home_kpm_module">Number of KPM modules:%d </string>
|
||||||
|
<string name="kpm_invalid_file">Invalid KPM file</string>
|
||||||
|
<string name="kernel_patched">Kernel not patched</string>
|
||||||
|
<string name="kernel_not_enabled">Kernel not configured</string>
|
||||||
|
<string name="custom_settings">Custom settings</string>
|
||||||
|
<string name="kpm_install_mode">install</string>
|
||||||
|
<string name="kpm_install_mode_load">Load</string>
|
||||||
|
<string name="kpm_install_mode_embed">embed</string>
|
||||||
|
<string name="kpm_install_mode_description">Please select the module installation mode: \n\nLoad: Temporarily load the module \nEmbedded: Permanently install into the system</string>
|
||||||
|
<string name="log_failed_to_check_module_file">Failed to check module file existence</string>
|
||||||
|
<string name="snackbar_failed_to_check_module_file">Unable to check if module file exists</string>
|
||||||
|
<string name="confirm_uninstall_title">Confirm uninstallation</string>
|
||||||
|
<string name="confirm_uninstall_confirm">removing</string>
|
||||||
|
<string name="confirm_uninstall_dismiss">abolish</string>
|
||||||
|
<string name="theme_color">Theme Color</string>
|
||||||
|
<string name="invalid_file_type">Incorrect file type, select .kpm file</string>
|
||||||
|
<string name="confirm_uninstall_title_with_filename">uninstallation</string>
|
||||||
|
<string name="confirm_uninstall_content">The following kpm modules will be uninstalled:\n%s</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
@@ -13,15 +13,38 @@
|
|||||||
#define CMD_KPM_CONTROL_MAX 7
|
#define CMD_KPM_CONTROL_MAX 7
|
||||||
|
|
||||||
// 控制代码
|
// 控制代码
|
||||||
#define SUKISU_KPM_LOAD 1
|
// prctl(xxx, 28, "PATH", "ARGS")
|
||||||
#define SUKISU_KPM_UNLOAD 2
|
// success return 0, error return -N
|
||||||
#define SUKISU_KPM_NUM 3
|
#define SUKISU_KPM_LOAD 28
|
||||||
#define SUKISU_KPM_LIST 4
|
|
||||||
#define SUKISU_KPM_INFO 5
|
|
||||||
#define SUKISU_KPM_CONTROL 6
|
|
||||||
#define SUKISU_KPM_PRINT 7
|
|
||||||
|
|
||||||
#define CONTROL_CODE(n) (CMD_KPM_CONTROL + n - 1)
|
// prctl(xxx, 29, "NAME")
|
||||||
|
// success return 0, error return -N
|
||||||
|
#define SUKISU_KPM_UNLOAD 29
|
||||||
|
|
||||||
|
// num = prctl(xxx, 30)
|
||||||
|
// error return -N
|
||||||
|
// success return +num or 0
|
||||||
|
#define SUKISU_KPM_NUM 30
|
||||||
|
|
||||||
|
// prctl(xxx, 31, Buffer, BufferSize)
|
||||||
|
// success return +out, error return -N
|
||||||
|
#define SUKISU_KPM_LIST 31
|
||||||
|
|
||||||
|
// prctl(xxx, 32, "NAME", Buffer[256])
|
||||||
|
// success return +out, error return -N
|
||||||
|
#define SUKISU_KPM_INFO 32
|
||||||
|
|
||||||
|
// prctl(xxx, 33, "NAME", "ARGS")
|
||||||
|
// success return KPM's result value
|
||||||
|
// error return -N
|
||||||
|
#define SUKISU_KPM_CONTROL 33
|
||||||
|
|
||||||
|
// prctl(xxx, 34, buffer, bufferSize)
|
||||||
|
// success return KPM's result value
|
||||||
|
// error return -N
|
||||||
|
#define SUKISU_KPM_VERSION 34
|
||||||
|
|
||||||
|
#define CONTROL_CODE(n) (n)
|
||||||
|
|
||||||
void print_usage(const char *prog) {
|
void print_usage(const char *prog) {
|
||||||
printf("Usage: %s <command> [args]\n", prog);
|
printf("Usage: %s <command> [args]\n", prog);
|
||||||
@@ -32,7 +55,7 @@ void print_usage(const char *prog) {
|
|||||||
printf(" list List loaded KPM modules\n");
|
printf(" list List loaded KPM modules\n");
|
||||||
printf(" info <name> Get info of a KPM module\n");
|
printf(" info <name> Get info of a KPM module\n");
|
||||||
printf(" control <name> <args> Send control command to a KPM module\n");
|
printf(" control <name> <args> Send control command to a KPM module\n");
|
||||||
printf(" print Print KPM module list to stdout\n");
|
printf(" version Print KPM Loader version\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char *argv[]) {
|
int main(int argc, char *argv[]) {
|
||||||
@@ -47,6 +70,9 @@ int main(int argc, char *argv[]) {
|
|||||||
if (strcmp(argv[1], "load") == 0 && argc >= 3) {
|
if (strcmp(argv[1], "load") == 0 && argc >= 3) {
|
||||||
// 加载 KPM 模块
|
// 加载 KPM 模块
|
||||||
ret = prctl(KSU_OPTIONS, CONTROL_CODE(SUKISU_KPM_LOAD), argv[2], (argc > 3 ? argv[3] : NULL), &out);
|
ret = prctl(KSU_OPTIONS, CONTROL_CODE(SUKISU_KPM_LOAD), argv[2], (argc > 3 ? argv[3] : NULL), &out);
|
||||||
|
if(out > 0) {
|
||||||
|
printf("Success");
|
||||||
|
}
|
||||||
} else if (strcmp(argv[1], "unload") == 0 && argc >= 3) {
|
} else if (strcmp(argv[1], "unload") == 0 && argc >= 3) {
|
||||||
// 卸载 KPM 模块
|
// 卸载 KPM 模块
|
||||||
ret = prctl(KSU_OPTIONS, CONTROL_CODE(SUKISU_KPM_UNLOAD), argv[2], NULL, &out);
|
ret = prctl(KSU_OPTIONS, CONTROL_CODE(SUKISU_KPM_UNLOAD), argv[2], NULL, &out);
|
||||||
@@ -59,22 +85,25 @@ int main(int argc, char *argv[]) {
|
|||||||
// 获取模块列表
|
// 获取模块列表
|
||||||
char buffer[1024] = {0};
|
char buffer[1024] = {0};
|
||||||
ret = prctl(KSU_OPTIONS, CONTROL_CODE(SUKISU_KPM_LIST), buffer, sizeof(buffer), &out);
|
ret = prctl(KSU_OPTIONS, CONTROL_CODE(SUKISU_KPM_LIST), buffer, sizeof(buffer), &out);
|
||||||
if (ret >= 0) {
|
if (out >= 0) {
|
||||||
printf("%s", buffer);
|
printf("%s", buffer);
|
||||||
}
|
}
|
||||||
} else if (strcmp(argv[1], "info") == 0 && argc >= 3) {
|
} else if (strcmp(argv[1], "info") == 0 && argc >= 3) {
|
||||||
// 获取指定模块信息
|
// 获取指定模块信息
|
||||||
char buffer[256] = {0};
|
char buffer[256] = {0};
|
||||||
ret = prctl(KSU_OPTIONS, CONTROL_CODE(SUKISU_KPM_INFO), argv[2], buffer, &out);
|
ret = prctl(KSU_OPTIONS, CONTROL_CODE(SUKISU_KPM_INFO), argv[2], buffer, &out);
|
||||||
if (ret >= 0) {
|
if (out >= 0) {
|
||||||
printf("%s\n", buffer);
|
printf("%s\n", buffer);
|
||||||
}
|
}
|
||||||
} else if (strcmp(argv[1], "control") == 0 && argc >= 4) {
|
} else if (strcmp(argv[1], "control") == 0 && argc >= 4) {
|
||||||
// 控制 KPM 模块
|
// 控制 KPM 模块
|
||||||
ret = prctl(KSU_OPTIONS, CONTROL_CODE(SUKISU_KPM_CONTROL), argv[2], argv[3], &out);
|
ret = prctl(KSU_OPTIONS, CONTROL_CODE(SUKISU_KPM_CONTROL), argv[2], argv[3], &out);
|
||||||
} else if (strcmp(argv[1], "print") == 0) {
|
} else if (strcmp(argv[1], "version") == 0) {
|
||||||
// 在 stdout 输出 KPM 列表
|
char buffer[1024] = {0};
|
||||||
ret = prctl(KSU_OPTIONS, CONTROL_CODE(SUKISU_KPM_PRINT), NULL, NULL, &out);
|
ret = prctl(KSU_OPTIONS, CONTROL_CODE(SUKISU_KPM_VERSION), buffer, sizeof(buffer), &out);
|
||||||
|
if (out >= 0) {
|
||||||
|
printf("%s", buffer);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
print_usage(argv[0]);
|
print_usage(argv[0]);
|
||||||
return 1;
|
return 1;
|
||||||
|
|||||||
2
userspace/ksud/Cargo.lock
generated
2
userspace/ksud/Cargo.lock
generated
@@ -784,7 +784,7 @@ dependencies = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zakomk"
|
name = "zakozako"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"android-properties",
|
"android-properties",
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "zakomk"
|
name = "zakozako"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
notify = "6.1"
|
||||||
anyhow = "1"
|
anyhow = "1"
|
||||||
clap = { version = "4", features = ["derive"] }
|
clap = { version = "4", features = ["derive"] }
|
||||||
const_format = "0.2"
|
const_format = "0.2"
|
||||||
|
|||||||
@@ -1,5 +1,10 @@
|
|||||||
____ _ _ _ ____ _____ _____
|
____ _ _ ____ _ _
|
||||||
/ ___|| |__(_) |__ | ___|| ____|_ _|
|
/ ___| _ _| | _(_) ___|| | | |
|
||||||
\___ \| '_ \ | '_ \|___ \| _| | |
|
\___ \| | | | |/ / \___ \| | | |
|
||||||
___) | | | | | | | ___) | |___ | |
|
___) | |_| | <| |___) | |_| |
|
||||||
|____/|_| |_|_| |_| |____/|_____| |_|
|
|____/ \__,_|_|\_\_|____/ \___/
|
||||||
|
_ _ _ _
|
||||||
|
| | | | | |_ _ __ __ _
|
||||||
|
| | | | | __| '__/ _\ |
|
||||||
|
| |_| | | |_| | | (_| |
|
||||||
|
\___/|_|\__|_| \__,_|
|
||||||
@@ -5,10 +5,13 @@ use anyhow::{Context, Result};
|
|||||||
use log::{info, warn};
|
use log::{info, warn};
|
||||||
use rustix::fs::{MountFlags, mount};
|
use rustix::fs::{MountFlags, mount};
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
use crate::kpm;
|
||||||
|
|
||||||
pub fn on_post_data_fs() -> Result<()> {
|
pub fn on_post_data_fs() -> Result<()> {
|
||||||
ksucalls::report_post_fs_data();
|
ksucalls::report_post_fs_data();
|
||||||
|
|
||||||
|
kpm::start_kpm_watcher()?;
|
||||||
|
|
||||||
utils::umask(0);
|
utils::umask(0);
|
||||||
|
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
@@ -98,6 +101,9 @@ pub fn on_post_data_fs() -> Result<()> {
|
|||||||
|
|
||||||
run_stage("post-mount", true);
|
run_stage("post-mount", true);
|
||||||
|
|
||||||
|
// load kpm modules
|
||||||
|
kpm::load_kpm_modules()?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
181
userspace/ksud/src/kpm.rs
Normal file
181
userspace/ksud/src/kpm.rs
Normal file
@@ -0,0 +1,181 @@
|
|||||||
|
use anyhow::Result;
|
||||||
|
use notify::{Watcher, RecursiveMode};
|
||||||
|
use std::path::Path;
|
||||||
|
use std::fs;
|
||||||
|
use anyhow::anyhow;
|
||||||
|
use std::ffi::OsStr;
|
||||||
|
|
||||||
|
pub const KPM_DIR: &str = "/data/adb/kpm";
|
||||||
|
pub const KPMMGR_PATH: &str = "/data/adb/ksu/bin/kpmmgr";
|
||||||
|
|
||||||
|
// 确保 KPM 目录存在,如果不存在则创建
|
||||||
|
pub fn ensure_kpm_dir() -> Result<()> {
|
||||||
|
if !Path::new(KPM_DIR).exists() {
|
||||||
|
fs::create_dir_all(KPM_DIR)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn start_kpm_watcher() -> Result<()> {
|
||||||
|
ensure_kpm_dir()?;
|
||||||
|
|
||||||
|
// 检查是否处于安全模式
|
||||||
|
if crate::utils::is_safe_mode() {
|
||||||
|
log::warn!("The system is in safe mode and is deleting all KPM modules...");
|
||||||
|
if let Err(e) = remove_all_kpms() {
|
||||||
|
log::error!("Error deleting all KPM modules: {}", e);
|
||||||
|
}
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut watcher = notify::recommended_watcher(|res| {
|
||||||
|
match res {
|
||||||
|
Ok(event) => handle_kpm_event(event),
|
||||||
|
Err(e) => log::error!("monitoring error: {:?}", e),
|
||||||
|
}
|
||||||
|
})?;
|
||||||
|
|
||||||
|
watcher.watch(Path::new(KPM_DIR), RecursiveMode::NonRecursive)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理 KPM 事件
|
||||||
|
pub fn handle_kpm_event(event: notify::Event) {
|
||||||
|
match event.kind {
|
||||||
|
notify::EventKind::Create(_) => handle_create_event(event.paths),
|
||||||
|
notify::EventKind::Remove(_) => handle_remove_event(event.paths),
|
||||||
|
notify::EventKind::Modify(_) => handle_modify_event(event.paths),
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_create_event(paths: Vec<std::path::PathBuf>) {
|
||||||
|
for path in paths {
|
||||||
|
if path.extension() == Some(OsStr::new("kpm")) {
|
||||||
|
if let Err(e) = load_kpm(&path) {
|
||||||
|
log::warn!("Failed to load {}: {}", path.display(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_remove_event(paths: Vec<std::path::PathBuf>) {
|
||||||
|
for path in paths {
|
||||||
|
if let Some(name) = path.file_stem().and_then(|s| s.to_str()) {
|
||||||
|
if let Err(e) = unload_kpm(name) {
|
||||||
|
log::warn!("Failed to unload {}: {}", name, e);
|
||||||
|
}
|
||||||
|
if let Err(e) = fs::remove_file(&path) {
|
||||||
|
log::error!("Failed to delete file: {}: {}", path.display(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_modify_event(paths: Vec<std::path::PathBuf>) {
|
||||||
|
for path in paths {
|
||||||
|
log::info!("Modified file: {}", path.display());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 加载 KPM 模块
|
||||||
|
pub fn load_kpm(path: &Path) -> Result<()> {
|
||||||
|
let path_str = path.to_str().ok_or_else(|| anyhow!("Invalid path: {}", path.display()))?;
|
||||||
|
let status = std::process::Command::new(KPMMGR_PATH)
|
||||||
|
.args(["load", path_str, ""])
|
||||||
|
.status()?;
|
||||||
|
|
||||||
|
if status.success() {
|
||||||
|
log::info!("Loaded KPM: {}", path.display());
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
// 卸载 KPM 模块并尝试删除对应文件
|
||||||
|
pub fn unload_kpm(name: &str) -> Result<()> {
|
||||||
|
let status = std::process::Command::new(KPMMGR_PATH)
|
||||||
|
.args(["unload", name])
|
||||||
|
.status()
|
||||||
|
.map_err(|e| anyhow!("Failed to execute kpmmgr: {}", e))?;
|
||||||
|
|
||||||
|
if status.success() {
|
||||||
|
let kpm_path = find_kpm_file(name)?;
|
||||||
|
if let Some(path) = kpm_path {
|
||||||
|
fs::remove_file(&path)
|
||||||
|
.map_err(|e| anyhow!("Failed to delete KPM file: {}: {}", path.display(), e))?;
|
||||||
|
log::info!("Deleted KPM file: {}", path.display());
|
||||||
|
}
|
||||||
|
|
||||||
|
log::info!("Successfully unloaded KPM: {}", name);
|
||||||
|
} else {
|
||||||
|
log::warn!("KPM unloading may have failed: {}", name);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
// 通过名称查找 KPM 文件
|
||||||
|
fn find_kpm_file(name: &str) -> Result<Option<std::path::PathBuf>> {
|
||||||
|
let kpm_dir = Path::new(KPM_DIR);
|
||||||
|
if !kpm_dir.exists() {
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
|
|
||||||
|
for entry in fs::read_dir(kpm_dir)? {
|
||||||
|
let path = entry?.path();
|
||||||
|
if let Some(file_name) = path.file_stem() {
|
||||||
|
if let Some(file_name_str) = file_name.to_str() {
|
||||||
|
if file_name_str == name && path.extension() == Some(OsStr::new("kpm")) {
|
||||||
|
return Ok(Some(path));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 安全模式下删除所有 KPM 模块
|
||||||
|
pub fn remove_all_kpms() -> Result<()> {
|
||||||
|
ensure_kpm_dir()?;
|
||||||
|
|
||||||
|
for entry in fs::read_dir(KPM_DIR)? {
|
||||||
|
let path = entry?.path();
|
||||||
|
if path.extension().is_some_and(|ext| ext == "kpm") {
|
||||||
|
if let Some(name) = path.file_stem() {
|
||||||
|
if let Err(e) = unload_kpm(name.to_string_lossy().as_ref()) {
|
||||||
|
log::error!("Failed to remove KPM: {}", e);
|
||||||
|
}
|
||||||
|
if let Err(e) = fs::remove_file(&path) {
|
||||||
|
log::error!("Failed to delete file: {}: {}", path.display(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
// 加载 KPM 模块
|
||||||
|
pub fn load_kpm_modules() -> Result<()> {
|
||||||
|
ensure_kpm_dir()?;
|
||||||
|
|
||||||
|
for entry in std::fs::read_dir(KPM_DIR)? {
|
||||||
|
let path = entry?.path();
|
||||||
|
if let Some(file_name) = path.file_stem() {
|
||||||
|
if let Some(file_name_str) = file_name.to_str() {
|
||||||
|
if file_name_str.is_empty() {
|
||||||
|
log::warn!("Invalid KPM file name: {}", path.display());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if path.extension().is_some_and(|ext| ext == "kpm") {
|
||||||
|
match load_kpm(&path) {
|
||||||
|
Ok(()) => log::info!("Successfully loaded KPM module: {}", path.display()),
|
||||||
|
Err(e) => log::warn!("Failed to load KPM module {}: {}", path.display(), e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
@@ -14,6 +14,7 @@ mod restorecon;
|
|||||||
mod sepolicy;
|
mod sepolicy;
|
||||||
mod su;
|
mod su;
|
||||||
mod utils;
|
mod utils;
|
||||||
|
mod kpm;
|
||||||
|
|
||||||
fn main() -> anyhow::Result<()> {
|
fn main() -> anyhow::Result<()> {
|
||||||
cli::run()
|
cli::run()
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
LOCAL_PATH := $(call my-dir)
|
LOCAL_PATH := $(call my-dir)
|
||||||
|
|
||||||
include $(CLEAR_VARS)
|
include $(CLEAR_VARS)
|
||||||
LOCAL_MODULE := zakomksd
|
LOCAL_MODULE := zakozakozako
|
||||||
LOCAL_SRC_FILES := zakomksd.c
|
LOCAL_SRC_FILES := susfs.c
|
||||||
include $(BUILD_EXECUTABLE)
|
include $(BUILD_EXECUTABLE)
|
||||||
Reference in New Issue
Block a user