You've already forked KernelSU
mirror of
https://github.com/tiann/KernelSU.git
synced 2025-08-27 23:46:34 +00:00
Compare commits
1 Commits
dependabot
...
add-device
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
01fe801ea7 |
1
.github/FUNDING.yml
vendored
1
.github/FUNDING.yml
vendored
@@ -2,3 +2,4 @@
|
||||
|
||||
github: tiann
|
||||
patreon: weishu
|
||||
custom: https://vxposed.com/donate.html
|
||||
|
||||
3
.github/workflows/add-device.yml
vendored
3
.github/workflows/add-device.yml
vendored
@@ -26,7 +26,7 @@ jobs:
|
||||
- name: Make pull request
|
||||
if: steps.handle-add-device.outputs.success == 'true'
|
||||
id: cpr
|
||||
uses: peter-evans/create-pull-request@v7
|
||||
uses: peter-evans/create-pull-request@v6
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
commit-message: "[add device]: ${{ steps.handle-add-device.outputs.device }}"
|
||||
@@ -37,7 +37,6 @@ jobs:
|
||||
branch: "add-device-${{ github.event.issue.number }}"
|
||||
labels: add-device
|
||||
delete-branch: true
|
||||
sign-commits: true
|
||||
- name: Check outputs
|
||||
if: ${{ steps.cpr.outputs.pull-request-number }}
|
||||
run: |
|
||||
|
||||
2
.github/workflows/avd-kernel.yml
vendored
2
.github/workflows/avd-kernel.yml
vendored
@@ -37,7 +37,7 @@ on:
|
||||
jobs:
|
||||
build:
|
||||
name: Build ${{ inputs.version_name }}
|
||||
runs-on: ubuntu-22.04
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Maximize build space
|
||||
uses: easimon/maximize-build-space@master
|
||||
|
||||
36
.github/workflows/build-debug-kernel.yml
vendored
36
.github/workflows/build-debug-kernel.yml
vendored
@@ -7,9 +7,9 @@ jobs:
|
||||
uses: ./.github/workflows/gki-kernel.yml
|
||||
with:
|
||||
version: android12-5.10
|
||||
version_name: android12-5.10.226
|
||||
tag: android12-5.10-2024-11
|
||||
os_patch_level: 2024-11
|
||||
version_name: android12-5.10.209
|
||||
tag: android12-5.10-2024-05
|
||||
os_patch_level: 2024-05
|
||||
patch_path: "5.10"
|
||||
debug: true
|
||||
build-debug-kernel-a13:
|
||||
@@ -17,11 +17,11 @@ jobs:
|
||||
matrix:
|
||||
include:
|
||||
- version: "5.10"
|
||||
sub_level: 223
|
||||
os_patch_level: 2024-11
|
||||
sub_level: 209
|
||||
os_patch_level: 2024-05
|
||||
- version: "5.15"
|
||||
sub_level: 167
|
||||
os_patch_level: 2024-11
|
||||
sub_level: 148
|
||||
os_patch_level: 2024-05
|
||||
uses: ./.github/workflows/gki-kernel.yml
|
||||
with:
|
||||
version: android13-${{ matrix.version }}
|
||||
@@ -34,29 +34,15 @@ jobs:
|
||||
matrix:
|
||||
include:
|
||||
- version: "5.15"
|
||||
sub_level: 167
|
||||
os_patch_level: 2024-11
|
||||
sub_level: 148
|
||||
os_patch_level: 2024-05
|
||||
- version: "6.1"
|
||||
sub_level: 115
|
||||
os_patch_level: 2024-12
|
||||
sub_level: 75
|
||||
os_patch_level: 2024-05
|
||||
uses: ./.github/workflows/gki-kernel.yml
|
||||
with:
|
||||
version: android14-${{ matrix.version }}
|
||||
version_name: android14-${{ matrix.version }}.${{ matrix.sub_level }}
|
||||
tag: android14-${{ matrix.version }}-${{ matrix.os_patch_level }}
|
||||
patch_path: ${{ matrix.version }}
|
||||
debug: true
|
||||
build-debug-kernel-a15:
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- version: "6.6"
|
||||
sub_level: 57
|
||||
os_patch_level: 2024-12
|
||||
uses: ./.github/workflows/gki-kernel.yml
|
||||
with:
|
||||
version: android15-${{ matrix.version }}
|
||||
version_name: android15-${{ matrix.version }}.${{ matrix.sub_level }}
|
||||
tag: android15-${{ matrix.version }}-${{ matrix.os_patch_level }}
|
||||
patch_path: ${{ matrix.version }}
|
||||
debug: true
|
||||
20
.github/workflows/build-kernel-a12.yml
vendored
20
.github/workflows/build-kernel-a12.yml
vendored
@@ -21,16 +21,12 @@ jobs:
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- sub_level: 198
|
||||
os_patch_level: 2024-01
|
||||
- sub_level: 205
|
||||
os_patch_level: 2024-03
|
||||
- sub_level: 209
|
||||
os_patch_level: 2024-05
|
||||
- sub_level: 218
|
||||
os_patch_level: 2024-08
|
||||
- sub_level: 226
|
||||
os_patch_level: 2024-11
|
||||
- sub_level: 233
|
||||
os_patch_level: 2025-02
|
||||
- sub_level: 236
|
||||
os_patch_level: 2025-05
|
||||
uses: ./.github/workflows/gki-kernel.yml
|
||||
secrets: inherit
|
||||
with:
|
||||
@@ -67,7 +63,7 @@ jobs:
|
||||
- name: Download prebuilt toolchain
|
||||
run: |
|
||||
AOSP_MIRROR=https://android.googlesource.com
|
||||
BRANCH=main-kernel-build-2024
|
||||
BRANCH=main-kernel-build-2023
|
||||
git clone $AOSP_MIRROR/platform/prebuilts/build-tools -b $BRANCH --depth 1 build-tools
|
||||
git clone $AOSP_MIRROR/kernel/prebuilts/build-tools -b $BRANCH --depth 1 kernel-build-tools
|
||||
git clone $AOSP_MIRROR/platform/system/tools/mkbootimg -b $BRANCH --depth 1
|
||||
@@ -116,7 +112,7 @@ jobs:
|
||||
uses: ./.github/workflows/gki-kernel.yml
|
||||
with:
|
||||
version: android12-5.10
|
||||
version_name: android12-5.10.223
|
||||
tag: android12-5.10-2024-11
|
||||
os_patch_level: 2024-11
|
||||
version_name: android12-5.10.209
|
||||
tag: android12-5.10-2024-05
|
||||
os_patch_level: 2024-05
|
||||
patch_path: "5.10"
|
||||
67
.github/workflows/build-kernel-a13.yml
vendored
67
.github/workflows/build-kernel-a13.yml
vendored
@@ -21,51 +21,30 @@ jobs:
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- version: "5.10"
|
||||
sub_level: 189
|
||||
os_patch_level: 2023-11
|
||||
- version: "5.10"
|
||||
sub_level: 198
|
||||
os_patch_level: 2024-01
|
||||
- version: "5.10"
|
||||
sub_level: 205
|
||||
os_patch_level: 2024-03
|
||||
- version: "5.10"
|
||||
sub_level: 209
|
||||
os_patch_level: 2024-05
|
||||
- version: "5.10"
|
||||
sub_level: 210
|
||||
os_patch_level: 2024-06
|
||||
- version: "5.10"
|
||||
sub_level: 214
|
||||
os_patch_level: 2024-07
|
||||
- version: "5.10"
|
||||
sub_level: 218
|
||||
os_patch_level: 2024-08
|
||||
- version: "5.10"
|
||||
sub_level: 223
|
||||
os_patch_level: 2024-11
|
||||
- version: "5.10"
|
||||
sub_level: 228
|
||||
os_patch_level: 2025-01
|
||||
- version: "5.10"
|
||||
sub_level: 234
|
||||
os_patch_level: 2025-03
|
||||
- version: "5.15"
|
||||
sub_level: 123
|
||||
os_patch_level: 2023-11
|
||||
- version: "5.15"
|
||||
sub_level: 137
|
||||
os_patch_level: 2024-01
|
||||
- version: "5.15"
|
||||
sub_level: 144
|
||||
os_patch_level: 2024-03
|
||||
- version: "5.15"
|
||||
sub_level: 148
|
||||
os_patch_level: 2024-05
|
||||
- version: "5.15"
|
||||
sub_level: 149
|
||||
os_patch_level: 2024-07
|
||||
- version: "5.15"
|
||||
sub_level: 151
|
||||
os_patch_level: 2024-08
|
||||
- version: "5.15"
|
||||
sub_level: 153
|
||||
os_patch_level: 2024-09
|
||||
- version: "5.15"
|
||||
sub_level: 167
|
||||
os_patch_level: 2024-11
|
||||
- version: "5.15"
|
||||
sub_level: 178
|
||||
os_patch_level: 2024-11
|
||||
- version: "5.15"
|
||||
sub_level: 170
|
||||
os_patch_level: 2025-01
|
||||
- version: "5.15"
|
||||
sub_level: 178
|
||||
os_patch_level: 2025-03
|
||||
uses: ./.github/workflows/gki-kernel.yml
|
||||
secrets: inherit
|
||||
with:
|
||||
@@ -102,7 +81,7 @@ jobs:
|
||||
- name: Download prebuilt toolchain
|
||||
run: |
|
||||
AOSP_MIRROR=https://android.googlesource.com
|
||||
BRANCH=main-kernel-build-2024
|
||||
BRANCH=main-kernel-build-2023
|
||||
git clone $AOSP_MIRROR/platform/prebuilts/build-tools -b $BRANCH --depth 1 build-tools
|
||||
git clone $AOSP_MIRROR/kernel/prebuilts/build-tools -b $BRANCH --depth 1 kernel-build-tools
|
||||
git clone $AOSP_MIRROR/platform/system/tools/mkbootimg -b $BRANCH --depth 1
|
||||
@@ -152,11 +131,11 @@ jobs:
|
||||
matrix:
|
||||
include:
|
||||
- version: "5.10"
|
||||
sub_level: 223
|
||||
os_patch_level: 2024-11
|
||||
sub_level: 209
|
||||
os_patch_level: 2024-05
|
||||
- version: "5.15"
|
||||
sub_level: 167
|
||||
os_patch_level: 2024-11
|
||||
sub_level: 148
|
||||
os_patch_level: 2024-05
|
||||
uses: ./.github/workflows/gki-kernel.yml
|
||||
with:
|
||||
version: android13-${{ matrix.version }}
|
||||
|
||||
82
.github/workflows/build-kernel-a14.yml
vendored
82
.github/workflows/build-kernel-a14.yml
vendored
@@ -21,63 +21,33 @@ jobs:
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- version: "5.15"
|
||||
sub_level: 131
|
||||
os_patch_level: 2023-11
|
||||
- version: "5.15"
|
||||
sub_level: 137
|
||||
os_patch_level: 2024-01
|
||||
- version: "5.15"
|
||||
sub_level: 144
|
||||
os_patch_level: 2024-03
|
||||
- version: "5.15"
|
||||
sub_level: 148
|
||||
os_patch_level: 2024-05
|
||||
- version: "5.15"
|
||||
sub_level: 149
|
||||
os_patch_level: 2024-06
|
||||
- version: "5.15"
|
||||
sub_level: 153
|
||||
os_patch_level: 2024-07
|
||||
- version: "5.15"
|
||||
sub_level: 158
|
||||
os_patch_level: 2024-08
|
||||
- version: "5.15"
|
||||
sub_level: 164
|
||||
os_patch_level: 2024-09
|
||||
- version: "5.15"
|
||||
sub_level: 167
|
||||
os_patch_level: 2024-11
|
||||
- version: "5.15"
|
||||
sub_level: 170
|
||||
os_patch_level: 2025-01
|
||||
- version: "5.15"
|
||||
sub_level: 178
|
||||
os_patch_level: 2025-03
|
||||
- version: "6.1"
|
||||
sub_level: 25
|
||||
os_patch_level: 2023-10
|
||||
- version: "6.1"
|
||||
sub_level: 43
|
||||
os_patch_level: 2023-11
|
||||
- version: "6.1"
|
||||
sub_level: 57
|
||||
os_patch_level: 2024-01
|
||||
- version: "6.1"
|
||||
sub_level: 68
|
||||
os_patch_level: 2024-03
|
||||
- version: "6.1"
|
||||
sub_level: 75
|
||||
os_patch_level: 2024-05
|
||||
- version: "6.1"
|
||||
sub_level: 78
|
||||
os_patch_level: 2024-06
|
||||
- version: "6.1"
|
||||
sub_level: 84
|
||||
os_patch_level: 2024-07
|
||||
- version: "6.1"
|
||||
sub_level: 90
|
||||
os_patch_level: 2024-08
|
||||
- version: "6.1"
|
||||
sub_level: 93
|
||||
os_patch_level: 2024-09
|
||||
- version: "6.1"
|
||||
sub_level: 99
|
||||
os_patch_level: 2024-10
|
||||
- version: "6.1"
|
||||
sub_level: 112
|
||||
os_patch_level: 2024-11
|
||||
- version: "6.1"
|
||||
sub_level: 115
|
||||
os_patch_level: 2024-12
|
||||
- version: "6.1"
|
||||
sub_level: 118
|
||||
os_patch_level: 2025-01
|
||||
- version: "6.1"
|
||||
sub_level: 128
|
||||
os_patch_level: 2025-03
|
||||
- version: "6.1"
|
||||
sub_level: 134
|
||||
os_patch_level: 2025-05
|
||||
uses: ./.github/workflows/gki-kernel.yml
|
||||
secrets: inherit
|
||||
with:
|
||||
@@ -114,7 +84,7 @@ jobs:
|
||||
- name: Download prebuilt toolchain
|
||||
run: |
|
||||
AOSP_MIRROR=https://android.googlesource.com
|
||||
BRANCH=main-kernel-build-2024
|
||||
BRANCH=main-kernel-build-2023
|
||||
git clone $AOSP_MIRROR/platform/prebuilts/build-tools -b $BRANCH --depth 1 build-tools
|
||||
git clone $AOSP_MIRROR/kernel/prebuilts/build-tools -b $BRANCH --depth 1 kernel-build-tools
|
||||
git clone $AOSP_MIRROR/platform/system/tools/mkbootimg -b $BRANCH --depth 1
|
||||
@@ -164,11 +134,11 @@ jobs:
|
||||
matrix:
|
||||
include:
|
||||
- version: "5.15"
|
||||
sub_level: 167
|
||||
os_patch_level: 2024-11
|
||||
sub_level: 148
|
||||
os_patch_level: 2024-05
|
||||
- version: "6.1"
|
||||
sub_level: 115
|
||||
os_patch_level: 2024-12
|
||||
sub_level: 75
|
||||
os_patch_level: 2024-05
|
||||
uses: ./.github/workflows/gki-kernel.yml
|
||||
with:
|
||||
version: android14-${{ matrix.version }}
|
||||
|
||||
35
.github/workflows/build-kernel-a15.yml
vendored
35
.github/workflows/build-kernel-a15.yml
vendored
@@ -23,34 +23,7 @@ jobs:
|
||||
include:
|
||||
- version: "6.6"
|
||||
sub_level: 30
|
||||
os_patch_level: 2024-08
|
||||
- version: "6.6"
|
||||
sub_level: 46
|
||||
os_patch_level: 2024-09
|
||||
- version: "6.6"
|
||||
sub_level: 50
|
||||
os_patch_level: 2024-10
|
||||
- version: "6.6"
|
||||
sub_level: 56
|
||||
os_patch_level: 2024-11
|
||||
- version: "6.6"
|
||||
sub_level: 57
|
||||
os_patch_level: 2024-12
|
||||
- version: "6.6"
|
||||
sub_level: 58
|
||||
os_patch_level: 2025-01
|
||||
- version: "6.6"
|
||||
sub_level: 66
|
||||
os_patch_level: 2025-02
|
||||
- version: "6.6"
|
||||
sub_level: 77
|
||||
os_patch_level: 2025-03
|
||||
- version: "6.6"
|
||||
sub_level: 82
|
||||
os_patch_level: 2025-04
|
||||
- version: "6.6"
|
||||
sub_level: 87
|
||||
os_patch_level: 2025-05
|
||||
os_patch_level: 2024-07
|
||||
uses: ./.github/workflows/gki-kernel.yml
|
||||
secrets: inherit
|
||||
with:
|
||||
@@ -137,12 +110,12 @@ jobs:
|
||||
matrix:
|
||||
include:
|
||||
- version: "6.6"
|
||||
sub_level: 57
|
||||
os_patch_level: 2024-12
|
||||
sub_level: 30
|
||||
os_patch_level: 2024-07
|
||||
uses: ./.github/workflows/gki-kernel.yml
|
||||
with:
|
||||
version: android15-${{ matrix.version }}
|
||||
version_name: android15-${{ matrix.version }}.${{ matrix.sub_level }}
|
||||
tag: android15-${{ matrix.version }}-${{ matrix.os_patch_level }}
|
||||
os_patch_level: ${{ matrix.os_patch_level }}
|
||||
patch_path: ${{ matrix.version }}
|
||||
patch_path: ${{ matrix.version }}
|
||||
|
||||
4
.github/workflows/build-kernel-arcvm.yml
vendored
4
.github/workflows/build-kernel-arcvm.yml
vendored
@@ -32,7 +32,7 @@ jobs:
|
||||
defconfig: arm64_arcvm_defconfig
|
||||
|
||||
name: Build ChromeOS ARCVM kernel
|
||||
runs-on: ubuntu-22.04
|
||||
runs-on: ubuntu-20.04
|
||||
env:
|
||||
LTO: thin
|
||||
ROOT_DIR: /
|
||||
@@ -50,7 +50,7 @@ jobs:
|
||||
|
||||
sudo ln -s --force python3 /usr/bin/python
|
||||
|
||||
export LLVM_VERSION=14
|
||||
export LLVM_VERSION=12
|
||||
wget https://apt.llvm.org/llvm.sh
|
||||
chmod +x llvm.sh
|
||||
sudo ./llvm.sh $LLVM_VERSION
|
||||
|
||||
24
.github/workflows/build-lkm.yml
vendored
24
.github/workflows/build-lkm.yml
vendored
@@ -15,23 +15,23 @@ jobs:
|
||||
matrix:
|
||||
include:
|
||||
- version: "android12-5.10"
|
||||
sub_level: 236
|
||||
os_patch_level: 2025-05
|
||||
sub_level: 209
|
||||
os_patch_level: 2024-05
|
||||
- version: "android13-5.10"
|
||||
sub_level: 234
|
||||
os_patch_level: 2025-03
|
||||
sub_level: 209
|
||||
os_patch_level: 2024-05
|
||||
- version: "android13-5.15"
|
||||
sub_level: 178
|
||||
os_patch_level: 2025-03
|
||||
sub_level: 148
|
||||
os_patch_level: 2024-05
|
||||
- version: "android14-5.15"
|
||||
sub_level: 178
|
||||
os_patch_level: 2025-03
|
||||
sub_level: 148
|
||||
os_patch_level: 2024-05
|
||||
- version: "android14-6.1"
|
||||
sub_level: 134
|
||||
os_patch_level: 2025-05
|
||||
sub_level: 75
|
||||
os_patch_level: 2024-05
|
||||
- version: "android15-6.6"
|
||||
sub_level: 87
|
||||
os_patch_level: 2025-05
|
||||
sub_level: 30
|
||||
os_patch_level: 2024-07
|
||||
uses: ./.github/workflows/gki-kernel.yml
|
||||
with:
|
||||
version: ${{ matrix.version }}
|
||||
|
||||
6
.github/workflows/clippy.yml
vendored
6
.github/workflows/clippy.yml
vendored
@@ -22,16 +22,16 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- run: rustup update stable
|
||||
- run: rustup update --force-non-host stable-x86_64-unknown-linux-gnu
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
workspaces: userspace/ksud
|
||||
|
||||
- name: Install cross
|
||||
run: |
|
||||
RUSTFLAGS="" cargo install cross --git https://github.com/cross-rs/cross --rev 66845c1
|
||||
cargo install cross --git https://github.com/cross-rs/cross --rev 66845c1
|
||||
|
||||
- name: Run clippy
|
||||
run: |
|
||||
cross clippy --manifest-path userspace/ksud/Cargo.toml --target aarch64-linux-android --release
|
||||
cross clippy --manifest-path userspace/ksud/Cargo.toml --target x86_64-linux-android --release
|
||||
cross clippy --manifest-path userspace/ksud/Cargo.toml --target x86_64-linux-android --release
|
||||
|
||||
4
.github/workflows/deploy-website.yml
vendored
4
.github/workflows/deploy-website.yml
vendored
@@ -37,7 +37,7 @@ jobs:
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: latest
|
||||
node-version: 20
|
||||
cache: yarn # or pnpm / yarn
|
||||
cache-dependency-path: website/yarn.lock
|
||||
- name: Setup Pages
|
||||
@@ -64,4 +64,4 @@ jobs:
|
||||
steps:
|
||||
- name: Deploy to GitHub Pages
|
||||
id: deployment
|
||||
uses: actions/deploy-pages@v4
|
||||
uses: actions/deploy-pages@v4
|
||||
|
||||
18
.github/workflows/gki-kernel.yml
vendored
18
.github/workflows/gki-kernel.yml
vendored
@@ -131,15 +131,8 @@ jobs:
|
||||
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
|
||||
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"
|
||||
@@ -198,11 +191,6 @@ jobs:
|
||||
- name: Make working directory clean to avoid dirty
|
||||
working-directory: android-kernel
|
||||
run: |
|
||||
# Fix bazel build error
|
||||
if [ -f common/BUILD.bazel ]; then
|
||||
[ -f android/abi_gki_protected_exports_aarch64 ] || sed -i '/^[[:space:]]*"protected_exports_list"[[:space:]]*:[[:space:]]*"android\/abi_gki_protected_exports_aarch64",$/d' common/BUILD.bazel
|
||||
fi
|
||||
|
||||
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"
|
||||
@@ -260,4 +248,4 @@ jobs:
|
||||
if: ${{ inputs.build_lkm == true }}
|
||||
with:
|
||||
name: ${{ inputs.version }}-lkm
|
||||
path: ./output/*_kernelsu.ko
|
||||
path: ./output/*_kernelsu.ko
|
||||
2
.github/workflows/ksud.yml
vendored
2
.github/workflows/ksud.yml
vendored
@@ -45,7 +45,7 @@ jobs:
|
||||
|
||||
- name: Install cross
|
||||
run: |
|
||||
RUSTFLAGS="" cargo install cross --git https://github.com/cross-rs/cross --rev 66845c1
|
||||
cargo install cross --git https://github.com/cross-rs/cross --rev 66845c1
|
||||
|
||||
- name: Build ksud
|
||||
run: CROSS_NO_WARNINGS=0 cross build --target ${{ inputs.target }} --release --manifest-path ./userspace/ksud/Cargo.toml
|
||||
|
||||
16
.github/workflows/release.yml
vendored
16
.github/workflows/release.yml
vendored
@@ -39,20 +39,6 @@ jobs:
|
||||
steps:
|
||||
- name: Download artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
- name: Rename ksud
|
||||
run: |
|
||||
mkdir -p ksud
|
||||
for dir in ./ksud-*; do
|
||||
if [ -d "$dir" ]; then
|
||||
echo "----- Rename $dir -----"
|
||||
ksud_platform_name=$(basename "$dir")
|
||||
find "$dir" -type f -name "ksud" -path "*/release/*" | while read -r ksud_file; do
|
||||
if [ -f "$ksud_file" ]; then
|
||||
mv "$ksud_file" "ksud/$ksud_platform_name"
|
||||
fi
|
||||
done
|
||||
fi
|
||||
done
|
||||
- name: Zip AnyKernel3
|
||||
run: |
|
||||
for dir in AnyKernel3-*; do
|
||||
@@ -93,4 +79,4 @@ jobs:
|
||||
boot-images-*/Image-*/*.img.gz
|
||||
kernel-WSA*.zip
|
||||
kernel-ARCVM*.zip
|
||||
ksud/ksud-*
|
||||
ksud-*
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
**English** | [Español](README_ES.md) | [简体中文](README_CN.md) | [繁體中文](README_TW.md) | [日本語](README_JP.md) | [한국어](README_KR.md) | [Polski](README_PL.md) | [Português (Brasil)](README_PT-BR.md) | [Türkçe](README_TR.md) | [Русский](README_RU.md) | [Tiếng Việt](README_VI.md) | [Indonesia](README_ID.md) | [עברית](README_IW.md) | [हिंदी](README_IN.md) | [Italiano](README_IT.md)
|
||||
**English** | [Español](README_ES.md) | [简体中文](README_CN.md) | [繁體中文](README_TW.md) | [日本語](README_JP.md) | [한국어](README_KR.md) | [Polski](README_PL.md) | [Português (Brasil)](README_PT-BR.md) | [Türkçe](README_TR.md) | [Русский](README_RU.md) | [Tiếng Việt](README_VI.md) | [Indonesia](README_ID.md) | [עברית](README_IW.md) | [हिंदी](README_IN.md)
|
||||
|
||||
# KernelSU
|
||||
|
||||
<img src="https://kernelsu.org/logo.png" style="width: 96px;" alt="logo">
|
||||
|
||||
A kernel-based root solution for Android devices.
|
||||
A Kernel-based root solution for Android devices.
|
||||
|
||||
[](https://github.com/tiann/KernelSU/releases/latest)
|
||||
[](https://hosted.weblate.org/engage/kernelsu)
|
||||
@@ -18,19 +18,19 @@ A kernel-based root solution for Android devices.
|
||||
2. Module system based on [OverlayFS](https://en.wikipedia.org/wiki/OverlayFS).
|
||||
3. [App Profile](https://kernelsu.org/guide/app-profile.html): Lock up the root power in a cage.
|
||||
|
||||
## Compatibility state
|
||||
## Compatibility State
|
||||
|
||||
KernelSU officially supports Android GKI 2.0 devices (kernel 5.10+). Older kernels (4.14+) are also supported, but the kernel will need to be built manually.
|
||||
KernelSU officially supports Android GKI 2.0 devices (kernel 5.10+). Older kernels (4.14+) are also compatible, but the kernel will have to be built manually.
|
||||
|
||||
With this, WSA, ChromeOS, and container-based Android are all supported.
|
||||
|
||||
Currently, only the `arm64-v8a` and `x86_64` architectures are supported.
|
||||
Currently, only `arm64-v8a` and `x86_64` are supported.
|
||||
|
||||
## Usage
|
||||
|
||||
- [Installation](https://kernelsu.org/guide/installation.html)
|
||||
- [How to build](https://kernelsu.org/guide/how-to-build.html)
|
||||
- [Official website](https://kernelsu.org/)
|
||||
- [Installation Instruction](https://kernelsu.org/guide/installation.html)
|
||||
- [How to build?](https://kernelsu.org/guide/how-to-build.html)
|
||||
- [Official Website](https://kernelsu.org/)
|
||||
|
||||
## Translation
|
||||
|
||||
@@ -51,7 +51,7 @@ For information on reporting security vulnerabilities in KernelSU, see [SECURITY
|
||||
|
||||
## Credits
|
||||
|
||||
- [Kernel-Assisted Superuser](https://git.zx2c4.com/kernel-assisted-superuser/about/): The KernelSU idea.
|
||||
- [Magisk](https://github.com/topjohnwu/Magisk): The powerful root tool.
|
||||
- [genuine](https://github.com/brevent/genuine/): APK v2 signature validation.
|
||||
- [Diamorphine](https://github.com/m0nad/Diamorphine): Some rootkit skills.
|
||||
- [kernel-assisted-superuser](https://git.zx2c4.com/kernel-assisted-superuser/about/): the KernelSU idea.
|
||||
- [Magisk](https://github.com/topjohnwu/Magisk): the powerful root tool.
|
||||
- [genuine](https://github.com/brevent/genuine/): apk v2 signature validation.
|
||||
- [Diamorphine](https://github.com/m0nad/Diamorphine): some rootkit skills.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
[English](README.md) | [Español](README_ES.md) | **简体中文** | [繁體中文](README_TW.md) | [日本語](README_JP.md) | [한국어](README_KR.md) | [Polski](README_PL.md) | [Português (Brasil)](README_PT-BR.md) | [Türkçe](README_TR.md) | [Русский](README_RU.md) | [Tiếng Việt](README_VI.md) | [Indonesia](README_ID.md) | [עברית](README_IW.md) | [हिंदी](README_IN.md) | [Italiano](README_IT.md)
|
||||
[English](README.md) | [Español](README_ES.md) | **简体中文** | [繁體中文](README_TW.md) | [日本語](README_JP.md) | [한국어](README_KR.md) | [Polski](README_PL.md) | [Português (Brasil)](README_PT-BR.md) | [Türkçe](README_TR.md) | [Русский](README_RU.md) | [Tiếng Việt](README_VI.md) | [Indonesia](README_ID.md) | [עברית](README_IW.md) | [हिंदी](README_IN.md)
|
||||
|
||||
# KernelSU
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
[English](README.md) | **Español** | [简体中文](README_CN.md) | [繁體中文](README_TW.md) | [日本語](README_JP.md) | [한국어](README_KR.md) | [Polski](README_PL.md) | [Português (Brasil)](README_PT-BR.md) | [Türkçe](README_TR.md) | [Русский](README_RU.md) | [Tiếng Việt](README_VI.md) | [Indonesia](README_ID.md) | [עברית](README_IW.md) | [हिंदी](README_IN.md) | [Italiano](README_IT.md)
|
||||
[English](README.md) | **Español** | [简体中文](README_CN.md) | [繁體中文](README_TW.md) | [日本語](README_JP.md) | [한국어](README_KR.md) | [Polski](README_PL.md) | [Português (Brasil)](README_PT-BR.md) | [Türkçe](README_TR.md) | [Русский](README_RU.md) | [Tiếng Việt](README_VI.md) | [Indonesia](README_ID.md) | [עברית](README_IW.md) | [हिंदी](README_IN.md)
|
||||
|
||||
# KernelSU
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
[English](README.md) | [Español](README_ES.md) | [简体中文](README_CN.md) | [繁體中文](README_TW.md) | [日本語](README_JP.md) | [한국어](README_KR.md) | [Polski](README_PL.md) | [Português (Brasil)](README_PT-BR.md) | [Türkçe](README_TR.md) | [Русский](README_RU.md) | [Tiếng Việt](README_VI.md) | **Indonesia** | [עברית](README_IW.md) | [हिंदी](README_IN.md) | [Italiano](README_IT.md)
|
||||
[English](README.md) | [Español](README_ES.md) | [简体中文](README_CN.md) | [繁體中文](README_TW.md) | [日本語](README_JP.md) | [한국어](README_KR.md) | [Polski](README_PL.md) | [Português (Brasil)](README_PT-BR.md) | [Türkçe](README_TR.md) | [Русский](README_RU.md) | [Tiếng Việt](README_VI.md) | **Indonesia** | [עברית](README_IW.md) | [हिंदी](README_IN.md)
|
||||
|
||||
# KernelSU
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
[English](README.md) | [Español](README_ES.md) | [简体中文](README_CN.md) | [繁體中文](README_TW.md) | [日本語](README_JP.md) | [한국어](README_KR.md) | [Polski](README_PL.md) | [Português (Brasil)](README_PT-BR.md) | [Türkçe](README_TR.md) | [Русский](README_RU.md) | [Tiếng Việt](README_VI.md) | [Indonesia](README_ID.md) | [עברית](README_IW.md) | **हिंदी** | [Italiano](README_IT.md)
|
||||
[English](README.md) | [Español](README_ES.md) | [简体中文](README_CN.md) | [繁體中文](README_TW.md) | [日本語](README_JP.md) | [한국어](README_KR.md) | [Polski](README_PL.md) | [Português (Brasil)](README_PT-BR.md) | [Türkçe](README_TR.md) | [Русский](README_RU.md) | [Tiếng Việt](README_VI.md) | [Indonesia](README_ID.md) | [עברית](README_IW.md) | **हिंदी**
|
||||
|
||||
# KernelSU
|
||||
|
||||
|
||||
@@ -1,58 +0,0 @@
|
||||
[English](REAME.md) | [Español](README_ES.md) | [简体中文](README_CN.md) | [繁體中文](README_TW.md) | [日本語](README_JP.md) | [한국어](README_KR.md) | [Polski](README_PL.md) | [Português (Brasil)](README_PT-BR.md) | [Türkçe](README_TR.md) | [Русский](README_RU.md) | [Tiếng Việt](README_VI.md) | [Indonesia](README_ID.md) | [עברית](README_IW.md) | [हिंदी](README_IN.md) | **Italiano**
|
||||
|
||||
# KernelSU
|
||||
|
||||
<img src="https://kernelsu.org/logo.png" style="width: 96px;" alt="logo">
|
||||
|
||||
Una soluzione per il root basata sul kernel per i dispositivi Android.
|
||||
|
||||
[](https://github.com/tiann/KernelSU/releases/latest)
|
||||
[](https://hosted.weblate.org/engage/kernelsu)
|
||||
[](https://t.me/KernelSU)
|
||||
[](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html)
|
||||
[](/LICENSE)
|
||||
|
||||
## Funzionalità
|
||||
|
||||
1. `su` e accesso root basato sul kernel.
|
||||
2. Sistema di moduli per la modifica del sistema basato su [OverlayFS](https://en.wikipedia.org/wiki/OverlayFS).
|
||||
3. [App profile](https://kernelsu.org/guide/app-profile.html): Limita i poteri dell'accesso root a permessi specifici.
|
||||
|
||||
## Compatibilità
|
||||
|
||||
KernelSU supporta ufficialmente i dispositivi Android GKI 2.0 (kernel 5.10 o superiore). I kernel precedenti (kernel 4.14+) sono anche compatibili, ma il kernel deve essere compilato manualmente.
|
||||
|
||||
Questo implica che WSA, ChromeOS e tutti le varianti di Android basate su container e virtualizzazione sono supportate.
|
||||
|
||||
Allo stato attuale solo le architetture a 64-bit ARM (arm64-v8a) e x86 (x86_64) sono supportate.
|
||||
|
||||
## Utilizzo
|
||||
|
||||
- [Istruzioni per l'installazione](https://kernelsu.org/guide/installation.html)
|
||||
- [Come compilare manualmente?](https://kernelsu.org/guide/how-to-build.html)
|
||||
- [Sito web ufficiale](https://kernelsu.org/)
|
||||
|
||||
## Traduzioni
|
||||
|
||||
Per aiutare a tradurre KernelSU o migliorare le traduzioni esistenti, si è pregati di utilizzare
|
||||
To help translate KernelSU or improve existing translations, please use [Weblate](https://hosted.weblate.org/engage/kernelsu/). Le richieste di pull delle traduzioni del manager non saranno più accettate perché sarebbero in conflitto con Weblate.
|
||||
|
||||
## Discussione
|
||||
|
||||
- Telegram: [@KernelSU](https://t.me/KernelSU)
|
||||
|
||||
## Securezza
|
||||
|
||||
Per informazioni riguardo la segnalazione di vulnerabilità di sicurezza per KernelSU, leggi [SECURITY.md](/SECURITY.md).
|
||||
|
||||
## Licenza
|
||||
|
||||
- I file nella cartella `kernel` sono forniti secondo la licenza [GPL-2.0-only](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html).
|
||||
- Tutte le altre parti, ad eccezione della certella `kernel`, seguono la licenza [GPL-3.0-or-later](https://www.gnu.org/licenses/gpl-3.0.html).
|
||||
|
||||
## Riconoscimenti e attribuzioni
|
||||
|
||||
- [kernel-assisted-superuser](https://git.zx2c4.com/kernel-assisted-superuser/about/): l'idea alla base di KernelSU.
|
||||
- [Magisk](https://github.com/topjohnwu/Magisk): la potente utilità per il root.
|
||||
- [genuine](https://github.com/brevent/genuine/): verifica della firma apk v2.
|
||||
- [Diamorphine](https://github.com/m0nad/Diamorphine): alcune capacità di rootkit.
|
||||
@@ -1,4 +1,4 @@
|
||||
[English](README.md) | [Español](README_ES.md) | [简体中文](README_CN.md) | [繁體中文](README_TW.md) | [日本語](README_JP.md) | [한국어](README_KR.md) | [Polski](README_PL.md) | [Português (Brasil)](README_PT-BR.md) | [Türkçe](README_TR.md) | [Русский](README_RU.md) | [Tiếng Việt](README_VI.md) | [Indonesia](README_ID.md) | **עברית** | [हिंदी](README_IN.md) | [Italiano](README_IT.md)
|
||||
[English](README.md) | [Español](README_ES.md) | [简体中文](README_CN.md) | [繁體中文](README_TW.md) | [日本語](README_JP.md) | [한국어](README_KR.md) | [Polski](README_PL.md) | [Português (Brasil)](README_PT-BR.md) | [Türkçe](README_TR.md) | [Русский](README_RU.md) | [Tiếng Việt](README_VI.md) | [Indonesia](README_ID.md) | **עברית** | [हिंदी](README_IN.md)
|
||||
|
||||
# KernelSU
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
[English](README.md) | [Español](README_ES.md) | [简体中文](README_CN.md) | [繁體中文](README_TW.md) | **日本語** | [한국어](README_KR.md) | [Polski](README_PL.md) | [Português (Brasil)](README_PT-BR.md) | [Türkçe](README_TR.md) | [Русский](README_RU.md) | [Tiếng Việt](README_VI.md) | [Indonesia](README_ID.md) | [עברית](README_IW.md) | [हिंदी](README_IN.md) | [Italiano](README_IT.md)
|
||||
[English](README.md) | [Español](README_ES.md) | [简体中文](README_CN.md) | [繁體中文](README_TW.md) | **日本語** | [한국어](README_KR.md) | [Polski](README_PL.md) | [Português (Brasil)](README_PT-BR.md) | [Türkçe](README_TR.md) | [Русский](README_RU.md) | [Tiếng Việt](README_VI.md) | [Indonesia](README_ID.md) | [עברית](README_IW.md) | [हिंदी](README_IN.md)
|
||||
|
||||
# KernelSU
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
[English](README.md) | [Español](README_ES.md) | [简体中文](README_CN.md) | [繁體中文](README_TW.md) | [日本語](README_JP.md) | **한국어** | [Polski](README_PL.md) | [Português (Brasil)](README_PT-BR.md) | [Türkçe](README_TR.md) | [Русский](README_RU.md) | [Tiếng Việt](README_VI.md) | [Indonesia](README_ID.md) | [עברית](README_IW.md) | [हिंदी](README_IN.md) | [Italiano](README_IT.md)
|
||||
[English](README.md) | [Español](README_ES.md) | [简体中文](README_CN.md) | [繁體中文](README_TW.md) | [日本語](README_JP.md) | **한국어** | [Polski](README_PL.md) | [Português (Brasil)](README_PT-BR.md) | [Türkçe](README_TR.md) | [Русский](README_RU.md) | [Tiếng Việt](README_VI.md) | [Indonesia](README_ID.md) | [עברית](README_IW.md) | [हिंदी](README_IN.md)
|
||||
|
||||
# KernelSU
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
[English](README.md) | [Español](README_ES.md) | [简体中文](README_CN.md) | [繁體中文](README_TW.md) | [日本語](README_JP.md) | [한국어](README_KR.md) | **Polski** | [Português (Brasil)](README_PT-BR.md) | [Türkçe](README_TR.md) | [Русский](README_RU.md) | [Tiếng Việt](README_VI.md) | [Indonesia](README_ID.md) | [עברית](README_IW.md) | [हिंदी](README_IN.md) | [Italiano](README_IT.md)
|
||||
[English](README.md) | [Español](README_ES.md) | [简体中文](README_CN.md) | [繁體中文](README_TW.md) | [日本語](README_JP.md) | [한국어](README_KR.md) | **Polski** | [Português (Brasil)](README_PT-BR.md) | [Türkçe](README_TR.md) | [Русский](README_RU.md) | [Tiếng Việt](README_VI.md) | [Indonesia](README_ID.md) | [עברית](README_IW.md) | [हिंदी](README_IN.md)
|
||||
|
||||
# KernelSU
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
[English](README.md) | [Español](README_ES.md) | [简体中文](README_CN.md) | [繁體中文](README_TW.md) | [日本語](README_JP.md) | [한국어](README_KR.md) | [Polski](README_PL.md) | **Português (Brasil)** | [Türkçe](README_TR.md) | [Русский](README_RU.md) | [Tiếng Việt](README_VI.md) | [Indonesia](README_ID.md) | [עברית](README_IW.md) | [हिंदी](README_IN.md) | [Italiano](README_IT.md)
|
||||
[English](README.md) | [Español](README_ES.md) | [简体中文](README_CN.md) | [繁體中文](README_TW.md) | [日本語](README_JP.md) | [한국어](README_KR.md) | [Polski](README_PL.md) | **Português (Brasil)** | [Türkçe](README_TR.md) | [Русский](README_RU.md) | [Tiếng Việt](README_VI.md) | [Indonesia](README_ID.md) | [עברית](README_IW.md) | [हिंदी](README_IN.md)
|
||||
|
||||
# KernelSU
|
||||
|
||||
@@ -15,26 +15,26 @@ Uma solução root baseada em kernel para dispositivos Android.
|
||||
## Características
|
||||
|
||||
1. `su` e gerenciamento de acesso root baseado em kernel.
|
||||
2. Sistema de módulos baseado em [OverlayFS](https://en.wikipedia.org/wiki/OverlayFS).
|
||||
2. Sistema modular baseado em [OverlayFS](https://en.wikipedia.org/wiki/OverlayFS).
|
||||
3. [Perfil do Aplicativo](https://kernelsu.org/pt_BR/guide/app-profile.html): Tranque o poder root em uma gaiola.
|
||||
|
||||
## Estado de compatibilidade
|
||||
|
||||
O KernelSU oferece suporte oficial a dispositivos Android GKI 2.0 (kernel 5.10+). Kernels mais antigos (4.14+) também são compatíveis, mas será necessário construir o kernel manualmente.
|
||||
O KernelSU oferece suporte oficial a dispositivos Android GKI 2.0 (kernel 5.10+). Kernels mais antigos (4.14+) também são compatíveis, mas o kernel terá que ser construído manualmente.
|
||||
|
||||
Com isso, WSA, ChromeOS e Android baseado em contêiner são todos suportados.
|
||||
|
||||
Atualmente, apenas as arquiteturas `arm64-v8a` e `x86_64` são compatíveis.
|
||||
Atualmente, apenas `arm64-v8a` e `x86_64` são suportados.
|
||||
|
||||
## Uso
|
||||
|
||||
- [Instalação](https://kernelsu.org/pt_BR/guide/installation.html)
|
||||
- [Como compilar](https://kernelsu.org/pt_BR/guide/how-to-build.html)
|
||||
- [Como compilar o KernelSU?](https://kernelsu.org/pt_BR/guide/how-to-build.html)
|
||||
- [Site oficial](https://kernelsu.org/pt_BR/)
|
||||
|
||||
## Tradução
|
||||
|
||||
Para contribuir com a tradução do KernelSU ou aprimorar traduções existentes, por favor, use o [Weblate](https://hosted.weblate.org/engage/kernelsu/). PR para a tradução do Manager não são mais aceitas, pois podem entrar em conflito com o Weblate.
|
||||
Para contribuir com a tradução do KernelSU ou aprimorar traduções existentes, por favor, utilize o [Weblate](https://hosted.weblate.org/engage/kernelsu/). PR para a tradução do Gerenciador não são mais aceitas, pois podem entrar em conflito com o Weblate.
|
||||
|
||||
## Discussão
|
||||
|
||||
@@ -51,7 +51,7 @@ Para obter informações sobre como relatar vulnerabilidades de segurança do Ke
|
||||
|
||||
## Créditos
|
||||
|
||||
- [Kernel-Assisted Superuser](https://git.zx2c4.com/kernel-assisted-superuser/about/): A ideia do KernelSU.
|
||||
- [Magisk](https://github.com/topjohnwu/Magisk): A poderosa ferramenta root.
|
||||
- [genuine](https://github.com/brevent/genuine/): Validação de assinatura APK v2.
|
||||
- [Diamorphine](https://github.com/m0nad/Diamorphine): Algumas habilidades de rootkit.
|
||||
- [kernel-assisted-superuser](https://git.zx2c4.com/kernel-assisted-superuser/about/): a ideia do KernelSU.
|
||||
- [Magisk](https://github.com/topjohnwu/Magisk): a poderosa ferramenta root.
|
||||
- [genuine](https://github.com/brevent/genuine/): validação de assinatura apk v2.
|
||||
- [Diamorphine](https://github.com/m0nad/Diamorphine): algumas habilidades de rootkit.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
[English](README.md) | [Español](README_ES.md) | [简体中文](README_CN.md) | [繁體中文](README_TW.md) | [日本語](README_JP.md) | [한국어](README_KR.md) | [Polski](README_PL.md) | [Português (Brasil)](README_PT-BR.md) | [Türkçe](README_TR.md) | **Русский** | [Tiếng Việt](README_VI.md) | [Indonesia](README_ID.md) | [עברית](README_IW.md) | [हिंदी](README_IN.md) | [Italiano](README_IT.md)
|
||||
[English](README.md) | [Español](README_ES.md) | [简体中文](README_CN.md) | [繁體中文](README_TW.md) | [日本語](README_JP.md) | [한국어](README_KR.md) | [Polski](README_PL.md) | [Português (Brasil)](README_PT-BR.md) | [Türkçe](README_TR.md) | **Русский** | [Tiếng Việt](README_VI.md) | [Indonesia](README_ID.md) | [עברית](README_IW.md) | [हिंदी](README_IN.md)
|
||||
|
||||
# KernelSU
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
[English](README.md) | [Español](README_ES.md) | [简体中文](README_CN.md) | [繁體中文](README_TW.md) | [日本語](README_JP.md) | [한국어](README_KR.md) | [Polski](README_PL.md) | [Português (Brasil)](README_PT-BR.md) | **Türkçe** | [Русский](README_RU.md) | [Tiếng Việt](README_VI.md) | [Indonesia](README_ID.md) | [עברית](README_IW.md) | [हिंदी](README_IN.md) | [Italiano](README_IT.md)
|
||||
[English](README.md) | [Español](README_ES.md) | [简体中文](README_CN.md) | [繁體中文](README_TW.md) | [日本語](README_JP.md) | [한국어](README_KR.md) | [Polski](README_PL.md) | [Português (Brasil)](README_PT-BR.md) | **Türkçe** | [Русский](README_RU.md) | [Tiếng Việt](README_VI.md) | [Indonesia](README_ID.md) | [עברית](README_IW.md) | [हिंदी](README_IN.md)
|
||||
|
||||
# KernelSU
|
||||
|
||||
|
||||
@@ -1,57 +1,56 @@
|
||||
[English](README.md) | [Español](README_ES.md) | [简体中文](README_CN.md) | **繁體中文** | [日本語](README_JP.md) | [한국어](README_KR.md) | [Polski](README_PL.md) | [Português (Brasil)](README_PT-BR.md) | [Türkçe](README_TR.md) | [Русский](README_RU.md) | [Tiếng Việt](README_VI.md) | [Indonesia](README_ID.md) | [עברית](README_IW.md) | [हिंदी](README_IN.md) | [Italiano](README_IT.md)
|
||||
[English](README.md) | [Español](README_ES.md) | [简体中文](README_CN.md) | **繁體中文** | [日本語](README_JP.md) | [한국어](README_KR.md) | [Polski](README_PL.md) | [Português (Brasil)](README_PT-BR.md) | [Türkçe](README_TR.md) | [Русский](README_RU.md) | [Tiếng Việt](README_VI.md) | [Indonesia](README_ID.md) | [עברית](README_IW.md) | [हिंदी](README_IN.md)
|
||||
|
||||
# KernelSU
|
||||
|
||||
<img src="https://kernelsu.org/logo.png" style="width: 96px;" alt="標誌">
|
||||
<img src="https://kernelsu.org/logo.png" style="width: 96px;" alt="logo">
|
||||
|
||||
一套基於 Android 裝置核心的 Root 解決方案。
|
||||
一個基於核心的 Android 裝置 Root 解決方案
|
||||
|
||||
[](https://github.com/tiann/KernelSU/releases/latest)
|
||||
[](https://hosted.weblate.org/engage/kernelsu)
|
||||
[](https://t.me/KernelSU)
|
||||
[](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html)
|
||||
[](/LICENSE)
|
||||
[](https://github.com/tiann/KernelSU/releases/latest)
|
||||
[](https://hosted.weblate.org/engage/kernelsu)
|
||||
[](https://t.me/KernelSU)
|
||||
[](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html)
|
||||
[](/LICENSE)
|
||||
|
||||
## 特色功能
|
||||
## 功能
|
||||
|
||||
1. 以核心內 `su` 管理 Root 存取。
|
||||
2. 以 [OverlayFS](https://zh.wikipedia.org/zh-tw/OverlayFS) 運作模組系統。
|
||||
3. [App Profile](https://kernelsu.org/zh_TW/guide/app-profile.html):使 Root 掌握的生殺大權受制於此。
|
||||
- 基於核心的 `su` 和 Root 存取權管理。
|
||||
- 基於 [OverlayFS](https://en.wikipedia.org/wiki/OverlayFS) 的模組系統。
|
||||
- [App Profile](https://kernelsu.org/zh_TW/guide/app-profile.html): 將 Root 的權限鎖在牢籠中.
|
||||
|
||||
## 相容事態
|
||||
## 相容性狀態
|
||||
|
||||
理論上採以 Android GKI 2.0 的裝置(核心版本 5.10+),皆受 KernelSU 支援;採以老舊核心版本(4.14+)的裝置在手動建置核心後,亦受支援。
|
||||
KernelSU 官方支援 Android GKI 2.0 的裝置 (核心版本 5.10+ );舊版核心同樣相容 (最低 4.14+ ),但需要自行編譯核心。
|
||||
|
||||
另可在 WSA、ChromeOS 一類的容器式 Android 中運作。
|
||||
WSA和ChromeOS和執行在容器中的 Android 也可以與 KernelSU 一同運作。
|
||||
|
||||
目前僅適用 `arm64-v8a` 以及 `x86_64` 架構。
|
||||
目前支援架構:`arm64-v8a` 和 `x86_64`。
|
||||
|
||||
## 使用手冊
|
||||
## 使用方法
|
||||
|
||||
- [安裝教學](https://kernelsu.org/zh_TW/guide/installation.html)
|
||||
- [如何建置 KernelSU?](https://kernelsu.org/zh_TW/guide/how-to-build.html)
|
||||
- [如何建置?](https://kernelsu.org/zh_TW/guide/how-to-build.html)
|
||||
- [官方網站](https://kernelsu.org/zh_TW/)
|
||||
|
||||
## 多語翻譯
|
||||
## 翻譯
|
||||
|
||||
欲要協助 KernelSU 邁向多語化,抑或改進翻譯品質,請前往 [Weblate](https://hosted.weblate.org/engage/kernelsu/) 進行翻譯。為避免與 Weblate 上的翻譯發生衝突,現已不再受理翻譯相關的管理工具 PR。
|
||||
若要協助翻譯 KernelSU 或改進現有翻譯,請使用 [Weblate](https://hosted.weblate.org/engage/kernelsu/)。 翻譯管理器的PR不再被接受,因為它會與Weblate衝突。
|
||||
|
||||
## 綜合討論
|
||||
## 討論
|
||||
|
||||
- Telegram:[@KernelSU](https://t.me/KernelSU)
|
||||
|
||||
## 安全政策
|
||||
## 安全
|
||||
有關報告 KernelSU 中的安全漏洞的資訊,請參閱 [SECURITY.md](/SECURITY.md)。
|
||||
|
||||
欲要得知、回報 KernelSU 的安全性漏洞,請參閱 [SECURITY.md](/SECURITY.md)。
|
||||
## 授權
|
||||
|
||||
## 授權條款
|
||||
- 目錄 `kernel` 下所有檔案為 [GPL-2-only](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html)。
|
||||
- 除 `kernel` 目錄的其他部分均為 [GPL-3-or-later](https://www.gnu.org/licenses/gpl-3.0.html)。
|
||||
|
||||
- 位於 `kernel` 資料夾的檔案以[《GPL-2.0-only》](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html)規範。
|
||||
- 非位於 `kernel` 資料夾的其他檔案以[《GPL-3.0-or-later》](https://www.gnu.org/licenses/gpl-3.0.html)規範。
|
||||
## 致謝
|
||||
|
||||
## 致謝名單
|
||||
|
||||
- [kernel-assisted-superuser](https://git.zx2c4.com/kernel-assisted-superuser/about/):KernelSU 的靈感來源。
|
||||
- [Magisk](https://github.com/topjohnwu/Magisk):強而有力的 Root 工具。
|
||||
- [genuine](https://github.com/brevent/genuine/):用於確效 Apk v2 簽章。
|
||||
- [Diamorphine](https://github.com/m0nad/Diamorphine): 用於增進 Rootkit 技巧。
|
||||
- [kernel-assisted-superuser](https://git.zx2c4.com/kernel-assisted-superuser/about/):KernelSU 的靈感。
|
||||
- [Magisk](https://github.com/topjohnwu/Magisk):sepolicy 實作。
|
||||
- [genuine](https://github.com/brevent/genuine/):apk v2 簽章驗證。
|
||||
- [Diamorphine](https://github.com/m0nad/Diamorphine):一些 rootkit 技巧。
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
[English](README.md) | [Español](README_ES.md) | [简体中文](README_CN.md) | [繁體中文](README_TW.md) | [日本語](README_JP.md) | [한국어](README_KR.md) | [Polski](README_PL.md) | [Português (Brasil)](README_PT-BR.md) | [Türkçe](README_TR.md) | [Русский](README_RU.md) | **Tiếng Việt** | [Indonesia](README_ID.md) | [עברית](README_IW.md) | [हिंदी](README_IN.md) | [Italiano](README_IT.md)
|
||||
[English](README.md) | [Español](README_ES.md) | [简体中文](README_CN.md) | [繁體中文](README_TW.md) | [日本語](README_JP.md) | [한국어](README_KR.md) | [Polski](README_PL.md) | [Português (Brasil)](README_PT-BR.md) | [Türkçe](README_TR.md) | [Русский](README_RU.md) | **Tiếng Việt** | [Indonesia](README_ID.md) | [עברית](README_IW.md) | [हिंदी](README_IN.md)
|
||||
|
||||
# KernelSU
|
||||
|
||||
|
||||
26
js/README.md
26
js/README.md
@@ -10,12 +10,12 @@ yarn add kernelsu
|
||||
|
||||
### exec
|
||||
|
||||
Spawns a **root** shell and runs a command within that shell, returning a Promise that resolves with the `stdout` and `stderr` outputs upon completion.
|
||||
Spawns a **root** shell and runs a command within that shell, passing the `stdout` and `stderr` to a Promise when complete.
|
||||
|
||||
- `command` `<string>` The command to run, with space-separated arguments.
|
||||
- `options` `<Object>`
|
||||
- `cwd` - Current working directory of the child process.
|
||||
- `env` - Environment key-value pairs.
|
||||
- `cwd` - Current working directory of the child process
|
||||
- `env` - Environment key-value pairs
|
||||
|
||||
```javascript
|
||||
import { exec } from 'kernelsu';
|
||||
@@ -31,13 +31,13 @@ if (errno === 0) {
|
||||
|
||||
Spawns a new process using the given `command` in **root** shell, with command-line arguments in `args`. If omitted, `args` defaults to an empty array.
|
||||
|
||||
Returns a `ChildProcess` instance. Instances of `ChildProcess` represent spawned child processes.
|
||||
Returns a `ChildProcess`, Instances of the ChildProcess represent spawned child processes.
|
||||
|
||||
- `command` `<string>` The command to run.
|
||||
- `args` `<string[]>` List of string arguments.
|
||||
- `options` `<Object>`:
|
||||
- `cwd` `<string>` - Current working directory of the child process.
|
||||
- `env` `<Object>` - Environment key-value pairs.
|
||||
- `cwd` `<string>` - Current working directory of the child process
|
||||
- `env` `<Object>` - Environment key-value pairs
|
||||
|
||||
Example of running `ls -lh /data`, capturing `stdout`, `stderr`, and the exit code:
|
||||
|
||||
@@ -63,9 +63,9 @@ ls.on('exit', (code) => {
|
||||
|
||||
##### Event 'exit'
|
||||
|
||||
- `code` `<number>` The exit code if the child process exited on its own.
|
||||
- `code` `<number>` The exit code if the child exited on its own.
|
||||
|
||||
The `'exit'` event is emitted when the child process ends. If the process exits, `code` contains the final exit code; otherwise, it is null.
|
||||
The `'exit'` event is emitted after the child process ends. If the process exited, `code` is the final exit code of the process, otherwise null
|
||||
|
||||
##### Event 'error'
|
||||
|
||||
@@ -109,13 +109,3 @@ Show a toast message.
|
||||
import { toast } from 'kernelsu';
|
||||
toast('Hello, world!');
|
||||
```
|
||||
|
||||
### moduleInfo
|
||||
|
||||
Get module info.
|
||||
|
||||
```javascript
|
||||
import { moduleInfo } from 'kernelsu';
|
||||
// print moduleId in console
|
||||
console.log(moduleInfo());
|
||||
```
|
||||
|
||||
5
js/index.d.ts
vendored
5
js/index.d.ts
vendored
@@ -37,12 +37,9 @@ declare function fullScreen(isFullScreen: boolean);
|
||||
|
||||
declare function toast(message: string);
|
||||
|
||||
declare function moduleInfo(): string;
|
||||
|
||||
export {
|
||||
exec,
|
||||
spawn,
|
||||
fullScreen,
|
||||
toast,
|
||||
moduleInfo
|
||||
toast
|
||||
}
|
||||
|
||||
@@ -113,7 +113,3 @@ export function fullScreen(isFullScreen) {
|
||||
export function toast(message) {
|
||||
ksu.toast(message);
|
||||
}
|
||||
|
||||
export function moduleInfo() {
|
||||
return ksu.moduleInfo();
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "kernelsu",
|
||||
"version": "1.0.7",
|
||||
"version": "1.0.6",
|
||||
"description": "Library for KernelSU's module WebUI",
|
||||
"main": "index.js",
|
||||
"types": "index.d.ts",
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
#include <linux/capability.h>
|
||||
#include <linux/compiler.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/gfp.h>
|
||||
@@ -63,14 +62,12 @@ static void remove_uid_from_arr(uid_t uid)
|
||||
|
||||
static void init_default_profiles()
|
||||
{
|
||||
kernel_cap_t full_cap = CAP_FULL_SET;
|
||||
|
||||
default_root_profile.uid = 0;
|
||||
default_root_profile.gid = 0;
|
||||
default_root_profile.groups_count = 1;
|
||||
default_root_profile.groups[0] = 0;
|
||||
memcpy(&default_root_profile.capabilities.effective, &full_cap,
|
||||
sizeof(default_root_profile.capabilities.effective));
|
||||
memset(&default_root_profile.capabilities, 0xff,
|
||||
sizeof(default_root_profile.capabilities));
|
||||
default_root_profile.namespaces = 0;
|
||||
strcpy(default_root_profile.selinux_domain, KSU_DEFAULT_SELINUX_DOMAIN);
|
||||
|
||||
@@ -111,7 +108,6 @@ void ksu_show_allow_list(void)
|
||||
static void ksu_grant_root_to_shell()
|
||||
{
|
||||
struct app_profile profile = {
|
||||
.version = KSU_APP_PROFILE_VER,
|
||||
.allow_su = true,
|
||||
.current_uid = 2000,
|
||||
};
|
||||
@@ -154,6 +150,11 @@ static bool profile_valid(struct app_profile *profile)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (forbid_system_uid(profile->current_uid)) {
|
||||
pr_err("uid lower than 2000 is unsupported: %d\n", profile->current_uid);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (profile->version < KSU_APP_PROFILE_VER) {
|
||||
pr_info("Unsupported profile version: %d\n", profile->version);
|
||||
return false;
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
#include <linux/sched.h>
|
||||
#include <linux/security.h>
|
||||
#include <linux/stddef.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/uidgid.h>
|
||||
@@ -49,10 +48,6 @@ static bool ksu_module_mounted = false;
|
||||
|
||||
extern int handle_sepolicy(unsigned long arg3, void __user *arg4);
|
||||
|
||||
static bool ksu_su_compat_enabled = true;
|
||||
extern void ksu_sucompat_init();
|
||||
extern void ksu_sucompat_exit();
|
||||
|
||||
static inline bool is_allow_su()
|
||||
{
|
||||
if (is_manager()) {
|
||||
@@ -108,12 +103,49 @@ static void setup_groups(struct root_profile *profile, struct cred *cred)
|
||||
|
||||
groups_sort(group_info);
|
||||
set_groups(cred, group_info);
|
||||
put_group_info(group_info);
|
||||
}
|
||||
|
||||
static void disable_seccomp()
|
||||
void escape_to_root(void)
|
||||
{
|
||||
assert_spin_locked(¤t->sighand->siglock);
|
||||
struct cred *cred;
|
||||
|
||||
cred = (struct cred *)__task_cred(current);
|
||||
|
||||
if (cred->euid.val == 0) {
|
||||
pr_warn("Already root, don't escape!\n");
|
||||
return;
|
||||
}
|
||||
struct root_profile *profile = ksu_get_root_profile(cred->uid.val);
|
||||
|
||||
cred->uid.val = profile->uid;
|
||||
cred->suid.val = profile->uid;
|
||||
cred->euid.val = profile->uid;
|
||||
cred->fsuid.val = profile->uid;
|
||||
|
||||
cred->gid.val = profile->gid;
|
||||
cred->fsgid.val = profile->gid;
|
||||
cred->sgid.val = profile->gid;
|
||||
cred->egid.val = profile->gid;
|
||||
|
||||
BUILD_BUG_ON(sizeof(profile->capabilities.effective) !=
|
||||
sizeof(kernel_cap_t));
|
||||
|
||||
// setup capabilities
|
||||
// we need CAP_DAC_READ_SEARCH becuase `/data/adb/ksud` is not accessible for non root process
|
||||
// we add it here but don't add it to cap_inhertiable, it would be dropped automaticly after exec!
|
||||
u64 cap_for_ksud =
|
||||
profile->capabilities.effective | CAP_DAC_READ_SEARCH;
|
||||
memcpy(&cred->cap_effective, &cap_for_ksud,
|
||||
sizeof(cred->cap_effective));
|
||||
memcpy(&cred->cap_inheritable, &profile->capabilities.effective,
|
||||
sizeof(cred->cap_inheritable));
|
||||
memcpy(&cred->cap_permitted, &profile->capabilities.effective,
|
||||
sizeof(cred->cap_permitted));
|
||||
memcpy(&cred->cap_bset, &profile->capabilities.effective,
|
||||
sizeof(cred->cap_bset));
|
||||
memcpy(&cred->cap_ambient, &profile->capabilities.effective,
|
||||
sizeof(cred->cap_ambient));
|
||||
|
||||
// disable seccomp
|
||||
#if defined(CONFIG_GENERIC_ENTRY) && \
|
||||
LINUX_VERSION_CODE >= KERNEL_VERSION(5, 11, 0)
|
||||
@@ -127,62 +159,9 @@ static void disable_seccomp()
|
||||
current->seccomp.filter = NULL;
|
||||
#else
|
||||
#endif
|
||||
}
|
||||
|
||||
void escape_to_root(void)
|
||||
{
|
||||
struct cred *cred;
|
||||
|
||||
cred = prepare_creds();
|
||||
if (!cred) {
|
||||
pr_warn("prepare_creds failed!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (cred->euid.val == 0) {
|
||||
pr_warn("Already root, don't escape!\n");
|
||||
abort_creds(cred);
|
||||
return;
|
||||
}
|
||||
|
||||
struct root_profile *profile = ksu_get_root_profile(cred->uid.val);
|
||||
|
||||
cred->uid.val = profile->uid;
|
||||
cred->suid.val = profile->uid;
|
||||
cred->euid.val = profile->uid;
|
||||
cred->fsuid.val = profile->uid;
|
||||
|
||||
cred->gid.val = profile->gid;
|
||||
cred->fsgid.val = profile->gid;
|
||||
cred->sgid.val = profile->gid;
|
||||
cred->egid.val = profile->gid;
|
||||
cred->securebits = 0;
|
||||
|
||||
BUILD_BUG_ON(sizeof(profile->capabilities.effective) !=
|
||||
sizeof(kernel_cap_t));
|
||||
|
||||
// setup capabilities
|
||||
// we need CAP_DAC_READ_SEARCH becuase `/data/adb/ksud` is not accessible for non root process
|
||||
// we add it here but don't add it to cap_inhertiable, it would be dropped automaticly after exec!
|
||||
u64 cap_for_ksud =
|
||||
profile->capabilities.effective | CAP_DAC_READ_SEARCH;
|
||||
memcpy(&cred->cap_effective, &cap_for_ksud,
|
||||
sizeof(cred->cap_effective));
|
||||
memcpy(&cred->cap_permitted, &profile->capabilities.effective,
|
||||
sizeof(cred->cap_permitted));
|
||||
memcpy(&cred->cap_bset, &profile->capabilities.effective,
|
||||
sizeof(cred->cap_bset));
|
||||
|
||||
setup_groups(profile, cred);
|
||||
|
||||
commit_creds(cred);
|
||||
|
||||
// Refer to kernel/seccomp.c: seccomp_set_mode_strict
|
||||
// When disabling Seccomp, ensure that current->sighand->siglock is held during the operation.
|
||||
spin_lock_irq(¤t->sighand->siglock);
|
||||
disable_seccomp();
|
||||
spin_unlock_irq(¤t->sighand->siglock);
|
||||
|
||||
setup_selinux(profile->selinux_domain);
|
||||
}
|
||||
|
||||
@@ -214,7 +193,7 @@ int ksu_handle_rename(struct dentry *old_dentry, struct dentry *new_dentry)
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!strstr(buf, "/system/packages.list")) {
|
||||
if (strcmp(buf, "/system/packages.list")) {
|
||||
return 0;
|
||||
}
|
||||
pr_info("renameat: %s -> %s, new path: %s\n", old_dentry->d_iname,
|
||||
@@ -225,26 +204,6 @@ int ksu_handle_rename(struct dentry *old_dentry, struct dentry *new_dentry)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void nuke_ext4_sysfs() {
|
||||
struct path path;
|
||||
int err = kern_path("/data/adb/modules", 0, &path);
|
||||
if (err) {
|
||||
pr_err("nuke path err: %d\n", err);
|
||||
return;
|
||||
}
|
||||
|
||||
struct super_block* sb = path.dentry->d_inode->i_sb;
|
||||
const char* name = sb->s_type->name;
|
||||
if (strcmp(name, "ext4") != 0) {
|
||||
pr_info("nuke but module aren't mounted\n");
|
||||
path_put(&path);
|
||||
return;
|
||||
}
|
||||
|
||||
ext4_unregister_sysfs(sb);
|
||||
path_put(&path);
|
||||
}
|
||||
|
||||
int ksu_handle_prctl(int option, unsigned long arg2, unsigned long arg3,
|
||||
unsigned long arg4, unsigned long arg5)
|
||||
{
|
||||
@@ -303,12 +262,12 @@ int ksu_handle_prctl(int option, unsigned long arg2, unsigned long arg3,
|
||||
if (copy_to_user(arg3, &version, sizeof(version))) {
|
||||
pr_err("prctl reply error, cmd: %lu\n", arg2);
|
||||
}
|
||||
u32 version_flags = 0;
|
||||
#ifdef MODULE
|
||||
version_flags |= 0x1;
|
||||
u32 is_lkm = 0x1;
|
||||
#else
|
||||
u32 is_lkm = 0x0;
|
||||
#endif
|
||||
if (arg4 &&
|
||||
copy_to_user(arg4, &version_flags, sizeof(version_flags))) {
|
||||
if (arg4 && copy_to_user(arg4, &is_lkm, sizeof(is_lkm))) {
|
||||
pr_err("prctl reply error, cmd: %lu\n", arg2);
|
||||
}
|
||||
return 0;
|
||||
@@ -339,7 +298,6 @@ int ksu_handle_prctl(int option, unsigned long arg2, unsigned long arg3,
|
||||
case EVENT_MODULE_MOUNTED: {
|
||||
ksu_module_mounted = true;
|
||||
pr_info("module mounted!\n");
|
||||
nuke_ext4_sysfs();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
@@ -413,17 +371,6 @@ int ksu_handle_prctl(int option, unsigned long arg2, unsigned long arg3,
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (arg2 == CMD_GET_MANAGER_UID) {
|
||||
uid_t manager_uid = ksu_get_manager_uid();
|
||||
if (copy_to_user(arg3, &manager_uid, sizeof(manager_uid))) {
|
||||
pr_err("get manager uid failed\n");
|
||||
}
|
||||
if (copy_to_user(result, &reply_ok, sizeof(reply_ok))) {
|
||||
pr_err("prctl reply error, cmd: %lu\n", arg2);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// all other cmds are for 'root manager'
|
||||
if (!from_manager) {
|
||||
return 0;
|
||||
@@ -466,42 +413,6 @@ int ksu_handle_prctl(int option, unsigned long arg2, unsigned long arg3,
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (arg2 == CMD_IS_SU_ENABLED) {
|
||||
if (copy_to_user(arg3, &ksu_su_compat_enabled,
|
||||
sizeof(ksu_su_compat_enabled))) {
|
||||
pr_err("copy su compat failed\n");
|
||||
return 0;
|
||||
}
|
||||
if (copy_to_user(result, &reply_ok, sizeof(reply_ok))) {
|
||||
pr_err("prctl reply error, cmd: %lu\n", arg2);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (arg2 == CMD_ENABLE_SU) {
|
||||
bool enabled = (arg3 != 0);
|
||||
if (enabled == ksu_su_compat_enabled) {
|
||||
pr_info("cmd enable su but no need to change.\n");
|
||||
if (copy_to_user(result, &reply_ok, sizeof(reply_ok))) {// return the reply_ok directly
|
||||
pr_err("prctl reply error, cmd: %lu\n", arg2);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (enabled) {
|
||||
ksu_sucompat_init();
|
||||
} else {
|
||||
ksu_sucompat_exit();
|
||||
}
|
||||
ksu_su_compat_enabled = enabled;
|
||||
|
||||
if (copy_to_user(result, &reply_ok, sizeof(reply_ok))) {
|
||||
pr_err("prctl reply error, cmd: %lu\n", arg2);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -552,13 +463,11 @@ static void try_umount(const char *mnt, bool check_mnt, int flags)
|
||||
|
||||
if (path.dentry != path.mnt->mnt_root) {
|
||||
// it is not root mountpoint, maybe umounted by others already.
|
||||
path_put(&path);
|
||||
return;
|
||||
}
|
||||
|
||||
// we are only interest in some specific mounts
|
||||
if (check_mnt && !should_umount(&path)) {
|
||||
path_put(&path);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -619,15 +528,14 @@ int ksu_handle_setuid(struct cred *new, const struct cred *old)
|
||||
|
||||
// fixme: use `collect_mounts` and `iterate_mount` to iterate all mountpoint and
|
||||
// filter the mountpoint whose target is `/data/adb`
|
||||
try_umount("/odm", true, 0);
|
||||
try_umount("/system", true, 0);
|
||||
try_umount("/vendor", true, 0);
|
||||
try_umount("/product", true, 0);
|
||||
try_umount("/system_ext", true, 0);
|
||||
try_umount("/data/adb/modules", false, MNT_DETACH);
|
||||
|
||||
// try umount ksu temp path
|
||||
try_umount("/debug_ramdisk", false, MNT_DETACH);
|
||||
try_umount("/sbin", false, MNT_DETACH);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -21,9 +21,6 @@
|
||||
#define CMD_SET_APP_PROFILE 11
|
||||
#define CMD_UID_GRANTED_ROOT 12
|
||||
#define CMD_UID_SHOULD_UMOUNT 13
|
||||
#define CMD_IS_SU_ENABLED 14
|
||||
#define CMD_ENABLE_SU 15
|
||||
#define CMD_GET_MANAGER_UID 16
|
||||
|
||||
#define EVENT_POST_FS_DATA 1
|
||||
#define EVENT_BOOT_COMPLETED 2
|
||||
|
||||
@@ -19,24 +19,19 @@
|
||||
static struct policydb *get_policydb(void)
|
||||
{
|
||||
struct policydb *db;
|
||||
struct selinux_policy *policy = selinux_state.policy;
|
||||
struct selinux_policy *policy = rcu_dereference(selinux_state.policy);
|
||||
db = &policy->policydb;
|
||||
return db;
|
||||
}
|
||||
|
||||
static DEFINE_MUTEX(ksu_rules);
|
||||
|
||||
void apply_kernelsu_rules()
|
||||
{
|
||||
struct policydb *db;
|
||||
|
||||
if (!getenforce()) {
|
||||
pr_info("SELinux permissive or disabled, apply rules!\n");
|
||||
}
|
||||
|
||||
mutex_lock(&ksu_rules);
|
||||
|
||||
db = get_policydb();
|
||||
rcu_read_lock();
|
||||
struct policydb *db = get_policydb();
|
||||
|
||||
ksu_permissive(db, KERNEL_SU_DOMAIN);
|
||||
ksu_typeattribute(db, KERNEL_SU_DOMAIN, "mlstrustedsubject");
|
||||
@@ -123,11 +118,11 @@ void apply_kernelsu_rules()
|
||||
// Allow all binder transactions
|
||||
ksu_allow(db, ALL, KERNEL_SU_DOMAIN, "binder", ALL);
|
||||
|
||||
// Allow system server kill su process
|
||||
ksu_allow(db, "system_server", KERNEL_SU_DOMAIN, "process", "getpgid");
|
||||
ksu_allow(db, "system_server", KERNEL_SU_DOMAIN, "process", "sigkill");
|
||||
// Allow system server kill su process
|
||||
ksu_allow(db, "system_server", KERNEL_SU_DOMAIN, "process", "getpgid");
|
||||
ksu_allow(db, "system_server", KERNEL_SU_DOMAIN, "process", "sigkill");
|
||||
|
||||
mutex_unlock(&ksu_rules);
|
||||
rcu_read_unlock();
|
||||
}
|
||||
|
||||
#define MAX_SEPOL_LEN 128
|
||||
@@ -189,8 +184,6 @@ static void reset_avc_cache()
|
||||
|
||||
int handle_sepolicy(unsigned long arg3, void __user *arg4)
|
||||
{
|
||||
struct policydb *db;
|
||||
|
||||
if (!arg4) {
|
||||
return -1;
|
||||
}
|
||||
@@ -208,9 +201,9 @@ int handle_sepolicy(unsigned long arg3, void __user *arg4)
|
||||
u32 cmd = data.cmd;
|
||||
u32 subcmd = data.subcmd;
|
||||
|
||||
mutex_lock(&ksu_rules);
|
||||
rcu_read_lock();
|
||||
|
||||
db = get_policydb();
|
||||
struct policydb *db = get_policydb();
|
||||
|
||||
int ret = -1;
|
||||
if (cmd == CMD_NORMAL_PERM) {
|
||||
@@ -460,7 +453,7 @@ int handle_sepolicy(unsigned long arg3, void __user *arg4)
|
||||
}
|
||||
|
||||
exit:
|
||||
mutex_unlock(&ksu_rules);
|
||||
rcu_read_unlock();
|
||||
|
||||
// only allow and xallow needs to reset avc cache, but we cannot do that because
|
||||
// we are in atomic context. so we just reset it every time.
|
||||
|
||||
@@ -191,7 +191,7 @@ int ksu_handle_devpts(struct inode *inode)
|
||||
|
||||
#ifdef CONFIG_KPROBES
|
||||
|
||||
static int faccessat_handler_pre(struct kprobe *p, struct pt_regs *regs)
|
||||
static int sys_faccessat_handler_pre(struct kprobe *p, struct pt_regs *regs)
|
||||
{
|
||||
struct pt_regs *real_regs = PT_REAL_REGS(regs);
|
||||
int *dfd = (int *)&PT_REGS_PARM1(real_regs);
|
||||
@@ -202,18 +202,17 @@ static int faccessat_handler_pre(struct kprobe *p, struct pt_regs *regs)
|
||||
return ksu_handle_faccessat(dfd, filename_user, mode, NULL);
|
||||
}
|
||||
|
||||
static int newfstatat_handler_pre(struct kprobe *p, struct pt_regs *regs)
|
||||
static int sys_newfstatat_handler_pre(struct kprobe *p, struct pt_regs *regs)
|
||||
{
|
||||
struct pt_regs *real_regs = PT_REAL_REGS(regs);
|
||||
int *dfd = (int *)&PT_REGS_PARM1(real_regs);
|
||||
const char __user **filename_user =
|
||||
(const char **)&PT_REGS_PARM2(real_regs);
|
||||
const char __user **filename_user = (const char **)&PT_REGS_PARM2(real_regs);
|
||||
int *flags = (int *)&PT_REGS_SYSCALL_PARM4(real_regs);
|
||||
|
||||
return ksu_handle_stat(dfd, filename_user, flags);
|
||||
}
|
||||
|
||||
static int execve_handler_pre(struct kprobe *p, struct pt_regs *regs)
|
||||
static int sys_execve_handler_pre(struct kprobe *p, struct pt_regs *regs)
|
||||
{
|
||||
struct pt_regs *real_regs = PT_REAL_REGS(regs);
|
||||
const char __user **filename_user =
|
||||
@@ -223,6 +222,21 @@ static int execve_handler_pre(struct kprobe *p, struct pt_regs *regs)
|
||||
NULL);
|
||||
}
|
||||
|
||||
static struct kprobe faccessat_kp = {
|
||||
.symbol_name = SYS_FACCESSAT_SYMBOL,
|
||||
.pre_handler = sys_faccessat_handler_pre,
|
||||
};
|
||||
|
||||
static struct kprobe newfstatat_kp = {
|
||||
.symbol_name = SYS_NEWFSTATAT_SYMBOL,
|
||||
.pre_handler = sys_newfstatat_handler_pre,
|
||||
};
|
||||
|
||||
static struct kprobe execve_kp = {
|
||||
.symbol_name = SYS_EXECVE_SYMBOL,
|
||||
.pre_handler = sys_execve_handler_pre,
|
||||
};
|
||||
|
||||
static int pts_unix98_lookup_pre(struct kprobe *p, struct pt_regs *regs)
|
||||
{
|
||||
struct inode *inode;
|
||||
@@ -232,55 +246,35 @@ static int pts_unix98_lookup_pre(struct kprobe *p, struct pt_regs *regs)
|
||||
return ksu_handle_devpts(inode);
|
||||
}
|
||||
|
||||
static struct kprobe *init_kprobe(const char *name,
|
||||
kprobe_pre_handler_t handler)
|
||||
{
|
||||
struct kprobe *kp = kzalloc(sizeof(struct kprobe), GFP_KERNEL);
|
||||
if (!kp)
|
||||
return NULL;
|
||||
kp->symbol_name = name;
|
||||
kp->pre_handler = handler;
|
||||
static struct kprobe pts_unix98_lookup_kp = { .symbol_name =
|
||||
"pts_unix98_lookup",
|
||||
.pre_handler =
|
||||
pts_unix98_lookup_pre };
|
||||
|
||||
int ret = register_kprobe(kp);
|
||||
pr_info("sucompat: register_%s kprobe: %d\n", name, ret);
|
||||
if (ret) {
|
||||
kfree(kp);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return kp;
|
||||
}
|
||||
|
||||
static void destroy_kprobe(struct kprobe **kp_ptr)
|
||||
{
|
||||
struct kprobe *kp = *kp_ptr;
|
||||
if (!kp)
|
||||
return;
|
||||
unregister_kprobe(kp);
|
||||
synchronize_rcu();
|
||||
kfree(kp);
|
||||
*kp_ptr = NULL;
|
||||
}
|
||||
|
||||
static struct kprobe *su_kps[4];
|
||||
#endif
|
||||
|
||||
// sucompat: permited process can execute 'su' to gain root access.
|
||||
void ksu_sucompat_init()
|
||||
{
|
||||
#ifdef CONFIG_KPROBES
|
||||
su_kps[0] = init_kprobe(SYS_EXECVE_SYMBOL, execve_handler_pre);
|
||||
su_kps[1] = init_kprobe(SYS_FACCESSAT_SYMBOL, faccessat_handler_pre);
|
||||
su_kps[2] = init_kprobe(SYS_NEWFSTATAT_SYMBOL, newfstatat_handler_pre);
|
||||
su_kps[3] = init_kprobe("pts_unix98_lookup", pts_unix98_lookup_pre);
|
||||
int ret;
|
||||
ret = register_kprobe(&execve_kp);
|
||||
pr_info("sucompat: execve_kp: %d\n", ret);
|
||||
ret = register_kprobe(&newfstatat_kp);
|
||||
pr_info("sucompat: newfstatat_kp: %d\n", ret);
|
||||
ret = register_kprobe(&faccessat_kp);
|
||||
pr_info("sucompat: faccessat_kp: %d\n", ret);
|
||||
ret = register_kprobe(&pts_unix98_lookup_kp);
|
||||
pr_info("sucompat: devpts_kp: %d\n", ret);
|
||||
#endif
|
||||
}
|
||||
|
||||
void ksu_sucompat_exit()
|
||||
{
|
||||
#ifdef CONFIG_KPROBES
|
||||
for (int i = 0; i < ARRAY_SIZE(su_kps); i++) {
|
||||
destroy_kprobe(&su_kps[i]);
|
||||
}
|
||||
unregister_kprobe(&execve_kp);
|
||||
unregister_kprobe(&newfstatat_kp);
|
||||
unregister_kprobe(&faccessat_kp);
|
||||
unregister_kprobe(&pts_unix98_lookup_kp);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -148,12 +148,6 @@ FILLDIR_RETURN_TYPE my_actor(struct dir_context *ctx, const char *name,
|
||||
if (!strncmp(name, "..", namelen) || !strncmp(name, ".", namelen))
|
||||
return FILLDIR_ACTOR_CONTINUE; // Skip "." and ".."
|
||||
|
||||
if (d_type == DT_DIR && namelen >= 8 && !strncmp(name, "vmdl", 4) &&
|
||||
!strncmp(name + namelen - 4, ".tmp", 4)) {
|
||||
pr_info("Skipping directory: %.*s\n", namelen, name);
|
||||
return FILLDIR_ACTOR_CONTINUE; // Skip staging package
|
||||
}
|
||||
|
||||
if (snprintf(dirpath, DATA_PATH_LEN, "%s/%.*s", my_ctx->parent_dir,
|
||||
namelen, name) >= DATA_PATH_LEN) {
|
||||
pr_err("Path too long: %s/%.*s\n", my_ctx->parent_dir, namelen,
|
||||
@@ -213,8 +207,7 @@ void search_manager(const char *path, int depth, struct list_head *uid_data)
|
||||
int i, stop = 0;
|
||||
struct list_head data_path_list;
|
||||
INIT_LIST_HEAD(&data_path_list);
|
||||
unsigned long data_app_magic = 0;
|
||||
|
||||
|
||||
// Initialize APK cache list
|
||||
struct apk_path_hash *pos, *n;
|
||||
list_for_each_entry(pos, &apk_path_hash_list, list) {
|
||||
@@ -227,7 +220,7 @@ void search_manager(const char *path, int depth, struct list_head *uid_data)
|
||||
data.depth = depth;
|
||||
list_add_tail(&data.list, &data_path_list);
|
||||
|
||||
for (i = depth; i >= 0; i--) {
|
||||
for (i = depth; i > 0; i--) {
|
||||
struct data_path *pos, *n;
|
||||
|
||||
list_for_each_entry_safe(pos, n, &data_path_list, list) {
|
||||
@@ -245,24 +238,6 @@ void search_manager(const char *path, int depth, struct list_head *uid_data)
|
||||
pr_err("Failed to open directory: %s, err: %ld\n", pos->dirpath, PTR_ERR(file));
|
||||
goto skip_iterate;
|
||||
}
|
||||
|
||||
// grab magic on first folder, which is /data/app
|
||||
if (!data_app_magic) {
|
||||
if (file->f_inode->i_sb->s_magic) {
|
||||
data_app_magic = file->f_inode->i_sb->s_magic;
|
||||
pr_info("%s: dir: %s got magic! 0x%lx\n", __func__, pos->dirpath, data_app_magic);
|
||||
} else {
|
||||
filp_close(file, NULL);
|
||||
goto skip_iterate;
|
||||
}
|
||||
}
|
||||
|
||||
if (file->f_inode->i_sb->s_magic != data_app_magic) {
|
||||
pr_info("%s: skip: %s magic: 0x%lx expected: 0x%lx\n", __func__, pos->dirpath,
|
||||
file->f_inode->i_sb->s_magic, data_app_magic);
|
||||
filp_close(file, NULL);
|
||||
goto skip_iterate;
|
||||
}
|
||||
|
||||
iterate_dir(file, &ctx.ctx);
|
||||
filp_close(file, NULL);
|
||||
@@ -376,14 +351,12 @@ void track_throne()
|
||||
if (ksu_is_manager_uid_valid()) {
|
||||
pr_info("manager is uninstalled, invalidate it!\n");
|
||||
ksu_invalidate_manager_uid();
|
||||
goto prune;
|
||||
}
|
||||
pr_info("Searching manager...\n");
|
||||
search_manager("/data/app", 2, &uid_list);
|
||||
pr_info("Search manager finished\n");
|
||||
}
|
||||
|
||||
prune:
|
||||
// then prune the allowlist
|
||||
ksu_prune_allowlist(is_uid_exist, &uid_list);
|
||||
out:
|
||||
|
||||
3
manager/.gitignore
vendored
3
manager/.gitignore
vendored
@@ -1,10 +1,9 @@
|
||||
*.iml
|
||||
.gradle
|
||||
local.properties
|
||||
.idea
|
||||
.kotlin
|
||||
.DS_Store
|
||||
build
|
||||
captures
|
||||
.cxx
|
||||
local.properties
|
||||
key.jks
|
||||
|
||||
@@ -1,7 +1,4 @@
|
||||
@file:Suppress("UnstableApiUsage")
|
||||
|
||||
import com.android.build.gradle.internal.api.BaseVariantOutputImpl
|
||||
import com.android.build.gradle.tasks.PackageAndroidArtifact
|
||||
|
||||
plugins {
|
||||
alias(libs.plugins.agp.app)
|
||||
@@ -49,13 +46,7 @@ android {
|
||||
useLegacyPackaging = true
|
||||
}
|
||||
resources {
|
||||
// https://stackoverflow.com/a/58956288
|
||||
// It will break Layout Inspector, but it's unused for release build.
|
||||
excludes += "META-INF/*.version"
|
||||
// https://github.com/Kotlin/kotlinx.coroutines?tab=readme-ov-file#avoiding-including-the-debug-infrastructure-in-the-resulting-apk
|
||||
excludes += "DebugProbesKt.bin"
|
||||
// https://issueantenna.com/repo/kotlin/kotlinx.coroutines/issues/3158
|
||||
excludes += "kotlin-tooling-metadata.json"
|
||||
excludes += "/META-INF/{AL2.0,LGPL2.1}"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,30 +61,13 @@ android {
|
||||
val output = it as BaseVariantOutputImpl
|
||||
output.outputFileName = "KernelSU_${managerVersionName}_${managerVersionCode}-$name.apk"
|
||||
}
|
||||
|
||||
kotlin.sourceSets {
|
||||
getByName(name) {
|
||||
kotlin.srcDir("build/generated/ksp/$name/kotlin")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// https://stackoverflow.com/a/77745844
|
||||
tasks.withType<PackageAndroidArtifact> {
|
||||
doFirst { appMetadata.asFile.orNull?.writeText("") }
|
||||
}
|
||||
|
||||
dependenciesInfo {
|
||||
includeInApk = false
|
||||
includeInBundle = false
|
||||
}
|
||||
|
||||
androidResources {
|
||||
generateLocaleConfig = true
|
||||
}
|
||||
}
|
||||
|
||||
ksp {
|
||||
arg("compose-destinations.defaultTransitions", "none")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
@@ -114,7 +88,12 @@ dependencies {
|
||||
implementation(libs.androidx.lifecycle.runtime.ktx)
|
||||
implementation(libs.androidx.lifecycle.viewmodel.compose)
|
||||
|
||||
implementation(libs.compose.destinations.core)
|
||||
implementation(libs.com.google.accompanist.drawablepainter)
|
||||
implementation(libs.com.google.accompanist.navigation.animation)
|
||||
implementation(libs.com.google.accompanist.systemuicontroller)
|
||||
implementation(libs.com.google.accompanist.webview)
|
||||
|
||||
implementation(libs.compose.destinations.animations.core)
|
||||
ksp(libs.compose.destinations.ksp)
|
||||
|
||||
implementation(libs.com.github.topjohnwu.libsu.core)
|
||||
|
||||
9
manager/app/proguard-rules.pro
vendored
9
manager/app/proguard-rules.pro
vendored
@@ -0,0 +1,9 @@
|
||||
-dontwarn org.bouncycastle.jsse.BCSSLParameters
|
||||
-dontwarn org.bouncycastle.jsse.BCSSLSocket
|
||||
-dontwarn org.bouncycastle.jsse.provider.BouncyCastleJsseProvider
|
||||
-dontwarn org.conscrypt.Conscrypt$Version
|
||||
-dontwarn org.conscrypt.Conscrypt
|
||||
-dontwarn org.conscrypt.ConscryptHostnameVerifier
|
||||
-dontwarn org.openjsse.javax.net.ssl.SSLParameters
|
||||
-dontwarn org.openjsse.javax.net.ssl.SSLSocket
|
||||
-dontwarn org.openjsse.net.ssl.OpenJSSE
|
||||
|
||||
@@ -12,8 +12,8 @@
|
||||
android:fullBackupContent="@xml/backup_rules"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
android:networkSecurityConfig="@xml/network_security_config"
|
||||
android:supportsRtl="true"
|
||||
android:networkSecurityConfig="@xml/network_security_config"
|
||||
android:theme="@style/Theme.KernelSU"
|
||||
tools:targetApi="34">
|
||||
<activity
|
||||
@@ -22,12 +22,16 @@
|
||||
android:theme="@style/Theme.KernelSU">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
|
||||
<meta-data
|
||||
android:name="android.app.lib_name"
|
||||
android:value="" />
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
android:name=".ui.webui.WebUIActivity"
|
||||
<activity android:name=".ui.webui.WebUIActivity"
|
||||
android:autoRemoveFromRecents="true"
|
||||
android:documentLaunchMode="intoExisting"
|
||||
android:exported="false"
|
||||
|
||||
@@ -295,14 +295,4 @@ extern "C"
|
||||
JNIEXPORT jboolean JNICALL
|
||||
Java_me_weishu_kernelsu_Natives_uidShouldUmount(JNIEnv *env, jobject thiz, jint uid) {
|
||||
return uid_should_umount(uid);
|
||||
}
|
||||
extern "C"
|
||||
JNIEXPORT jboolean JNICALL
|
||||
Java_me_weishu_kernelsu_Natives_isSuEnabled(JNIEnv *env, jobject thiz) {
|
||||
return is_su_enabled();
|
||||
}
|
||||
extern "C"
|
||||
JNIEXPORT jboolean JNICALL
|
||||
Java_me_weishu_kernelsu_Natives_setSuEnabled(JNIEnv *env, jobject thiz, jboolean enabled) {
|
||||
return set_su_enabled(enabled);
|
||||
}
|
||||
@@ -27,8 +27,6 @@
|
||||
|
||||
#define CMD_IS_UID_GRANTED_ROOT 12
|
||||
#define CMD_IS_UID_SHOULD_UMOUNT 13
|
||||
#define CMD_IS_SU_ENABLED 14
|
||||
#define CMD_ENABLE_SU 15
|
||||
|
||||
static bool ksuctl(int cmd, void* arg1, void* arg2) {
|
||||
int32_t result = 0;
|
||||
@@ -50,13 +48,12 @@ bool become_manager(const char* pkg) {
|
||||
}
|
||||
|
||||
// cache the result to avoid unnecessary syscall
|
||||
static bool is_lkm = false;
|
||||
|
||||
int get_version(void) {
|
||||
static bool is_lkm;
|
||||
int get_version() {
|
||||
int32_t version = -1;
|
||||
int32_t flags = 0;
|
||||
ksuctl(CMD_GET_VERSION, &version, &flags);
|
||||
if (!is_lkm && (flags & 0x1)) {
|
||||
int32_t lkm = 0;
|
||||
ksuctl(CMD_GET_VERSION, &version, &lkm);
|
||||
if (!is_lkm && lkm != 0) {
|
||||
is_lkm = true;
|
||||
}
|
||||
return version;
|
||||
@@ -87,14 +84,3 @@ bool set_app_profile(const app_profile *profile) {
|
||||
bool get_app_profile(p_key_t key, app_profile *profile) {
|
||||
return ksuctl(CMD_GET_APP_PROFILE, (void*) profile, nullptr);
|
||||
}
|
||||
|
||||
bool set_su_enabled(bool enabled) {
|
||||
return ksuctl(CMD_ENABLE_SU, (void*) enabled, nullptr);
|
||||
}
|
||||
|
||||
bool is_su_enabled() {
|
||||
bool enabled = true;
|
||||
// if ksuctl failed, we assume su is enabled, and it cannot be disabled.
|
||||
ksuctl(CMD_IS_SU_ENABLED, &enabled, nullptr);
|
||||
return enabled;
|
||||
}
|
||||
|
||||
@@ -79,8 +79,4 @@ bool set_app_profile(const app_profile *profile);
|
||||
|
||||
bool get_app_profile(p_key_t key, app_profile *profile);
|
||||
|
||||
bool set_su_enabled(bool enabled);
|
||||
|
||||
bool is_su_enabled();
|
||||
|
||||
#endif //KERNELSU_KSU_H
|
||||
|
||||
@@ -1,22 +1,16 @@
|
||||
package me.weishu.kernelsu
|
||||
|
||||
import android.app.Application
|
||||
import android.system.Os
|
||||
import coil.Coil
|
||||
import coil.ImageLoader
|
||||
import me.zhanghai.android.appiconloader.coil.AppIconFetcher
|
||||
import me.zhanghai.android.appiconloader.coil.AppIconKeyer
|
||||
import okhttp3.Cache
|
||||
import okhttp3.OkHttpClient
|
||||
import java.io.File
|
||||
import java.util.Locale
|
||||
|
||||
lateinit var ksuApp: KernelSUApplication
|
||||
|
||||
class KernelSUApplication : Application() {
|
||||
|
||||
lateinit var okhttpClient: OkHttpClient
|
||||
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
ksuApp = this
|
||||
@@ -36,20 +30,7 @@ class KernelSUApplication : Application() {
|
||||
if (!webroot.exists()) {
|
||||
webroot.mkdir()
|
||||
}
|
||||
|
||||
// Provide working env for rust's temp_dir()
|
||||
Os.setenv("TMPDIR", cacheDir.absolutePath, true)
|
||||
|
||||
okhttpClient =
|
||||
OkHttpClient.Builder().cache(Cache(File(cacheDir, "okhttp"), 10 * 1024 * 1024))
|
||||
.addInterceptor { block ->
|
||||
block.proceed(
|
||||
block.request().newBuilder()
|
||||
.header("User-Agent", "KernelSU/${BuildConfig.VERSION_CODE}")
|
||||
.header("Accept-Language", Locale.getDefault().toLanguageTag()).build()
|
||||
)
|
||||
}.build()
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -21,9 +21,6 @@ object Natives {
|
||||
// 11640: Support query working mode, LKM or GKI
|
||||
// when MINIMAL_SUPPORTED_KERNEL > 11640, we can remove this constant.
|
||||
const val MINIMAL_SUPPORTED_KERNEL_LKM = 11648
|
||||
|
||||
// 12040: Support disable sucompat mode
|
||||
const val MINIMAL_SUPPORTED_SU_COMPAT = 12040
|
||||
const val KERNEL_SU_DOMAIN = "u:r:su:s0"
|
||||
|
||||
const val ROOT_UID = 0
|
||||
@@ -58,15 +55,6 @@ object Natives {
|
||||
external fun getAppProfile(key: String?, uid: Int): Profile
|
||||
external fun setAppProfile(profile: Profile?): Boolean
|
||||
|
||||
/**
|
||||
* `su` compat mode can be disabled temporarily.
|
||||
* 0: disabled
|
||||
* 1: enabled
|
||||
* negative : error
|
||||
*/
|
||||
external fun isSuEnabled(): Boolean
|
||||
external fun setSuEnabled(enabled: Boolean): Boolean
|
||||
|
||||
private const val NON_ROOT_DEFAULT_PROFILE_KEY = "$"
|
||||
private const val NOBODY_UID = 9999
|
||||
|
||||
|
||||
@@ -1,30 +1,14 @@
|
||||
package me.weishu.kernelsu.ui
|
||||
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
import androidx.compose.animation.AnimatedContentTransitionScope
|
||||
import androidx.compose.animation.EnterTransition
|
||||
import androidx.compose.animation.ExitTransition
|
||||
import androidx.compose.animation.core.tween
|
||||
import androidx.compose.animation.fadeIn
|
||||
import androidx.compose.animation.fadeOut
|
||||
import androidx.compose.animation.scaleOut
|
||||
import androidx.compose.animation.slideInHorizontally
|
||||
import androidx.compose.animation.slideOutHorizontally
|
||||
import androidx.compose.foundation.layout.WindowInsets
|
||||
import androidx.compose.foundation.layout.WindowInsetsSides
|
||||
import androidx.compose.foundation.layout.displayCutout
|
||||
import androidx.compose.foundation.layout.only
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.systemBars
|
||||
import androidx.compose.foundation.layout.union
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.NavigationBar
|
||||
import androidx.compose.material3.NavigationBarItem
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.SnackbarHost
|
||||
import androidx.compose.material3.SnackbarHostState
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
@@ -34,96 +18,39 @@ import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.navigation.NavBackStackEntry
|
||||
import androidx.navigation.NavHostController
|
||||
import androidx.navigation.compose.rememberNavController
|
||||
import com.ramcosta.composedestinations.DestinationsNavHost
|
||||
import com.ramcosta.composedestinations.animations.NavHostAnimatedDestinationStyle
|
||||
import com.ramcosta.composedestinations.generated.NavGraphs
|
||||
import com.ramcosta.composedestinations.navigation.popBackStack
|
||||
import com.ramcosta.composedestinations.utils.isRouteOnBackStackAsState
|
||||
import com.ramcosta.composedestinations.utils.rememberDestinationsNavigator
|
||||
import me.weishu.kernelsu.Natives
|
||||
import me.weishu.kernelsu.ksuApp
|
||||
import me.weishu.kernelsu.ui.screen.BottomBarDestination
|
||||
import me.weishu.kernelsu.ui.screen.NavGraphs
|
||||
import me.weishu.kernelsu.ui.theme.KernelSUTheme
|
||||
import me.weishu.kernelsu.ui.util.LocalSnackbarHost
|
||||
import me.weishu.kernelsu.ui.util.rootAvailable
|
||||
import me.weishu.kernelsu.ui.util.install
|
||||
|
||||
class MainActivity : ComponentActivity() {
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
|
||||
// Enable edge to edge
|
||||
enableEdgeToEdge()
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||
window.isNavigationBarContrastEnforced = false
|
||||
}
|
||||
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
val isManager = Natives.becomeManager(ksuApp.packageName)
|
||||
if (isManager) install()
|
||||
|
||||
setContent {
|
||||
KernelSUTheme {
|
||||
val navController = rememberNavController()
|
||||
val snackBarHostState = remember { SnackbarHostState() }
|
||||
val bottomBarRoutes = remember {
|
||||
BottomBarDestination.entries.map { it.direction.route }.toSet()
|
||||
}
|
||||
val snackbarHostState = remember { SnackbarHostState() }
|
||||
Scaffold(
|
||||
bottomBar = { BottomBar(navController) },
|
||||
contentWindowInsets = WindowInsets(0, 0, 0, 0)
|
||||
snackbarHost = { SnackbarHost(snackbarHostState) }
|
||||
) { innerPadding ->
|
||||
CompositionLocalProvider(
|
||||
LocalSnackbarHost provides snackBarHostState,
|
||||
LocalSnackbarHost provides snackbarHostState,
|
||||
) {
|
||||
DestinationsNavHost(
|
||||
modifier = Modifier.padding(innerPadding),
|
||||
navGraph = NavGraphs.root,
|
||||
navController = navController,
|
||||
defaultTransitions = object : NavHostAnimatedDestinationStyle() {
|
||||
override val enterTransition: AnimatedContentTransitionScope<NavBackStackEntry>.() -> EnterTransition = {
|
||||
// If the target is a detail page (not a bottom navigation page), slide in from the right
|
||||
if (targetState.destination.route !in bottomBarRoutes) {
|
||||
slideInHorizontally(initialOffsetX = { it })
|
||||
} else {
|
||||
// Otherwise (switching between bottom navigation pages), use fade in
|
||||
fadeIn(animationSpec = tween(340))
|
||||
}
|
||||
}
|
||||
|
||||
override val exitTransition: AnimatedContentTransitionScope<NavBackStackEntry>.() -> ExitTransition = {
|
||||
// If navigating from the home page (bottom navigation page) to a detail page, slide out to the left
|
||||
if (initialState.destination.route in bottomBarRoutes && targetState.destination.route !in bottomBarRoutes) {
|
||||
slideOutHorizontally(targetOffsetX = { -it / 4 }) + fadeOut()
|
||||
} else {
|
||||
// Otherwise (switching between bottom navigation pages), use fade out
|
||||
fadeOut(animationSpec = tween(340))
|
||||
}
|
||||
}
|
||||
|
||||
override val popEnterTransition: AnimatedContentTransitionScope<NavBackStackEntry>.() -> EnterTransition = {
|
||||
// If returning to the home page (bottom navigation page), slide in from the left
|
||||
if (targetState.destination.route in bottomBarRoutes) {
|
||||
slideInHorizontally(initialOffsetX = { -it / 4 }) + fadeIn()
|
||||
} else {
|
||||
// Otherwise (e.g., returning between multiple detail pages), use default fade in
|
||||
fadeIn(animationSpec = tween(340))
|
||||
}
|
||||
}
|
||||
|
||||
override val popExitTransition: AnimatedContentTransitionScope<NavBackStackEntry>.() -> ExitTransition = {
|
||||
// If returning from a detail page (not a bottom navigation page), scale down and fade out
|
||||
if (initialState.destination.route !in bottomBarRoutes) {
|
||||
scaleOut(targetScale = 0.9f) + fadeOut()
|
||||
} else {
|
||||
// Otherwise, use default fade out
|
||||
fadeOut(animationSpec = tween(340))
|
||||
}
|
||||
}
|
||||
}
|
||||
navController = navController
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -134,15 +61,9 @@ class MainActivity : ComponentActivity() {
|
||||
|
||||
@Composable
|
||||
private fun BottomBar(navController: NavHostController) {
|
||||
val navigator = navController.rememberDestinationsNavigator()
|
||||
val isManager = Natives.becomeManager(ksuApp.packageName)
|
||||
val fullFeatured = isManager && !Natives.requireNewKernel() && rootAvailable()
|
||||
NavigationBar(
|
||||
tonalElevation = 8.dp,
|
||||
windowInsets = WindowInsets.systemBars.union(WindowInsets.displayCutout).only(
|
||||
WindowInsetsSides.Horizontal + WindowInsetsSides.Bottom
|
||||
)
|
||||
) {
|
||||
NavigationBar(tonalElevation = 8.dp) {
|
||||
BottomBarDestination.entries.forEach { destination ->
|
||||
if (!fullFeatured && destination.rootRequired) return@forEach
|
||||
val isCurrentDestOnBackStack by navController.isRouteOnBackStackAsState(destination.direction)
|
||||
@@ -150,10 +71,11 @@ private fun BottomBar(navController: NavHostController) {
|
||||
selected = isCurrentDestOnBackStack,
|
||||
onClick = {
|
||||
if (isCurrentDestOnBackStack) {
|
||||
navigator.popBackStack(destination.direction, false)
|
||||
navController.popBackStack(destination.direction, false)
|
||||
}
|
||||
navigator.navigate(destination.direction) {
|
||||
popUpTo(NavGraphs.root) {
|
||||
|
||||
navController.navigate(destination.direction.route) {
|
||||
popUpTo(NavGraphs.root.route) {
|
||||
saveState = true
|
||||
}
|
||||
launchSingleTop = true
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package me.weishu.kernelsu.ui.component
|
||||
|
||||
import android.text.method.LinkMovementMethod
|
||||
import android.widget.TextView
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
@@ -9,28 +11,24 @@ import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.ElevatedCard
|
||||
import androidx.compose.material3.LocalContentColor
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.scale
|
||||
import androidx.compose.ui.res.colorResource
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.graphics.toArgb
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.AnnotatedString
|
||||
import androidx.compose.ui.text.SpanStyle
|
||||
import androidx.compose.ui.text.TextLinkStyles
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.text.fromHtml
|
||||
import androidx.compose.ui.text.style.TextDecoration
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import androidx.compose.ui.viewinterop.AndroidView
|
||||
import androidx.compose.ui.window.Dialog
|
||||
import androidx.core.content.res.ResourcesCompat
|
||||
import androidx.core.text.HtmlCompat
|
||||
import com.google.accompanist.drawablepainter.rememberDrawablePainter
|
||||
import me.weishu.kernelsu.BuildConfig
|
||||
import me.weishu.kernelsu.R
|
||||
|
||||
@@ -38,8 +36,9 @@ import me.weishu.kernelsu.R
|
||||
@Composable
|
||||
fun AboutCard() {
|
||||
ElevatedCard(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
shape = RoundedCornerShape(8.dp)
|
||||
modifier = Modifier
|
||||
.fillMaxWidth(),
|
||||
shape = RoundedCornerShape(8.dp),
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
@@ -53,9 +52,7 @@ fun AboutCard() {
|
||||
|
||||
@Composable
|
||||
fun AboutDialog(dismiss: () -> Unit) {
|
||||
Dialog(
|
||||
onDismissRequest = { dismiss() }
|
||||
) {
|
||||
Dialog(onDismissRequest = { dismiss() }) {
|
||||
AboutCard()
|
||||
}
|
||||
}
|
||||
@@ -63,20 +60,21 @@ fun AboutDialog(dismiss: () -> Unit) {
|
||||
@Composable
|
||||
private fun AboutCardContent() {
|
||||
Column(
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
) {
|
||||
val drawable = ResourcesCompat.getDrawable(
|
||||
LocalContext.current.resources,
|
||||
R.mipmap.ic_launcher,
|
||||
LocalContext.current.theme
|
||||
)
|
||||
|
||||
Row {
|
||||
Surface(
|
||||
modifier = Modifier.size(40.dp),
|
||||
color = colorResource(id = R.color.ic_launcher_background),
|
||||
shape = CircleShape
|
||||
) {
|
||||
Image(
|
||||
painter = painterResource(id = R.drawable.ic_launcher_foreground),
|
||||
contentDescription = "icon",
|
||||
modifier = Modifier.scale(1.4f)
|
||||
)
|
||||
}
|
||||
Image(
|
||||
painter = rememberDrawablePainter(drawable),
|
||||
contentDescription = "icon",
|
||||
modifier = Modifier.size(40.dp)
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.width(12.dp))
|
||||
|
||||
@@ -95,31 +93,31 @@ private fun AboutCardContent() {
|
||||
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
|
||||
val annotatedString = AnnotatedString.Companion.fromHtml(
|
||||
htmlString = stringResource(
|
||||
HtmlText(
|
||||
html = stringResource(
|
||||
id = R.string.about_source_code,
|
||||
"<b><a href=\"https://github.com/tiann/KernelSU\">GitHub</a></b>",
|
||||
"<b><a href=\"https://t.me/KernelSU\">Telegram</a></b>"
|
||||
),
|
||||
linkStyles = TextLinkStyles(
|
||||
style = SpanStyle(
|
||||
color = MaterialTheme.colorScheme.primary,
|
||||
textDecoration = TextDecoration.Underline
|
||||
),
|
||||
pressedStyle = SpanStyle(
|
||||
color = MaterialTheme.colorScheme.primary,
|
||||
background = MaterialTheme.colorScheme.secondaryContainer,
|
||||
textDecoration = TextDecoration.Underline
|
||||
)
|
||||
)
|
||||
)
|
||||
Text(
|
||||
text = annotatedString,
|
||||
style = TextStyle(
|
||||
fontSize = 14.sp
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun HtmlText(html: String, modifier: Modifier = Modifier) {
|
||||
val contentColor = LocalContentColor.current
|
||||
AndroidView(
|
||||
modifier = modifier,
|
||||
factory = { context ->
|
||||
TextView(context).also {
|
||||
it.movementMethod = LinkMovementMethod.getInstance()
|
||||
}
|
||||
},
|
||||
update = {
|
||||
it.text = HtmlCompat.fromHtml(html, HtmlCompat.FROM_HTML_MODE_COMPACT)
|
||||
it.setTextColor(contentColor.toArgb())
|
||||
}
|
||||
)
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
package me.weishu.kernelsu.ui.component
|
||||
|
||||
import android.graphics.text.LineBreaker
|
||||
import android.os.Build
|
||||
import android.os.Parcelable
|
||||
import android.text.Layout
|
||||
import android.text.method.LinkMovementMethod
|
||||
@@ -97,8 +96,8 @@ interface ConfirmDialogHandle : DialogHandle {
|
||||
}
|
||||
|
||||
private abstract class DialogHandleBase(
|
||||
val visible: MutableState<Boolean>,
|
||||
val coroutineScope: CoroutineScope
|
||||
protected val visible: MutableState<Boolean>,
|
||||
protected val coroutineScope: CoroutineScope
|
||||
) : DialogHandle {
|
||||
override val isShown: Boolean
|
||||
get() = visible.value
|
||||
@@ -433,9 +432,7 @@ private fun MarkdownContent(content: String) {
|
||||
TextView(context).apply {
|
||||
movementMethod = LinkMovementMethod.getInstance()
|
||||
setSpannableFactory(NoCopySpannableFactory.getInstance())
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||
breakStrategy = LineBreaker.BREAK_STRATEGY_SIMPLE
|
||||
}
|
||||
breakStrategy = LineBreaker.BREAK_STRATEGY_SIMPLE
|
||||
hyphenationFrequency = Layout.HYPHENATION_FREQUENCY_NONE
|
||||
layoutParams = ViewGroup.LayoutParams(
|
||||
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
package me.weishu.kernelsu.ui.component
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import me.weishu.kernelsu.Natives
|
||||
import me.weishu.kernelsu.ksuApp
|
||||
|
||||
@Composable
|
||||
fun KsuIsValid(
|
||||
content: @Composable () -> Unit
|
||||
) {
|
||||
val isManager = Natives.becomeManager(ksuApp.packageName)
|
||||
val ksuVersion = if (isManager) Natives.version else null
|
||||
|
||||
if (ksuVersion != null) {
|
||||
content()
|
||||
}
|
||||
}
|
||||
@@ -5,12 +5,8 @@ import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.animation.fadeIn
|
||||
import androidx.compose.animation.fadeOut
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.WindowInsets
|
||||
import androidx.compose.foundation.layout.WindowInsetsSides
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.only
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.safeDrawing
|
||||
import androidx.compose.foundation.text.KeyboardActions
|
||||
import androidx.compose.foundation.text.KeyboardOptions
|
||||
import androidx.compose.material.icons.Icons
|
||||
@@ -23,7 +19,6 @@ import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.OutlinedTextField
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TopAppBar
|
||||
import androidx.compose.material3.TopAppBarScrollBehavior
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.DisposableEffect
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
@@ -53,7 +48,6 @@ fun SearchAppBar(
|
||||
onBackClick: (() -> Unit)? = null,
|
||||
onConfirm: (() -> Unit)? = null,
|
||||
dropdownContent: @Composable (() -> Unit)? = null,
|
||||
scrollBehavior: TopAppBarScrollBehavior? = null
|
||||
) {
|
||||
val keyboardController = LocalSoftwareKeyboardController.current
|
||||
val focusRequester = remember { FocusRequester() }
|
||||
@@ -138,13 +132,10 @@ fun SearchAppBar(
|
||||
dropdownContent()
|
||||
}
|
||||
|
||||
},
|
||||
windowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal),
|
||||
scrollBehavior = scrollBehavior
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Preview
|
||||
@Composable
|
||||
private fun SearchAppBarPreview() {
|
||||
|
||||
@@ -1,18 +1,14 @@
|
||||
package me.weishu.kernelsu.ui.component
|
||||
|
||||
import androidx.compose.foundation.LocalIndication
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.selection.toggleable
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.ListItem
|
||||
import androidx.compose.material3.RadioButton
|
||||
import androidx.compose.material3.Switch
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.semantics.Role
|
||||
|
||||
@Composable
|
||||
fun SwitchItem(
|
||||
@@ -23,18 +19,10 @@ fun SwitchItem(
|
||||
enabled: Boolean = true,
|
||||
onCheckedChange: (Boolean) -> Unit
|
||||
) {
|
||||
val interactionSource = remember { MutableInteractionSource() }
|
||||
|
||||
ListItem(
|
||||
modifier = Modifier
|
||||
.toggleable(
|
||||
value = checked,
|
||||
interactionSource = interactionSource,
|
||||
role = Role.Switch,
|
||||
enabled = enabled,
|
||||
indication = LocalIndication.current,
|
||||
onValueChange = onCheckedChange
|
||||
),
|
||||
modifier = Modifier.clickable {
|
||||
onCheckedChange.invoke(!checked)
|
||||
},
|
||||
headlineContent = {
|
||||
Text(title)
|
||||
},
|
||||
@@ -42,12 +30,7 @@ fun SwitchItem(
|
||||
{ Icon(icon, title) }
|
||||
},
|
||||
trailingContent = {
|
||||
Switch(
|
||||
checked = checked,
|
||||
enabled = enabled,
|
||||
onCheckedChange = onCheckedChange,
|
||||
interactionSource = interactionSource
|
||||
)
|
||||
Switch(checked = checked, enabled = enabled, onCheckedChange = onCheckedChange)
|
||||
},
|
||||
supportingContent = {
|
||||
if (summary != null) {
|
||||
@@ -69,6 +52,6 @@ fun RadioItem(
|
||||
},
|
||||
leadingContent = {
|
||||
RadioButton(selected = selected, onClick = onClick)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
@file:OptIn(ExperimentalMaterial3Api::class)
|
||||
|
||||
package me.weishu.kernelsu.ui.component.profile
|
||||
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.ExperimentalLayoutApi
|
||||
import androidx.compose.foundation.layout.FlowRow
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.text.KeyboardActions
|
||||
@@ -19,14 +20,12 @@ import androidx.compose.material3.ExposedDropdownMenuBox
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.ListItem
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.MenuAnchorType
|
||||
import androidx.compose.material3.OutlinedCard
|
||||
import androidx.compose.material3.OutlinedTextField
|
||||
import androidx.compose.material3.OutlinedTextFieldDefaults
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableIntStateOf
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
@@ -73,7 +72,6 @@ fun RootProfileConfig(
|
||||
)
|
||||
}
|
||||
|
||||
/*
|
||||
var expanded by remember { mutableStateOf(false) }
|
||||
val currentNamespace = when (profile.namespace) {
|
||||
Natives.Profile.Namespace.INHERITED.ordinal -> stringResource(R.string.profile_namespace_inherited)
|
||||
@@ -88,7 +86,7 @@ fun RootProfileConfig(
|
||||
) {
|
||||
OutlinedTextField(
|
||||
modifier = Modifier
|
||||
.menuAnchor(MenuAnchorType.PrimaryNotEditable)
|
||||
.menuAnchor()
|
||||
.fillMaxWidth(),
|
||||
readOnly = true,
|
||||
label = { Text(stringResource(R.string.profile_namespace)) },
|
||||
@@ -127,7 +125,6 @@ fun RootProfileConfig(
|
||||
}
|
||||
}
|
||||
})
|
||||
*/
|
||||
|
||||
UidPanel(uid = profile.uid, label = "uid", onUidChange = {
|
||||
onProfileChange(
|
||||
@@ -187,7 +184,7 @@ fun RootProfileConfig(
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalLayoutApi::class, ExperimentalMaterial3Api::class)
|
||||
@OptIn(ExperimentalLayoutApi::class)
|
||||
@Composable
|
||||
fun GroupsPanel(selected: List<Groups>, closeSelection: (selection: Set<Groups>) -> Unit) {
|
||||
val selectGroupsDialog = rememberCustomDialog { dismiss: () -> Unit ->
|
||||
@@ -237,20 +234,14 @@ fun GroupsPanel(selected: List<Groups>, closeSelection: (selection: Set<Groups>)
|
||||
)
|
||||
}
|
||||
|
||||
OutlinedCard(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(16.dp)
|
||||
) {
|
||||
OutlinedCard(modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(16.dp)
|
||||
.clickable {
|
||||
selectGroupsDialog.show()
|
||||
}) {
|
||||
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.clickable {
|
||||
selectGroupsDialog.show()
|
||||
}
|
||||
.padding(16.dp)
|
||||
) {
|
||||
Column(modifier = Modifier.padding(16.dp)) {
|
||||
Text(stringResource(R.string.profile_groups))
|
||||
FlowRow {
|
||||
selected.forEach { group ->
|
||||
@@ -265,7 +256,7 @@ fun GroupsPanel(selected: List<Groups>, closeSelection: (selection: Set<Groups>)
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalLayoutApi::class, ExperimentalMaterial3Api::class)
|
||||
@OptIn(ExperimentalLayoutApi::class)
|
||||
@Composable
|
||||
fun CapsPanel(
|
||||
selected: Collection<Capabilities>,
|
||||
@@ -308,20 +299,14 @@ fun CapsPanel(
|
||||
)
|
||||
}
|
||||
|
||||
OutlinedCard(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(16.dp)
|
||||
) {
|
||||
OutlinedCard(modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(16.dp)
|
||||
.clickable {
|
||||
selectCapabilitiesDialog.show()
|
||||
}) {
|
||||
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.clickable {
|
||||
selectCapabilitiesDialog.show()
|
||||
}
|
||||
.padding(16.dp)
|
||||
) {
|
||||
Column(modifier = Modifier.padding(16.dp)) {
|
||||
Text(stringResource(R.string.profile_capabilities))
|
||||
FlowRow {
|
||||
selected.forEach { group ->
|
||||
@@ -344,10 +329,10 @@ private fun UidPanel(uid: Int, label: String, onUidChange: (Int) -> Unit) {
|
||||
mutableStateOf(false)
|
||||
}
|
||||
var lastValidUid by remember {
|
||||
mutableIntStateOf(uid)
|
||||
mutableStateOf(uid)
|
||||
}
|
||||
val keyboardController = LocalSoftwareKeyboardController.current
|
||||
|
||||
val keyboardController = LocalSoftwareKeyboardController.current
|
||||
OutlinedTextField(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
label = { Text(label) },
|
||||
@@ -380,7 +365,6 @@ private fun UidPanel(uid: Int, label: String, onUidChange: (Int) -> Unit) {
|
||||
})
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
private fun SELinuxPanel(
|
||||
profile: Natives.Profile,
|
||||
@@ -468,7 +452,7 @@ private fun SELinuxPanel(
|
||||
),
|
||||
label = { Text(text = stringResource(R.string.profile_selinux_context)) },
|
||||
value = profile.context,
|
||||
onValueChange = { }
|
||||
onValueChange = { },
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -12,7 +12,6 @@ import androidx.compose.material3.ExposedDropdownMenuBox
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.ListItem
|
||||
import androidx.compose.material3.MenuAnchorType
|
||||
import androidx.compose.material3.OutlinedTextField
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
@@ -55,7 +54,7 @@ fun TemplateConfig(
|
||||
) {
|
||||
OutlinedTextField(
|
||||
modifier = Modifier
|
||||
.menuAnchor(MenuAnchorType.PrimaryNotEditable)
|
||||
.menuAnchor()
|
||||
.fillMaxWidth(),
|
||||
readOnly = true,
|
||||
label = { Text(stringResource(R.string.profile_template)) },
|
||||
|
||||
@@ -7,14 +7,10 @@ import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.BoxWithConstraints
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.WindowInsets
|
||||
import androidx.compose.foundation.layout.WindowInsetsSides
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.only
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.safeDrawing
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
@@ -32,11 +28,8 @@ import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.ListItem
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.SnackbarHost
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TopAppBar
|
||||
import androidx.compose.material3.TopAppBarDefaults
|
||||
import androidx.compose.material3.TopAppBarScrollBehavior
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
@@ -46,7 +39,6 @@ import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.geometry.Offset
|
||||
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||
import androidx.compose.ui.input.pointer.pointerInput
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
@@ -55,13 +47,9 @@ import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.DpOffset
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.compose.dropUnlessResumed
|
||||
import coil.compose.AsyncImage
|
||||
import coil.request.ImageRequest
|
||||
import com.ramcosta.composedestinations.annotation.Destination
|
||||
import com.ramcosta.composedestinations.annotation.RootGraph
|
||||
import com.ramcosta.composedestinations.generated.destinations.AppProfileTemplateScreenDestination
|
||||
import com.ramcosta.composedestinations.generated.destinations.TemplateEditorScreenDestination
|
||||
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
|
||||
import kotlinx.coroutines.launch
|
||||
import me.weishu.kernelsu.Natives
|
||||
@@ -70,6 +58,8 @@ import me.weishu.kernelsu.ui.component.SwitchItem
|
||||
import me.weishu.kernelsu.ui.component.profile.AppProfileConfig
|
||||
import me.weishu.kernelsu.ui.component.profile.RootProfileConfig
|
||||
import me.weishu.kernelsu.ui.component.profile.TemplateConfig
|
||||
import me.weishu.kernelsu.ui.screen.destinations.AppProfileTemplateScreenDestination
|
||||
import me.weishu.kernelsu.ui.screen.destinations.TemplateEditorScreenDestination
|
||||
import me.weishu.kernelsu.ui.util.LocalSnackbarHost
|
||||
import me.weishu.kernelsu.ui.util.forceStopApp
|
||||
import me.weishu.kernelsu.ui.util.getSepolicy
|
||||
@@ -83,20 +73,19 @@ import me.weishu.kernelsu.ui.viewmodel.getTemplateInfoById
|
||||
* @author weishu
|
||||
* @date 2023/5/16.
|
||||
*/
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Destination<RootGraph>
|
||||
@Destination
|
||||
@Composable
|
||||
fun AppProfileScreen(
|
||||
navigator: DestinationsNavigator,
|
||||
appInfo: SuperUserViewModel.AppInfo,
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
val snackBarHost = LocalSnackbarHost.current
|
||||
val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior()
|
||||
val snackbarHost = LocalSnackbarHost.current
|
||||
val scope = rememberCoroutineScope()
|
||||
val failToUpdateAppProfile = stringResource(R.string.failed_to_update_app_profile).format(appInfo.label)
|
||||
val failToUpdateSepolicy = stringResource(R.string.failed_to_update_sepolicy).format(appInfo.label)
|
||||
val suNotAllowed = stringResource(R.string.su_not_allowed).format(appInfo.label)
|
||||
val failToUpdateAppProfile =
|
||||
stringResource(R.string.failed_to_update_app_profile).format(appInfo.label)
|
||||
val failToUpdateSepolicy =
|
||||
stringResource(R.string.failed_to_update_sepolicy).format(appInfo.label)
|
||||
|
||||
val packageName = appInfo.packageName
|
||||
val initialProfile = Natives.getAppProfile(packageName, appInfo.uid)
|
||||
@@ -108,25 +97,18 @@ fun AppProfileScreen(
|
||||
}
|
||||
|
||||
Scaffold(
|
||||
topBar = {
|
||||
TopBar(
|
||||
onBack = dropUnlessResumed { navigator.popBackStack() },
|
||||
scrollBehavior = scrollBehavior
|
||||
)
|
||||
},
|
||||
snackbarHost = { SnackbarHost(hostState = snackBarHost) },
|
||||
contentWindowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal)
|
||||
topBar = { TopBar { navigator.popBackStack() } },
|
||||
) { paddingValues ->
|
||||
AppProfileInner(
|
||||
modifier = Modifier
|
||||
.padding(paddingValues)
|
||||
.nestedScroll(scrollBehavior.nestedScrollConnection)
|
||||
.verticalScroll(rememberScrollState()),
|
||||
packageName = appInfo.packageName,
|
||||
appLabel = appInfo.label,
|
||||
appIcon = {
|
||||
AsyncImage(
|
||||
model = ImageRequest.Builder(context).data(appInfo.packageInfo).crossfade(true).build(),
|
||||
model = ImageRequest.Builder(context).data(appInfo.packageInfo).crossfade(true)
|
||||
.build(),
|
||||
contentDescription = appInfo.label,
|
||||
modifier = Modifier
|
||||
.padding(4.dp)
|
||||
@@ -145,19 +127,14 @@ fun AppProfileScreen(
|
||||
},
|
||||
onProfileChange = {
|
||||
scope.launch {
|
||||
if (it.allowSu) {
|
||||
// sync with allowlist.c - forbid_system_uid
|
||||
if (appInfo.uid < 2000 && appInfo.uid != 1000) {
|
||||
snackBarHost.showSnackbar(suNotAllowed)
|
||||
return@launch
|
||||
}
|
||||
if (!it.rootUseDefault && it.rules.isNotEmpty() && !setSepolicy(profile.name, it.rules)) {
|
||||
snackBarHost.showSnackbar(failToUpdateSepolicy)
|
||||
if (it.allowSu && !it.rootUseDefault && it.rules.isNotEmpty()) {
|
||||
if (!setSepolicy(profile.name, it.rules)) {
|
||||
snackbarHost.showSnackbar(failToUpdateSepolicy)
|
||||
return@launch
|
||||
}
|
||||
}
|
||||
if (!Natives.setAppProfile(it)) {
|
||||
snackBarHost.showSnackbar(failToUpdateAppProfile.format(appInfo.uid))
|
||||
snackbarHost.showSnackbar(failToUpdateAppProfile.format(appInfo.uid))
|
||||
} else {
|
||||
profile = it
|
||||
}
|
||||
@@ -197,9 +174,7 @@ private fun AppProfileInner(
|
||||
)
|
||||
|
||||
Crossfade(targetState = isRootGranted, label = "") { current ->
|
||||
Column(
|
||||
modifier = Modifier.padding(bottom = 6.dp + 48.dp + 6.dp /* SnackBar height */)
|
||||
) {
|
||||
Column {
|
||||
if (current) {
|
||||
val initialMode = if (profile.rootUseDefault) {
|
||||
Mode.Default
|
||||
@@ -263,10 +238,7 @@ private enum class Mode(@StringRes private val res: Int) {
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
private fun TopBar(
|
||||
onBack: () -> Unit,
|
||||
scrollBehavior: TopAppBarScrollBehavior? = null
|
||||
) {
|
||||
private fun TopBar(onBack: () -> Unit) {
|
||||
TopAppBar(
|
||||
title = {
|
||||
Text(stringResource(R.string.profile))
|
||||
@@ -276,8 +248,6 @@ private fun TopBar(
|
||||
onClick = onBack
|
||||
) { Icon(Icons.AutoMirrored.Filled.ArrowBack, contentDescription = null) }
|
||||
},
|
||||
windowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal),
|
||||
scrollBehavior = scrollBehavior
|
||||
)
|
||||
}
|
||||
|
||||
@@ -333,8 +303,7 @@ private fun AppMenuBox(packageName: String, content: @Composable () -> Unit) {
|
||||
touchPoint = it
|
||||
expanded = true
|
||||
}
|
||||
}
|
||||
) {
|
||||
}) {
|
||||
|
||||
content()
|
||||
|
||||
|
||||
@@ -5,11 +5,11 @@ import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.*
|
||||
import androidx.compose.material.icons.outlined.*
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import com.ramcosta.composedestinations.generated.destinations.HomeScreenDestination
|
||||
import com.ramcosta.composedestinations.generated.destinations.ModuleScreenDestination
|
||||
import com.ramcosta.composedestinations.generated.destinations.SuperUserScreenDestination
|
||||
import com.ramcosta.composedestinations.spec.DirectionDestinationSpec
|
||||
import me.weishu.kernelsu.R
|
||||
import me.weishu.kernelsu.ui.screen.destinations.HomeScreenDestination
|
||||
import me.weishu.kernelsu.ui.screen.destinations.SuperUserScreenDestination
|
||||
import me.weishu.kernelsu.ui.screen.destinations.ModuleScreenDestination
|
||||
|
||||
enum class BottomBarDestination(
|
||||
val direction: DirectionDestinationSpec,
|
||||
|
||||
@@ -1,150 +0,0 @@
|
||||
package me.weishu.kernelsu.ui.screen
|
||||
|
||||
import android.os.Environment
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
||||
import androidx.compose.material.icons.filled.Save
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.SnackbarHost
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TopAppBar
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.input.key.Key
|
||||
import androidx.compose.ui.input.key.key
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.font.FontFamily
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.compose.dropUnlessResumed
|
||||
import com.ramcosta.composedestinations.annotation.Destination
|
||||
import com.ramcosta.composedestinations.annotation.RootGraph
|
||||
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import me.weishu.kernelsu.R
|
||||
import me.weishu.kernelsu.ui.component.KeyEventBlocker
|
||||
import me.weishu.kernelsu.ui.util.LocalSnackbarHost
|
||||
import me.weishu.kernelsu.ui.util.runModuleAction
|
||||
import java.io.File
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.Date
|
||||
import java.util.Locale
|
||||
|
||||
@Composable
|
||||
@Destination<RootGraph>
|
||||
fun ExecuteModuleActionScreen(navigator: DestinationsNavigator, moduleId: String) {
|
||||
var text by rememberSaveable { mutableStateOf("") }
|
||||
var tempText : String
|
||||
val logContent = rememberSaveable { StringBuilder() }
|
||||
val snackBarHost = LocalSnackbarHost.current
|
||||
val scope = rememberCoroutineScope()
|
||||
val scrollState = rememberScrollState()
|
||||
var actionResult: Boolean
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
if (text.isNotEmpty()) {
|
||||
return@LaunchedEffect
|
||||
}
|
||||
withContext(Dispatchers.IO) {
|
||||
runModuleAction(
|
||||
moduleId = moduleId,
|
||||
onStdout = {
|
||||
tempText = "$it\n"
|
||||
if (tempText.startsWith("[H[J")) { // clear command
|
||||
text = tempText.substring(6)
|
||||
} else {
|
||||
text += tempText
|
||||
}
|
||||
logContent.append(it).append("\n")
|
||||
},
|
||||
onStderr = {
|
||||
logContent.append(it).append("\n")
|
||||
}
|
||||
).let {
|
||||
actionResult = it
|
||||
}
|
||||
}
|
||||
if (actionResult) navigator.popBackStack()
|
||||
}
|
||||
|
||||
Scaffold(
|
||||
topBar = {
|
||||
TopBar(
|
||||
onBack = dropUnlessResumed {
|
||||
navigator.popBackStack()
|
||||
},
|
||||
onSave = {
|
||||
scope.launch {
|
||||
val format = SimpleDateFormat("yyyy-MM-dd-HH-mm-ss", Locale.getDefault())
|
||||
val date = format.format(Date())
|
||||
val file = File(
|
||||
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS),
|
||||
"KernelSU_module_action_log_${date}.log"
|
||||
)
|
||||
file.writeText(logContent.toString())
|
||||
snackBarHost.showSnackbar("Log saved to ${file.absolutePath}")
|
||||
}
|
||||
}
|
||||
)
|
||||
},
|
||||
snackbarHost = { SnackbarHost(snackBarHost) }
|
||||
) { innerPadding ->
|
||||
KeyEventBlocker {
|
||||
it.key == Key.VolumeDown || it.key == Key.VolumeUp
|
||||
}
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxSize(1f)
|
||||
.padding(innerPadding)
|
||||
.verticalScroll(scrollState),
|
||||
) {
|
||||
LaunchedEffect(text) {
|
||||
scrollState.animateScrollTo(scrollState.maxValue)
|
||||
}
|
||||
Text(
|
||||
modifier = Modifier.padding(8.dp),
|
||||
text = text,
|
||||
fontSize = MaterialTheme.typography.bodySmall.fontSize,
|
||||
fontFamily = FontFamily.Monospace,
|
||||
lineHeight = MaterialTheme.typography.bodySmall.lineHeight,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
private fun TopBar(onBack: () -> Unit = {}, onSave: () -> Unit = {}) {
|
||||
TopAppBar(
|
||||
title = { Text(stringResource(R.string.action)) },
|
||||
navigationIcon = {
|
||||
IconButton(
|
||||
onClick = onBack
|
||||
) { Icon(Icons.AutoMirrored.Filled.ArrowBack, contentDescription = null) }
|
||||
},
|
||||
actions = {
|
||||
IconButton(onClick = onSave) {
|
||||
Icon(
|
||||
imageVector = Icons.Filled.Save,
|
||||
contentDescription = stringResource(id = R.string.save_log),
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
@@ -4,12 +4,8 @@ import android.net.Uri
|
||||
import android.os.Environment
|
||||
import android.os.Parcelable
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.WindowInsets
|
||||
import androidx.compose.foundation.layout.WindowInsetsSides
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.only
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.safeDrawing
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.icons.Icons
|
||||
@@ -22,12 +18,8 @@ import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.SnackbarHost
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TopAppBar
|
||||
import androidx.compose.material3.TopAppBarDefaults
|
||||
import androidx.compose.material3.TopAppBarScrollBehavior
|
||||
import androidx.compose.material3.rememberTopAppBarState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
@@ -38,14 +30,11 @@ import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.input.key.Key
|
||||
import androidx.compose.ui.input.key.key
|
||||
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.font.FontFamily
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.compose.dropUnlessResumed
|
||||
import com.ramcosta.composedestinations.annotation.Destination
|
||||
import com.ramcosta.composedestinations.annotation.RootGraph
|
||||
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
|
||||
import com.ramcosta.composedestinations.navigation.EmptyDestinationsNavigator
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
@@ -54,7 +43,6 @@ import kotlinx.coroutines.withContext
|
||||
import kotlinx.parcelize.Parcelize
|
||||
import me.weishu.kernelsu.R
|
||||
import me.weishu.kernelsu.ui.component.KeyEventBlocker
|
||||
import me.weishu.kernelsu.ui.util.FlashResult
|
||||
import me.weishu.kernelsu.ui.util.LkmSelection
|
||||
import me.weishu.kernelsu.ui.util.LocalSnackbarHost
|
||||
import me.weishu.kernelsu.ui.util.flashModule
|
||||
@@ -67,47 +55,27 @@ import java.text.SimpleDateFormat
|
||||
import java.util.Date
|
||||
import java.util.Locale
|
||||
|
||||
/**
|
||||
* @author weishu
|
||||
* @date 2023/1/1.
|
||||
*/
|
||||
|
||||
enum class FlashingStatus {
|
||||
FLASHING,
|
||||
SUCCESS,
|
||||
FAILED
|
||||
}
|
||||
|
||||
// Lets you flash modules sequentially when mutiple zipUris are selected
|
||||
fun flashModulesSequentially(
|
||||
uris: List<Uri>,
|
||||
onStdout: (String) -> Unit,
|
||||
onStderr: (String) -> Unit
|
||||
): FlashResult {
|
||||
for (uri in uris) {
|
||||
flashModule(uri, onStdout, onStderr).apply {
|
||||
if (code != 0) {
|
||||
return FlashResult(code, err, showReboot)
|
||||
}
|
||||
}
|
||||
}
|
||||
return FlashResult(0, "", true)
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
/**
|
||||
* @author weishu
|
||||
* @date 2023/1/1.
|
||||
*/
|
||||
@Composable
|
||||
@Destination<RootGraph>
|
||||
@Destination
|
||||
fun FlashScreen(navigator: DestinationsNavigator, flashIt: FlashIt) {
|
||||
|
||||
var text by rememberSaveable { mutableStateOf("") }
|
||||
var tempText: String
|
||||
val logContent = rememberSaveable { StringBuilder() }
|
||||
var showFloatAction by rememberSaveable { mutableStateOf(false) }
|
||||
|
||||
val snackBarHost = LocalSnackbarHost.current
|
||||
val scope = rememberCoroutineScope()
|
||||
val scrollState = rememberScrollState()
|
||||
val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState())
|
||||
var flashing by rememberSaveable {
|
||||
mutableStateOf(FlashingStatus.FLASHING)
|
||||
}
|
||||
@@ -117,26 +85,21 @@ fun FlashScreen(navigator: DestinationsNavigator, flashIt: FlashIt) {
|
||||
return@LaunchedEffect
|
||||
}
|
||||
withContext(Dispatchers.IO) {
|
||||
flashIt(flashIt, onStdout = {
|
||||
tempText = "$it\n"
|
||||
if (tempText.startsWith("[H[J")) { // clear command
|
||||
text = tempText.substring(6)
|
||||
} else {
|
||||
text += tempText
|
||||
}
|
||||
logContent.append(it).append("\n")
|
||||
}, onStderr = {
|
||||
logContent.append(it).append("\n")
|
||||
}).apply {
|
||||
flashIt(flashIt, onFinish = { showReboot, code ->
|
||||
if (code != 0) {
|
||||
text += "Error code: $code.\n $err Please save and check the log.\n"
|
||||
text += "Error: exit code = $code.\nPlease save and check the log.\n"
|
||||
}
|
||||
if (showReboot) {
|
||||
text += "\n\n\n"
|
||||
showFloatAction = true
|
||||
}
|
||||
flashing = if (code == 0) FlashingStatus.SUCCESS else FlashingStatus.FAILED
|
||||
}
|
||||
}, onStdout = {
|
||||
text += "$it\n"
|
||||
logContent.append(it).append("\n")
|
||||
}, onStderr = {
|
||||
logContent.append(it).append("\n")
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -144,7 +107,7 @@ fun FlashScreen(navigator: DestinationsNavigator, flashIt: FlashIt) {
|
||||
topBar = {
|
||||
TopBar(
|
||||
flashing,
|
||||
onBack = dropUnlessResumed {
|
||||
onBack = {
|
||||
navigator.popBackStack()
|
||||
},
|
||||
onSave = {
|
||||
@@ -158,8 +121,7 @@ fun FlashScreen(navigator: DestinationsNavigator, flashIt: FlashIt) {
|
||||
file.writeText(logContent.toString())
|
||||
snackBarHost.showSnackbar("Log saved to ${file.absolutePath}")
|
||||
}
|
||||
},
|
||||
scrollBehavior = scrollBehavior
|
||||
}
|
||||
)
|
||||
},
|
||||
floatingActionButton = {
|
||||
@@ -177,9 +139,8 @@ fun FlashScreen(navigator: DestinationsNavigator, flashIt: FlashIt) {
|
||||
text = { Text(text = reboot) },
|
||||
)
|
||||
}
|
||||
},
|
||||
snackbarHost = { SnackbarHost(hostState = snackBarHost) },
|
||||
contentWindowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal)
|
||||
|
||||
}
|
||||
) { innerPadding ->
|
||||
KeyEventBlocker {
|
||||
it.key == Key.VolumeDown || it.key == Key.VolumeUp
|
||||
@@ -188,7 +149,6 @@ fun FlashScreen(navigator: DestinationsNavigator, flashIt: FlashIt) {
|
||||
modifier = Modifier
|
||||
.fillMaxSize(1f)
|
||||
.padding(innerPadding)
|
||||
.nestedScroll(scrollBehavior.nestedScrollConnection)
|
||||
.verticalScroll(scrollState),
|
||||
) {
|
||||
LaunchedEffect(text) {
|
||||
@@ -210,7 +170,7 @@ sealed class FlashIt : Parcelable {
|
||||
data class FlashBoot(val boot: Uri? = null, val lkm: LkmSelection, val ota: Boolean) :
|
||||
FlashIt()
|
||||
|
||||
data class FlashModules(val uris: List<Uri>) : FlashIt()
|
||||
data class FlashModule(val uri: Uri) : FlashIt()
|
||||
|
||||
data object FlashRestore : FlashIt()
|
||||
|
||||
@@ -218,37 +178,31 @@ sealed class FlashIt : Parcelable {
|
||||
}
|
||||
|
||||
fun flashIt(
|
||||
flashIt: FlashIt,
|
||||
flashIt: FlashIt, onFinish: (Boolean, Int) -> Unit,
|
||||
onStdout: (String) -> Unit,
|
||||
onStderr: (String) -> Unit
|
||||
): FlashResult {
|
||||
return when (flashIt) {
|
||||
) {
|
||||
when (flashIt) {
|
||||
is FlashIt.FlashBoot -> installBoot(
|
||||
flashIt.boot,
|
||||
flashIt.lkm,
|
||||
flashIt.ota,
|
||||
onFinish,
|
||||
onStdout,
|
||||
onStderr
|
||||
)
|
||||
|
||||
is FlashIt.FlashModules -> {
|
||||
flashModulesSequentially(flashIt.uris, onStdout, onStderr)
|
||||
}
|
||||
is FlashIt.FlashModule -> flashModule(flashIt.uri, onFinish, onStdout, onStderr)
|
||||
|
||||
FlashIt.FlashRestore -> restoreBoot(onStdout, onStderr)
|
||||
FlashIt.FlashRestore -> restoreBoot(onFinish, onStdout, onStderr)
|
||||
|
||||
FlashIt.FlashUninstall -> uninstallPermanently(onStdout, onStderr)
|
||||
FlashIt.FlashUninstall -> uninstallPermanently(onFinish, onStdout, onStderr)
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
private fun TopBar(
|
||||
status: FlashingStatus,
|
||||
onBack: () -> Unit = {},
|
||||
onSave: () -> Unit = {},
|
||||
scrollBehavior: TopAppBarScrollBehavior? = null
|
||||
) {
|
||||
private fun TopBar(status: FlashingStatus, onBack: () -> Unit = {}, onSave: () -> Unit = {}) {
|
||||
TopAppBar(
|
||||
title = {
|
||||
Text(
|
||||
@@ -273,9 +227,7 @@ private fun TopBar(
|
||||
contentDescription = "Localized description"
|
||||
)
|
||||
}
|
||||
},
|
||||
windowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal),
|
||||
scrollBehavior = scrollBehavior
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -22,58 +22,48 @@ import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalUriHandler
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.core.content.pm.PackageInfoCompat
|
||||
import com.ramcosta.composedestinations.annotation.Destination
|
||||
import com.ramcosta.composedestinations.annotation.RootGraph
|
||||
import com.ramcosta.composedestinations.generated.destinations.InstallScreenDestination
|
||||
import com.ramcosta.composedestinations.generated.destinations.SettingScreenDestination
|
||||
import com.ramcosta.composedestinations.annotation.RootNavGraph
|
||||
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import me.weishu.kernelsu.*
|
||||
import me.weishu.kernelsu.R
|
||||
import me.weishu.kernelsu.ui.component.rememberConfirmDialog
|
||||
import me.weishu.kernelsu.ui.component.KsuIsValid
|
||||
import me.weishu.kernelsu.ui.screen.destinations.InstallScreenDestination
|
||||
import me.weishu.kernelsu.ui.screen.destinations.SettingScreenDestination
|
||||
import me.weishu.kernelsu.ui.util.*
|
||||
import me.weishu.kernelsu.ui.util.module.LatestVersionInfo
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Destination<RootGraph>(start = true)
|
||||
@RootNavGraph(start = true)
|
||||
@Destination
|
||||
@Composable
|
||||
fun HomeScreen(navigator: DestinationsNavigator) {
|
||||
val kernelVersion = getKernelVersion()
|
||||
val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState())
|
||||
|
||||
Scaffold(
|
||||
topBar = {
|
||||
TopBar(
|
||||
kernelVersion,
|
||||
onSettingsClick = {
|
||||
navigator.navigate(SettingScreenDestination)
|
||||
},
|
||||
onInstallClick = {
|
||||
navigator.navigate(InstallScreenDestination)
|
||||
},
|
||||
scrollBehavior = scrollBehavior
|
||||
)
|
||||
},
|
||||
contentWindowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal)
|
||||
) { innerPadding ->
|
||||
Scaffold(topBar = {
|
||||
TopBar(kernelVersion, onSettingsClick = {
|
||||
navigator.navigate(SettingScreenDestination)
|
||||
}, onInstallClick = {
|
||||
navigator.navigate(InstallScreenDestination)
|
||||
})
|
||||
}) { innerPadding ->
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.padding(innerPadding)
|
||||
.nestedScroll(scrollBehavior.nestedScrollConnection)
|
||||
.verticalScroll(rememberScrollState())
|
||||
.padding(horizontal = 16.dp),
|
||||
.padding(horizontal = 16.dp)
|
||||
.verticalScroll(rememberScrollState()),
|
||||
verticalArrangement = Arrangement.spacedBy(16.dp)
|
||||
) {
|
||||
val isManager = Natives.becomeManager(ksuApp.packageName)
|
||||
SideEffect {
|
||||
if (isManager) install()
|
||||
}
|
||||
val ksuVersion = if (isManager) Natives.version else null
|
||||
val lkmMode = ksuVersion?.let {
|
||||
if (it >= Natives.MINIMAL_SUPPORTED_KERNEL_LKM && kernelVersion.isGKI()) Natives.isLkmMode else null
|
||||
@@ -113,11 +103,12 @@ fun UpdateCard() {
|
||||
val context = LocalContext.current
|
||||
val latestVersionInfo = LatestVersionInfo()
|
||||
val newVersion by produceState(initialValue = latestVersionInfo) {
|
||||
value = withContext(Dispatchers.IO) {
|
||||
value = withContext(Dispatchers.IO){
|
||||
checkNewVersion()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
val currentVersionCode = getManagerVersion(context).second
|
||||
val newVersionCode = newVersion.versionCode
|
||||
val newVersionUrl = newVersion.downloadUrl
|
||||
@@ -165,60 +156,52 @@ fun RebootDropdownItem(@StringRes id: Int, reason: String = "") {
|
||||
private fun TopBar(
|
||||
kernelVersion: KernelVersion,
|
||||
onInstallClick: () -> Unit,
|
||||
onSettingsClick: () -> Unit,
|
||||
scrollBehavior: TopAppBarScrollBehavior? = null
|
||||
onSettingsClick: () -> Unit
|
||||
) {
|
||||
TopAppBar(
|
||||
title = { Text(stringResource(R.string.app_name)) },
|
||||
actions = {
|
||||
if (kernelVersion.isGKI()) {
|
||||
IconButton(onClick = onInstallClick) {
|
||||
Icon(
|
||||
imageVector = Icons.Filled.Archive,
|
||||
contentDescription = stringResource(id = R.string.install)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
var showDropdown by remember { mutableStateOf(false) }
|
||||
KsuIsValid() {
|
||||
IconButton(onClick = {
|
||||
showDropdown = true
|
||||
}) {
|
||||
Icon(
|
||||
imageVector = Icons.Filled.Refresh,
|
||||
contentDescription = stringResource(id = R.string.reboot)
|
||||
)
|
||||
|
||||
DropdownMenu(expanded = showDropdown, onDismissRequest = {
|
||||
showDropdown = false
|
||||
}) {
|
||||
|
||||
RebootDropdownItem(id = R.string.reboot)
|
||||
|
||||
val pm = LocalContext.current.getSystemService(Context.POWER_SERVICE) as PowerManager?
|
||||
@Suppress("DEPRECATION")
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R && pm?.isRebootingUserspaceSupported == true) {
|
||||
RebootDropdownItem(id = R.string.reboot_userspace, reason = "userspace")
|
||||
}
|
||||
RebootDropdownItem(id = R.string.reboot_recovery, reason = "recovery")
|
||||
RebootDropdownItem(id = R.string.reboot_bootloader, reason = "bootloader")
|
||||
RebootDropdownItem(id = R.string.reboot_download, reason = "download")
|
||||
RebootDropdownItem(id = R.string.reboot_edl, reason = "edl")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
IconButton(onClick = onSettingsClick) {
|
||||
TopAppBar(title = { Text(stringResource(R.string.app_name)) }, actions = {
|
||||
if (kernelVersion.isGKI()) {
|
||||
IconButton(onClick = onInstallClick) {
|
||||
Icon(
|
||||
imageVector = Icons.Filled.Settings,
|
||||
contentDescription = stringResource(id = R.string.settings)
|
||||
imageVector = Icons.Filled.Archive,
|
||||
contentDescription = stringResource(id = R.string.install)
|
||||
)
|
||||
}
|
||||
},
|
||||
windowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal),
|
||||
scrollBehavior = scrollBehavior
|
||||
)
|
||||
}
|
||||
|
||||
var showDropdown by remember { mutableStateOf(false) }
|
||||
IconButton(onClick = {
|
||||
showDropdown = true
|
||||
}) {
|
||||
Icon(
|
||||
imageVector = Icons.Filled.Refresh,
|
||||
contentDescription = stringResource(id = R.string.reboot)
|
||||
)
|
||||
|
||||
DropdownMenu(expanded = showDropdown, onDismissRequest = {
|
||||
showDropdown = false
|
||||
}) {
|
||||
|
||||
RebootDropdownItem(id = R.string.reboot)
|
||||
|
||||
val pm =
|
||||
LocalContext.current.getSystemService(Context.POWER_SERVICE) as PowerManager?
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R && pm?.isRebootingUserspaceSupported == true) {
|
||||
RebootDropdownItem(id = R.string.reboot_userspace, reason = "userspace")
|
||||
}
|
||||
RebootDropdownItem(id = R.string.reboot_recovery, reason = "recovery")
|
||||
RebootDropdownItem(id = R.string.reboot_bootloader, reason = "bootloader")
|
||||
RebootDropdownItem(id = R.string.reboot_download, reason = "download")
|
||||
RebootDropdownItem(id = R.string.reboot_edl, reason = "edl")
|
||||
}
|
||||
}
|
||||
|
||||
IconButton(onClick = onSettingsClick) {
|
||||
Icon(
|
||||
imageVector = Icons.Filled.Settings,
|
||||
contentDescription = stringResource(id = R.string.settings)
|
||||
)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@Composable
|
||||
@@ -432,10 +415,9 @@ private fun InfoCard() {
|
||||
}
|
||||
}
|
||||
|
||||
fun getManagerVersion(context: Context): Pair<String, Long> {
|
||||
fun getManagerVersion(context: Context): Pair<String, Int> {
|
||||
val packageInfo = context.packageManager.getPackageInfo(context.packageName, 0)!!
|
||||
val versionCode = PackageInfoCompat.getLongVersionCode(packageInfo)
|
||||
return Pair(packageInfo.versionName!!, versionCode)
|
||||
return Pair(packageInfo.versionName!!, packageInfo.versionCode)
|
||||
}
|
||||
|
||||
@Preview
|
||||
|
||||
@@ -6,19 +6,11 @@ import android.net.Uri
|
||||
import androidx.activity.compose.rememberLauncherForActivityResult
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.compose.foundation.LocalIndication
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.WindowInsets
|
||||
import androidx.compose.foundation.layout.WindowInsetsSides
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.only
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.safeDrawing
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.selection.toggleable
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
||||
import androidx.compose.material.icons.filled.FileUpload
|
||||
@@ -31,9 +23,6 @@ import androidx.compose.material3.RadioButton
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TopAppBar
|
||||
import androidx.compose.material3.TopAppBarDefaults
|
||||
import androidx.compose.material3.TopAppBarScrollBehavior
|
||||
import androidx.compose.material3.rememberTopAppBarState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
@@ -42,26 +31,22 @@ import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.semantics.Role
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.compose.dropUnlessResumed
|
||||
import com.maxkeppeker.sheets.core.models.base.Header
|
||||
import com.maxkeppeker.sheets.core.models.base.rememberUseCaseState
|
||||
import com.maxkeppeler.sheets.list.ListDialog
|
||||
import com.maxkeppeler.sheets.list.models.ListOption
|
||||
import com.maxkeppeler.sheets.list.models.ListSelection
|
||||
import com.ramcosta.composedestinations.annotation.Destination
|
||||
import com.ramcosta.composedestinations.annotation.RootGraph
|
||||
import com.ramcosta.composedestinations.generated.destinations.FlashScreenDestination
|
||||
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
|
||||
import com.ramcosta.composedestinations.navigation.EmptyDestinationsNavigator
|
||||
import me.weishu.kernelsu.R
|
||||
import me.weishu.kernelsu.ui.component.DialogHandle
|
||||
import me.weishu.kernelsu.ui.component.rememberConfirmDialog
|
||||
import me.weishu.kernelsu.ui.component.rememberCustomDialog
|
||||
import me.weishu.kernelsu.ui.screen.destinations.FlashScreenDestination
|
||||
import me.weishu.kernelsu.ui.util.LkmSelection
|
||||
import me.weishu.kernelsu.ui.util.getCurrentKmi
|
||||
import me.weishu.kernelsu.ui.util.getSupportedKmis
|
||||
@@ -73,8 +58,7 @@ import me.weishu.kernelsu.ui.util.rootAvailable
|
||||
* @author weishu
|
||||
* @date 2024/3/12.
|
||||
*/
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Destination<RootGraph>
|
||||
@Destination
|
||||
@Composable
|
||||
fun InstallScreen(navigator: DestinationsNavigator) {
|
||||
var installMethod by remember {
|
||||
@@ -129,24 +113,12 @@ fun InstallScreen(navigator: DestinationsNavigator) {
|
||||
})
|
||||
}
|
||||
|
||||
val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState())
|
||||
|
||||
Scaffold(
|
||||
topBar = {
|
||||
TopBar(
|
||||
onBack = dropUnlessResumed { navigator.popBackStack() },
|
||||
onLkmUpload = onLkmUpload,
|
||||
scrollBehavior = scrollBehavior
|
||||
)
|
||||
},
|
||||
contentWindowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal)
|
||||
) { innerPadding ->
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.padding(innerPadding)
|
||||
.nestedScroll(scrollBehavior.nestedScrollConnection)
|
||||
.verticalScroll(rememberScrollState())
|
||||
) {
|
||||
Scaffold(topBar = {
|
||||
TopBar(
|
||||
onBack = { navigator.popBackStack() }, onLkmUpload = onLkmUpload
|
||||
)
|
||||
}) {
|
||||
Column(modifier = Modifier.padding(it)) {
|
||||
SelectInstallMethod { method ->
|
||||
installMethod = method
|
||||
}
|
||||
@@ -259,31 +231,16 @@ private fun SelectInstallMethod(onSelected: (InstallMethod) -> Unit = {}) {
|
||||
|
||||
Column {
|
||||
radioOptions.forEach { option ->
|
||||
val interactionSource = remember { MutableInteractionSource() }
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
Row(verticalAlignment = Alignment.CenterVertically,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.toggleable(
|
||||
value = option.javaClass == selectedOption?.javaClass,
|
||||
onValueChange = {
|
||||
onClick(option)
|
||||
},
|
||||
role = Role.RadioButton,
|
||||
indication = LocalIndication.current,
|
||||
interactionSource = interactionSource
|
||||
)
|
||||
) {
|
||||
RadioButton(
|
||||
selected = option.javaClass == selectedOption?.javaClass,
|
||||
onClick = {
|
||||
.clickable {
|
||||
onClick(option)
|
||||
},
|
||||
interactionSource = interactionSource
|
||||
)
|
||||
Column(
|
||||
modifier = Modifier.padding(vertical = 12.dp)
|
||||
) {
|
||||
}) {
|
||||
RadioButton(selected = option.javaClass == selectedOption?.javaClass, onClick = {
|
||||
onClick(option)
|
||||
})
|
||||
Column {
|
||||
Text(
|
||||
text = stringResource(id = option.label),
|
||||
fontSize = MaterialTheme.typography.titleMedium.fontSize,
|
||||
@@ -335,28 +292,20 @@ fun rememberSelectKmiDialog(onSelected: (String?) -> Unit): DialogHandle {
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
private fun TopBar(
|
||||
onBack: () -> Unit = {},
|
||||
onLkmUpload: () -> Unit = {},
|
||||
scrollBehavior: TopAppBarScrollBehavior? = null
|
||||
) {
|
||||
TopAppBar(
|
||||
title = { Text(stringResource(R.string.install)) }, navigationIcon = {
|
||||
IconButton(
|
||||
onClick = onBack
|
||||
) { Icon(Icons.AutoMirrored.Filled.ArrowBack, contentDescription = null) }
|
||||
}, actions = {
|
||||
IconButton(onClick = onLkmUpload) {
|
||||
Icon(Icons.Filled.FileUpload, contentDescription = null)
|
||||
}
|
||||
},
|
||||
windowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal),
|
||||
scrollBehavior = scrollBehavior
|
||||
)
|
||||
private fun TopBar(onBack: () -> Unit = {}, onLkmUpload: () -> Unit = {}) {
|
||||
TopAppBar(title = { Text(stringResource(R.string.install)) }, navigationIcon = {
|
||||
IconButton(
|
||||
onClick = onBack
|
||||
) { Icon(Icons.AutoMirrored.Filled.ArrowBack, contentDescription = null) }
|
||||
}, actions = {
|
||||
IconButton(onClick = onLkmUpload) {
|
||||
Icon(Icons.Filled.FileUpload, contentDescription = null)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@Composable
|
||||
@Preview
|
||||
fun SelectInstallPreview() {
|
||||
fun SelectInstall_Preview() {
|
||||
InstallScreen(EmptyDestinationsNavigator)
|
||||
}
|
||||
@@ -1,66 +1,47 @@
|
||||
package me.weishu.kernelsu.ui.screen
|
||||
|
||||
import android.app.Activity.RESULT_OK
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.util.Log
|
||||
import android.widget.Toast
|
||||
import androidx.activity.compose.rememberLauncherForActivityResult
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.compose.foundation.LocalIndication
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.WindowInsets
|
||||
import androidx.compose.foundation.layout.WindowInsetsSides
|
||||
import androidx.compose.foundation.layout.defaultMinSize
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.only
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.safeDrawing
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.selection.toggleable
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.ExperimentalMaterialApi
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.outlined.Wysiwyg
|
||||
import androidx.compose.material.icons.filled.Add
|
||||
import androidx.compose.material.icons.filled.MoreVert
|
||||
import androidx.compose.material.icons.outlined.PlayArrow
|
||||
import androidx.compose.material.icons.outlined.Download
|
||||
import androidx.compose.material.icons.outlined.Delete
|
||||
import androidx.compose.material3.AlertDialog
|
||||
import androidx.compose.material.pullrefresh.PullRefreshIndicator
|
||||
import androidx.compose.material.pullrefresh.pullRefresh
|
||||
import androidx.compose.material.pullrefresh.rememberPullRefreshState
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.ButtonDefaults
|
||||
import androidx.compose.material3.Checkbox
|
||||
import androidx.compose.material3.DropdownMenu
|
||||
import androidx.compose.material3.DropdownMenuItem
|
||||
import androidx.compose.material3.CardDefaults
|
||||
import androidx.compose.material3.ElevatedCard
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.ExtendedFloatingActionButton
|
||||
import androidx.compose.material3.FilledTonalButton
|
||||
import androidx.compose.material3.HorizontalDivider
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.SnackbarDuration
|
||||
import androidx.compose.material3.SnackbarHost
|
||||
import androidx.compose.material3.SnackbarHostState
|
||||
import androidx.compose.material3.SnackbarResult
|
||||
import androidx.compose.material3.Switch
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.material3.TopAppBarDefaults
|
||||
import androidx.compose.material3.pulltorefresh.PullToRefreshBox
|
||||
import androidx.compose.material3.rememberTopAppBarState
|
||||
import androidx.compose.material3.TopAppBar
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
@@ -68,13 +49,12 @@ import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.produceState
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.semantics.Role
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.text.style.TextDecoration
|
||||
@@ -84,21 +64,16 @@ import androidx.compose.ui.unit.Dp
|
||||
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.generated.destinations.ExecuteModuleActionScreenDestination
|
||||
import com.ramcosta.composedestinations.generated.destinations.FlashScreenDestination
|
||||
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
|
||||
import com.ramcosta.composedestinations.navigation.EmptyDestinationsNavigator
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import me.weishu.kernelsu.Natives
|
||||
import me.weishu.kernelsu.R
|
||||
import me.weishu.kernelsu.ksuApp
|
||||
import me.weishu.kernelsu.ui.component.ConfirmResult
|
||||
import me.weishu.kernelsu.ui.component.SearchAppBar
|
||||
import me.weishu.kernelsu.ui.component.rememberConfirmDialog
|
||||
import me.weishu.kernelsu.ui.component.rememberLoadingDialog
|
||||
import me.weishu.kernelsu.ui.screen.destinations.FlashScreenDestination
|
||||
import me.weishu.kernelsu.ui.util.DownloadListener
|
||||
import me.weishu.kernelsu.ui.util.LocalSnackbarHost
|
||||
import me.weishu.kernelsu.ui.util.download
|
||||
@@ -106,24 +81,18 @@ import me.weishu.kernelsu.ui.util.hasMagisk
|
||||
import me.weishu.kernelsu.ui.util.reboot
|
||||
import me.weishu.kernelsu.ui.util.toggleModule
|
||||
import me.weishu.kernelsu.ui.util.uninstallModule
|
||||
import me.weishu.kernelsu.ui.util.getFileName
|
||||
import me.weishu.kernelsu.ui.viewmodel.ModuleViewModel
|
||||
import me.weishu.kernelsu.ui.webui.WebUIActivity
|
||||
import okhttp3.OkHttpClient
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Destination<RootGraph>
|
||||
@Destination
|
||||
@Composable
|
||||
fun ModuleScreen(navigator: DestinationsNavigator) {
|
||||
val viewModel = viewModel<ModuleViewModel>()
|
||||
val context = LocalContext.current
|
||||
val snackBarHost = LocalSnackbarHost.current
|
||||
val scope = rememberCoroutineScope()
|
||||
val prefs = context.getSharedPreferences("settings", Context.MODE_PRIVATE)
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
if (viewModel.moduleList.isEmpty() || viewModel.isNeedRefresh) {
|
||||
viewModel.sortEnabledFirst = prefs.getBoolean("module_sort_enabled_first", false)
|
||||
viewModel.sortActionFirst = prefs.getBoolean("module_sort_action_first", false)
|
||||
viewModel.fetchModuleList()
|
||||
}
|
||||
}
|
||||
@@ -133,132 +102,41 @@ fun ModuleScreen(navigator: DestinationsNavigator) {
|
||||
|
||||
val hideInstallButton = isSafeMode || hasMagisk
|
||||
|
||||
val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState())
|
||||
|
||||
val webUILauncher = rememberLauncherForActivityResult(
|
||||
contract = ActivityResultContracts.StartActivityForResult()
|
||||
) { viewModel.fetchModuleList() }
|
||||
|
||||
Scaffold(
|
||||
topBar = {
|
||||
SearchAppBar(
|
||||
title = { Text(stringResource(R.string.module)) },
|
||||
searchText = viewModel.search,
|
||||
onSearchTextChange = { viewModel.search = it },
|
||||
onClearClick = { viewModel.search = "" },
|
||||
dropdownContent = {
|
||||
var showDropdown by remember { mutableStateOf(false) }
|
||||
|
||||
IconButton(
|
||||
onClick = { showDropdown = true },
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Filled.MoreVert,
|
||||
contentDescription = stringResource(id = R.string.settings)
|
||||
)
|
||||
|
||||
DropdownMenu(expanded = showDropdown, onDismissRequest = {
|
||||
showDropdown = false
|
||||
}) {
|
||||
DropdownMenuItem(text = {
|
||||
Text(stringResource(R.string.module_sort_action_first))
|
||||
}, trailingIcon = {
|
||||
Checkbox(viewModel.sortActionFirst, null)
|
||||
}, onClick = {
|
||||
viewModel.sortActionFirst =
|
||||
!viewModel.sortActionFirst
|
||||
prefs.edit()
|
||||
.putBoolean(
|
||||
"module_sort_action_first",
|
||||
viewModel.sortActionFirst
|
||||
)
|
||||
.apply()
|
||||
scope.launch {
|
||||
viewModel.fetchModuleList()
|
||||
}
|
||||
})
|
||||
DropdownMenuItem(text = {
|
||||
Text(stringResource(R.string.module_sort_enabled_first))
|
||||
}, trailingIcon = {
|
||||
Checkbox(viewModel.sortEnabledFirst, null)
|
||||
}, onClick = {
|
||||
viewModel.sortEnabledFirst =
|
||||
!viewModel.sortEnabledFirst
|
||||
prefs.edit()
|
||||
.putBoolean(
|
||||
"module_sort_enabled_first",
|
||||
viewModel.sortEnabledFirst
|
||||
)
|
||||
.apply()
|
||||
scope.launch {
|
||||
viewModel.fetchModuleList()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
},
|
||||
scrollBehavior = scrollBehavior,
|
||||
)
|
||||
},
|
||||
floatingActionButton = {
|
||||
if (!hideInstallButton) {
|
||||
val moduleInstall = stringResource(id = R.string.module_install)
|
||||
val confirmTitle = stringResource(R.string.module)
|
||||
var zipUris by remember { mutableStateOf<List<Uri>>(emptyList()) }
|
||||
val confirmDialog = rememberConfirmDialog(onConfirm = {
|
||||
navigator.navigate(FlashScreenDestination(FlashIt.FlashModules(zipUris)))
|
||||
viewModel.markNeedRefresh()
|
||||
})
|
||||
val selectZipLauncher = rememberLauncherForActivityResult(
|
||||
contract = ActivityResultContracts.StartActivityForResult()
|
||||
) {
|
||||
if (it.resultCode != RESULT_OK) {
|
||||
return@rememberLauncherForActivityResult
|
||||
}
|
||||
val data = it.data ?: return@rememberLauncherForActivityResult
|
||||
val clipData = data.clipData
|
||||
|
||||
val uris = mutableListOf<Uri>()
|
||||
if (clipData != null) {
|
||||
for (i in 0 until clipData.itemCount) {
|
||||
clipData.getItemAt(i)?.uri?.let { uris.add(it) }
|
||||
}
|
||||
} else {
|
||||
data.data?.let { uris.add(it) }
|
||||
}
|
||||
|
||||
if (uris.size == 1) {
|
||||
navigator.navigate(FlashScreenDestination(FlashIt.FlashModules(listOf(uris.first()))))
|
||||
} else if (uris.size > 1) {
|
||||
// multiple files selected
|
||||
val moduleNames = uris.mapIndexed { index, uri -> "\n${index + 1}. ${uri.getFileName(context)}" }.joinToString("")
|
||||
val confirmContent = context.getString(R.string.module_install_prompt_with_name, moduleNames)
|
||||
zipUris = uris
|
||||
confirmDialog.showConfirm(
|
||||
title = confirmTitle,
|
||||
content = confirmContent,
|
||||
markdown = true
|
||||
)
|
||||
}
|
||||
Scaffold(topBar = {
|
||||
TopBar()
|
||||
}, floatingActionButton = if (hideInstallButton) {
|
||||
{ /* Empty */ }
|
||||
} else {
|
||||
{
|
||||
val moduleInstall = stringResource(id = R.string.module_install)
|
||||
val selectZipLauncher = rememberLauncherForActivityResult(
|
||||
contract = ActivityResultContracts.StartActivityForResult()
|
||||
) {
|
||||
if (it.resultCode != RESULT_OK) {
|
||||
return@rememberLauncherForActivityResult
|
||||
}
|
||||
val data = it.data ?: return@rememberLauncherForActivityResult
|
||||
val uri = data.data ?: return@rememberLauncherForActivityResult
|
||||
|
||||
ExtendedFloatingActionButton(
|
||||
onClick = {
|
||||
// Select the zip files to install
|
||||
val intent = Intent(Intent.ACTION_GET_CONTENT).apply {
|
||||
type = "application/zip"
|
||||
putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true)
|
||||
}
|
||||
selectZipLauncher.launch(intent)
|
||||
},
|
||||
icon = { Icon(Icons.Filled.Add, moduleInstall) },
|
||||
text = { Text(text = moduleInstall) },
|
||||
)
|
||||
navigator.navigate(FlashScreenDestination(FlashIt.FlashModule(uri)))
|
||||
|
||||
viewModel.markNeedRefresh()
|
||||
|
||||
Log.i("ModuleScreen", "select zip result: ${it.data}")
|
||||
}
|
||||
},
|
||||
contentWindowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal),
|
||||
snackbarHost = { SnackbarHost(hostState = snackBarHost) }
|
||||
) { innerPadding ->
|
||||
|
||||
ExtendedFloatingActionButton(
|
||||
onClick = {
|
||||
// select the zip file to install
|
||||
val intent = Intent(Intent.ACTION_GET_CONTENT)
|
||||
intent.type = "application/zip"
|
||||
selectZipLauncher.launch(intent)
|
||||
},
|
||||
icon = { Icon(Icons.Filled.Add, moduleInstall) },
|
||||
text = { Text(text = moduleInstall) },
|
||||
)
|
||||
}
|
||||
}) { innerPadding ->
|
||||
|
||||
when {
|
||||
hasMagisk -> {
|
||||
@@ -277,59 +155,53 @@ fun ModuleScreen(navigator: DestinationsNavigator) {
|
||||
|
||||
else -> {
|
||||
ModuleList(
|
||||
navigator,
|
||||
viewModel = viewModel,
|
||||
modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
|
||||
boxModifier = Modifier.padding(innerPadding),
|
||||
onInstallModule = {
|
||||
navigator.navigate(FlashScreenDestination(FlashIt.FlashModules(listOf(it))))
|
||||
},
|
||||
onClickModule = { id, name, hasWebUi ->
|
||||
viewModel = viewModel, modifier = Modifier
|
||||
.padding(innerPadding)
|
||||
.fillMaxSize(),
|
||||
onInstallModule =
|
||||
{
|
||||
navigator.navigate(FlashScreenDestination(FlashIt.FlashModule(it)))
|
||||
}, onClickModule = { id, name, hasWebUi ->
|
||||
if (hasWebUi) {
|
||||
webUILauncher.launch(
|
||||
Intent(context, WebUIActivity::class.java)
|
||||
.setData(Uri.parse("kernelsu://webui/$id"))
|
||||
.putExtra("id", id)
|
||||
.putExtra("name", name)
|
||||
context.startActivity(Intent(context, WebUIActivity::class.java)
|
||||
.setData(Uri.parse("kernelsu://webui/$id"))
|
||||
.putExtra("id", id)
|
||||
.putExtra("name", name)
|
||||
)
|
||||
}
|
||||
},
|
||||
context = context,
|
||||
snackBarHost = snackBarHost
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@OptIn(ExperimentalMaterialApi::class)
|
||||
@Composable
|
||||
private fun ModuleList(
|
||||
navigator: DestinationsNavigator,
|
||||
viewModel: ModuleViewModel,
|
||||
modifier: Modifier = Modifier,
|
||||
boxModifier: Modifier = Modifier,
|
||||
onInstallModule: (Uri) -> Unit,
|
||||
onClickModule: (id: String, name: String, hasWebUi: Boolean) -> Unit,
|
||||
context: Context,
|
||||
snackBarHost: SnackbarHostState
|
||||
onClickModule: (id: String, name: String, hasWebUi: Boolean) -> Unit
|
||||
) {
|
||||
val failedEnable = stringResource(R.string.module_failed_to_enable)
|
||||
val failedDisable = stringResource(R.string.module_failed_to_disable)
|
||||
val failedUninstall = stringResource(R.string.module_uninstall_failed)
|
||||
val successUninstall = stringResource(R.string.module_uninstall_success)
|
||||
val reboot = stringResource(R.string.reboot)
|
||||
val rebootToApply = stringResource(R.string.reboot_to_apply)
|
||||
val moduleStr = stringResource(R.string.module)
|
||||
val uninstall = stringResource(R.string.uninstall)
|
||||
val cancel = stringResource(android.R.string.cancel)
|
||||
val moduleUninstallConfirm = stringResource(R.string.module_uninstall_confirm)
|
||||
val reboot = stringResource(id = R.string.reboot)
|
||||
val rebootToApply = stringResource(id = R.string.reboot_to_apply)
|
||||
val moduleStr = stringResource(id = R.string.module)
|
||||
val uninstall = stringResource(id = R.string.uninstall)
|
||||
val cancel = stringResource(id = android.R.string.cancel)
|
||||
val moduleUninstallConfirm = stringResource(id = R.string.module_uninstall_confirm)
|
||||
val updateText = stringResource(R.string.module_update)
|
||||
val changelogText = stringResource(R.string.module_changelog)
|
||||
val downloadingText = stringResource(R.string.module_downloading)
|
||||
val startDownloadingText = stringResource(R.string.module_start_downloading)
|
||||
val fetchChangeLogFailed = stringResource(R.string.module_changelog_failed)
|
||||
|
||||
val snackBarHost = LocalSnackbarHost.current
|
||||
val context = LocalContext.current
|
||||
|
||||
val loadingDialog = rememberLoadingDialog()
|
||||
val confirmDialog = rememberConfirmDialog()
|
||||
|
||||
@@ -342,7 +214,7 @@ private fun ModuleList(
|
||||
val changelogResult = loadingDialog.withLoading {
|
||||
withContext(Dispatchers.IO) {
|
||||
runCatching {
|
||||
ksuApp.okhttpClient.newCall(
|
||||
OkHttpClient().newCall(
|
||||
okhttp3.Request.Builder().url(changelogUrl).build()
|
||||
).execute().body!!.string()
|
||||
}
|
||||
@@ -428,31 +300,26 @@ private fun ModuleList(
|
||||
} else {
|
||||
null
|
||||
}
|
||||
val result = snackBarHost.showSnackbar(
|
||||
message = message,
|
||||
actionLabel = actionLabel,
|
||||
duration = SnackbarDuration.Long
|
||||
)
|
||||
val result = snackBarHost.showSnackbar(message, actionLabel = actionLabel)
|
||||
if (result == SnackbarResult.ActionPerformed) {
|
||||
reboot()
|
||||
}
|
||||
}
|
||||
PullToRefreshBox(
|
||||
modifier = boxModifier,
|
||||
onRefresh = {
|
||||
viewModel.fetchModuleList()
|
||||
},
|
||||
isRefreshing = viewModel.isRefreshing
|
||||
) {
|
||||
|
||||
val refreshState = rememberPullRefreshState(refreshing = viewModel.isRefreshing,
|
||||
onRefresh = { viewModel.fetchModuleList() })
|
||||
Box(modifier.pullRefresh(refreshState)) {
|
||||
val context = LocalContext.current
|
||||
|
||||
LazyColumn(
|
||||
modifier = modifier,
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
verticalArrangement = Arrangement.spacedBy(16.dp),
|
||||
contentPadding = remember {
|
||||
PaddingValues(
|
||||
start = 16.dp,
|
||||
top = 16.dp,
|
||||
end = 16.dp,
|
||||
bottom = 16.dp + 56.dp + 16.dp + 48.dp + 6.dp /* Scaffold Fab Spacing + Fab container height + SnackBar height */
|
||||
bottom = 16.dp + 16.dp + 56.dp /* Scaffold Fab Spacing + Fab container height */
|
||||
)
|
||||
},
|
||||
) {
|
||||
@@ -487,6 +354,7 @@ private fun ModuleList(
|
||||
|
||||
else -> {
|
||||
items(viewModel.moduleList) { module ->
|
||||
var isChecked by rememberSaveable(module) { mutableStateOf(module.enabled) }
|
||||
val scope = rememberCoroutineScope()
|
||||
val updatedModule by produceState(initialValue = Triple("", "", "")) {
|
||||
scope.launch(Dispatchers.IO) {
|
||||
@@ -494,51 +362,42 @@ private fun ModuleList(
|
||||
}
|
||||
}
|
||||
|
||||
ModuleItem(
|
||||
navigator = navigator,
|
||||
module = module,
|
||||
updateUrl = updatedModule.first,
|
||||
onUninstall = {
|
||||
scope.launch { onModuleUninstall(module) }
|
||||
},
|
||||
onCheckChanged = {
|
||||
scope.launch {
|
||||
val success = loadingDialog.withLoading {
|
||||
withContext(Dispatchers.IO) {
|
||||
toggleModule(module.id, !module.enabled)
|
||||
}
|
||||
ModuleItem(module, isChecked, updatedModule.first, onUninstall = {
|
||||
scope.launch { onModuleUninstall(module) }
|
||||
}, onCheckChanged = {
|
||||
scope.launch {
|
||||
val success = loadingDialog.withLoading {
|
||||
withContext(Dispatchers.IO) {
|
||||
toggleModule(module.id, !isChecked)
|
||||
}
|
||||
if (success) {
|
||||
viewModel.fetchModuleList()
|
||||
}
|
||||
if (success) {
|
||||
isChecked = it
|
||||
viewModel.fetchModuleList()
|
||||
|
||||
val result = snackBarHost.showSnackbar(
|
||||
message = rebootToApply,
|
||||
actionLabel = reboot,
|
||||
duration = SnackbarDuration.Long
|
||||
)
|
||||
if (result == SnackbarResult.ActionPerformed) {
|
||||
reboot()
|
||||
}
|
||||
} else {
|
||||
val message = if (module.enabled) failedDisable else failedEnable
|
||||
snackBarHost.showSnackbar(message.format(module.name))
|
||||
}
|
||||
}
|
||||
},
|
||||
onUpdate = {
|
||||
scope.launch {
|
||||
onModuleUpdate(
|
||||
module,
|
||||
updatedModule.third,
|
||||
updatedModule.first,
|
||||
"${module.name}-${updatedModule.second}.zip"
|
||||
val result = snackBarHost.showSnackbar(
|
||||
rebootToApply, actionLabel = reboot
|
||||
)
|
||||
if (result == SnackbarResult.ActionPerformed) {
|
||||
reboot()
|
||||
}
|
||||
} else {
|
||||
val message = if (isChecked) failedDisable else failedEnable
|
||||
snackBarHost.showSnackbar(message.format(module.name))
|
||||
}
|
||||
},
|
||||
onClick = {
|
||||
onClickModule(it.id, it.name, it.hasWebUi)
|
||||
}
|
||||
)
|
||||
}, onUpdate = {
|
||||
scope.launch {
|
||||
onModuleUpdate(
|
||||
module,
|
||||
updatedModule.third,
|
||||
updatedModule.first,
|
||||
"${module.name}-${updatedModule.second}.zip"
|
||||
)
|
||||
}
|
||||
}, onClick = {
|
||||
onClickModule(it.id, it.name, it.hasWebUi)
|
||||
})
|
||||
|
||||
// fix last item shadow incomplete in LazyColumn
|
||||
Spacer(Modifier.height(1.dp))
|
||||
@@ -549,13 +408,24 @@ private fun ModuleList(
|
||||
|
||||
DownloadListener(context, onInstallModule)
|
||||
|
||||
PullRefreshIndicator(
|
||||
refreshing = viewModel.isRefreshing, state = refreshState, modifier = Modifier.align(
|
||||
Alignment.TopCenter
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun ModuleItem(
|
||||
navigator: DestinationsNavigator,
|
||||
private fun TopBar() {
|
||||
TopAppBar(title = { Text(stringResource(R.string.module)) })
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ModuleItem(
|
||||
module: ModuleViewModel.ModuleInfo,
|
||||
isChecked: Boolean,
|
||||
updateUrl: String,
|
||||
onUninstall: (ModuleViewModel.ModuleInfo) -> Unit,
|
||||
onCheckChanged: (Boolean) -> Unit,
|
||||
@@ -563,31 +433,13 @@ fun ModuleItem(
|
||||
onClick: (ModuleViewModel.ModuleInfo) -> Unit
|
||||
) {
|
||||
ElevatedCard(
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
colors = CardDefaults.elevatedCardColors(containerColor = MaterialTheme.colorScheme.surface)
|
||||
) {
|
||||
val textDecoration = if (!module.remove) null else TextDecoration.LineThrough
|
||||
val interactionSource = remember { MutableInteractionSource() }
|
||||
val indication = LocalIndication.current
|
||||
val viewModel = viewModel<ModuleViewModel>()
|
||||
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.run {
|
||||
if (module.hasWebUi) {
|
||||
toggleable(
|
||||
value = module.enabled,
|
||||
enabled = !module.remove && module.enabled,
|
||||
interactionSource = interactionSource,
|
||||
role = Role.Button,
|
||||
indication = indication,
|
||||
onValueChange = { onClick(module) }
|
||||
)
|
||||
} else {
|
||||
this
|
||||
}
|
||||
}
|
||||
.padding(22.dp, 18.dp, 22.dp, 12.dp)
|
||||
) {
|
||||
val textDecoration = if (!module.remove) null else TextDecoration.LineThrough
|
||||
|
||||
Column(modifier = Modifier.clickable { onClick(module) }.padding(24.dp, 16.dp, 24.dp, 0.dp)) {
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
@@ -595,9 +447,7 @@ fun ModuleItem(
|
||||
val moduleVersion = stringResource(id = R.string.module_version)
|
||||
val moduleAuthor = stringResource(id = R.string.module_author)
|
||||
|
||||
Column(
|
||||
modifier = Modifier.fillMaxWidth(0.8f)
|
||||
) {
|
||||
Column(modifier = Modifier.fillMaxWidth(0.8f)) {
|
||||
Text(
|
||||
text = module.name,
|
||||
fontSize = MaterialTheme.typography.titleMedium.fontSize,
|
||||
@@ -632,9 +482,8 @@ fun ModuleItem(
|
||||
) {
|
||||
Switch(
|
||||
enabled = !module.update,
|
||||
checked = module.enabled,
|
||||
onCheckedChange = onCheckChanged,
|
||||
interactionSource = if (!module.hasWebUi) interactionSource else null
|
||||
checked = isChecked,
|
||||
onCheckedChange = onCheckChanged
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -652,114 +501,53 @@ fun ModuleItem(
|
||||
textDecoration = textDecoration
|
||||
)
|
||||
|
||||
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
|
||||
HorizontalDivider(thickness = Dp.Hairline)
|
||||
|
||||
Spacer(modifier = Modifier.height(4.dp))
|
||||
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
|
||||
if (module.hasActionScript) {
|
||||
FilledTonalButton(
|
||||
modifier = Modifier.defaultMinSize(52.dp, 32.dp),
|
||||
enabled = !module.remove && module.enabled,
|
||||
onClick = {
|
||||
navigator.navigate(ExecuteModuleActionScreenDestination(module.id))
|
||||
viewModel.markNeedRefresh()
|
||||
},
|
||||
contentPadding = ButtonDefaults.TextButtonContentPadding
|
||||
) {
|
||||
Icon(
|
||||
modifier = Modifier.size(20.dp),
|
||||
imageVector = Icons.Outlined.PlayArrow,
|
||||
contentDescription = null
|
||||
)
|
||||
if (!module.hasWebUi && updateUrl.isEmpty()) {
|
||||
Text(
|
||||
modifier = Modifier.padding(start = 7.dp),
|
||||
text = stringResource(R.string.action),
|
||||
fontFamily = MaterialTheme.typography.labelMedium.fontFamily,
|
||||
fontSize = MaterialTheme.typography.labelMedium.fontSize
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.weight(0.1f, true))
|
||||
}
|
||||
|
||||
if (module.hasWebUi) {
|
||||
FilledTonalButton(
|
||||
modifier = Modifier.defaultMinSize(52.dp, 32.dp),
|
||||
enabled = !module.remove && module.enabled,
|
||||
onClick = { onClick(module) },
|
||||
interactionSource = interactionSource,
|
||||
contentPadding = ButtonDefaults.TextButtonContentPadding
|
||||
) {
|
||||
Icon(
|
||||
modifier = Modifier.size(20.dp),
|
||||
imageVector = Icons.AutoMirrored.Outlined.Wysiwyg,
|
||||
contentDescription = null
|
||||
)
|
||||
if (!module.hasActionScript && updateUrl.isEmpty()) {
|
||||
Text(
|
||||
modifier = Modifier.padding(start = 7.dp),
|
||||
fontFamily = MaterialTheme.typography.labelMedium.fontFamily,
|
||||
fontSize = MaterialTheme.typography.labelMedium.fontSize,
|
||||
text = stringResource(R.string.open)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.weight(1f, true))
|
||||
|
||||
if (updateUrl.isNotEmpty()) {
|
||||
Button(
|
||||
modifier = Modifier.defaultMinSize(52.dp, 32.dp),
|
||||
enabled = !module.remove,
|
||||
modifier = Modifier
|
||||
.padding(0.dp)
|
||||
.defaultMinSize(52.dp, 32.dp),
|
||||
onClick = { onUpdate(module) },
|
||||
shape = ButtonDefaults.textShape,
|
||||
contentPadding = ButtonDefaults.TextButtonContentPadding
|
||||
shape = RoundedCornerShape(6.dp),
|
||||
contentPadding = PaddingValues(0.dp)
|
||||
) {
|
||||
Icon(
|
||||
modifier = Modifier.size(20.dp),
|
||||
imageVector = Icons.Outlined.Download,
|
||||
contentDescription = null
|
||||
)
|
||||
if (!module.hasActionScript || !module.hasWebUi) {
|
||||
Text(
|
||||
modifier = Modifier.padding(start = 7.dp),
|
||||
fontFamily = MaterialTheme.typography.labelMedium.fontFamily,
|
||||
fontSize = MaterialTheme.typography.labelMedium.fontSize,
|
||||
text = stringResource(R.string.module_update)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.weight(0.1f, true))
|
||||
}
|
||||
|
||||
FilledTonalButton(
|
||||
modifier = Modifier.defaultMinSize(52.dp, 32.dp),
|
||||
enabled = !module.remove,
|
||||
onClick = { onUninstall(module) },
|
||||
contentPadding = ButtonDefaults.TextButtonContentPadding
|
||||
) {
|
||||
Icon(
|
||||
modifier = Modifier.size(20.dp),
|
||||
imageVector = Icons.Outlined.Delete,
|
||||
contentDescription = null
|
||||
)
|
||||
if (!module.hasActionScript && !module.hasWebUi && updateUrl.isEmpty()) {
|
||||
Text(
|
||||
modifier = Modifier.padding(start = 7.dp),
|
||||
fontFamily = MaterialTheme.typography.labelMedium.fontFamily,
|
||||
fontSize = MaterialTheme.typography.labelMedium.fontSize,
|
||||
text = stringResource(R.string.uninstall)
|
||||
text = stringResource(R.string.module_update),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
TextButton(
|
||||
enabled = !module.remove,
|
||||
onClick = { onUninstall(module) },
|
||||
) {
|
||||
Text(
|
||||
fontFamily = MaterialTheme.typography.labelMedium.fontFamily,
|
||||
fontSize = MaterialTheme.typography.labelMedium.fontSize,
|
||||
text = stringResource(R.string.uninstall),
|
||||
)
|
||||
}
|
||||
|
||||
if (module.hasWebUi) {
|
||||
TextButton(
|
||||
onClick = { onClick(module) },
|
||||
) {
|
||||
Text(
|
||||
fontFamily = MaterialTheme.typography.labelMedium.fontFamily,
|
||||
fontSize = MaterialTheme.typography.labelMedium.fontSize,
|
||||
text = stringResource(R.string.open),
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -780,10 +568,9 @@ fun ModuleItemPreview() {
|
||||
description = "I am a test module and i do nothing but show a very long description",
|
||||
enabled = true,
|
||||
update = true,
|
||||
remove = false,
|
||||
remove = true,
|
||||
updateJson = "",
|
||||
hasWebUi = false,
|
||||
hasActionScript = false
|
||||
)
|
||||
ModuleItem(EmptyDestinationsNavigator, module, "", {}, {}, {}, {})
|
||||
}
|
||||
ModuleItem(module, true, "", {}, {}, {}, {})
|
||||
}
|
||||
@@ -1,20 +1,18 @@
|
||||
package me.weishu.kernelsu.ui.screen
|
||||
|
||||
import android.content.ContentResolver
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.database.Cursor
|
||||
import android.net.Uri
|
||||
import android.provider.OpenableColumns
|
||||
import android.util.Log
|
||||
import android.widget.Toast
|
||||
import androidx.activity.compose.rememberLauncherForActivityResult
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.WindowInsets
|
||||
import androidx.compose.foundation.layout.WindowInsetsSides
|
||||
import androidx.compose.foundation.layout.only
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.safeDrawing
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.icons.Icons
|
||||
@@ -27,23 +25,19 @@ import androidx.compose.material.icons.filled.Delete
|
||||
import androidx.compose.material.icons.filled.DeleteForever
|
||||
import androidx.compose.material.icons.filled.DeveloperMode
|
||||
import androidx.compose.material.icons.filled.Fence
|
||||
import androidx.compose.material.icons.filled.FolderDelete
|
||||
import androidx.compose.material.icons.filled.RemoveModerator
|
||||
import androidx.compose.material.icons.filled.Save
|
||||
import androidx.compose.material.icons.filled.Share
|
||||
import androidx.compose.material.icons.filled.Update
|
||||
import androidx.compose.material3.BottomSheetScaffold
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.ListItem
|
||||
import androidx.compose.material3.ModalBottomSheet
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.SnackbarHost
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TopAppBar
|
||||
import androidx.compose.material3.TopAppBarDefaults
|
||||
import androidx.compose.material3.TopAppBarScrollBehavior
|
||||
import androidx.compose.material3.rememberTopAppBarState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
@@ -54,7 +48,6 @@ import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.LineHeightStyle
|
||||
@@ -62,7 +55,6 @@ import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.core.content.FileProvider
|
||||
import androidx.lifecycle.compose.dropUnlessResumed
|
||||
import com.maxkeppeker.sheets.core.models.base.Header
|
||||
import com.maxkeppeker.sheets.core.models.base.IconSource
|
||||
import com.maxkeppeker.sheets.core.models.base.rememberUseCaseState
|
||||
@@ -70,9 +62,6 @@ import com.maxkeppeler.sheets.list.ListDialog
|
||||
import com.maxkeppeler.sheets.list.models.ListOption
|
||||
import com.maxkeppeler.sheets.list.models.ListSelection
|
||||
import com.ramcosta.composedestinations.annotation.Destination
|
||||
import com.ramcosta.composedestinations.annotation.RootGraph
|
||||
import com.ramcosta.composedestinations.generated.destinations.AppProfileTemplateScreenDestination
|
||||
import com.ramcosta.composedestinations.generated.destinations.FlashScreenDestination
|
||||
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
|
||||
import com.ramcosta.composedestinations.navigation.EmptyDestinationsNavigator
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
@@ -85,12 +74,13 @@ import me.weishu.kernelsu.ui.component.AboutDialog
|
||||
import me.weishu.kernelsu.ui.component.ConfirmResult
|
||||
import me.weishu.kernelsu.ui.component.DialogHandle
|
||||
import me.weishu.kernelsu.ui.component.SwitchItem
|
||||
import me.weishu.kernelsu.ui.component.KsuIsValid
|
||||
import me.weishu.kernelsu.ui.component.rememberConfirmDialog
|
||||
import me.weishu.kernelsu.ui.component.rememberCustomDialog
|
||||
import me.weishu.kernelsu.ui.component.rememberLoadingDialog
|
||||
import me.weishu.kernelsu.ui.util.LocalSnackbarHost
|
||||
import me.weishu.kernelsu.ui.screen.destinations.AppProfileTemplateScreenDestination
|
||||
import me.weishu.kernelsu.ui.screen.destinations.FlashScreenDestination
|
||||
import me.weishu.kernelsu.ui.util.getBugreportFile
|
||||
import me.weishu.kernelsu.ui.util.getFileNameFromUri
|
||||
import me.weishu.kernelsu.ui.util.shrinkModules
|
||||
import java.time.LocalDateTime
|
||||
import java.time.format.DateTimeFormatter
|
||||
@@ -100,23 +90,15 @@ import java.time.format.DateTimeFormatter
|
||||
* @date 2023/1/1.
|
||||
*/
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Destination<RootGraph>
|
||||
@Destination
|
||||
@Composable
|
||||
fun SettingScreen(navigator: DestinationsNavigator) {
|
||||
val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState())
|
||||
val snackBarHost = LocalSnackbarHost.current
|
||||
|
||||
Scaffold(
|
||||
topBar = {
|
||||
TopBar(
|
||||
onBack = dropUnlessResumed {
|
||||
navigator.popBackStack()
|
||||
},
|
||||
scrollBehavior = scrollBehavior
|
||||
)
|
||||
},
|
||||
snackbarHost = { SnackbarHost(snackBarHost) },
|
||||
contentWindowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal)
|
||||
TopBar(onBack = {
|
||||
navigator.popBackStack()
|
||||
})
|
||||
}
|
||||
) { paddingValues ->
|
||||
val aboutDialog = rememberCustomDialog {
|
||||
AboutDialog(it)
|
||||
@@ -127,74 +109,33 @@ fun SettingScreen(navigator: DestinationsNavigator) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.padding(paddingValues)
|
||||
.nestedScroll(scrollBehavior.nestedScrollConnection)
|
||||
.verticalScroll(rememberScrollState())
|
||||
) {
|
||||
|
||||
val context = LocalContext.current
|
||||
val scope = rememberCoroutineScope()
|
||||
|
||||
val exportBugreportLauncher = rememberLauncherForActivityResult(
|
||||
ActivityResultContracts.CreateDocument("application/gzip")
|
||||
) { uri: Uri? ->
|
||||
if (uri == null) return@rememberLauncherForActivityResult
|
||||
scope.launch(Dispatchers.IO) {
|
||||
loadingDialog.show()
|
||||
context.contentResolver.openOutputStream(uri)?.use { output ->
|
||||
getBugreportFile(context).inputStream().use {
|
||||
it.copyTo(output)
|
||||
}
|
||||
}
|
||||
loadingDialog.hide()
|
||||
snackBarHost.showSnackbar(context.getString(R.string.log_saved))
|
||||
}
|
||||
}
|
||||
|
||||
val profileTemplate = stringResource(id = R.string.settings_profile_template)
|
||||
KsuIsValid() {
|
||||
ListItem(
|
||||
leadingContent = { Icon(Icons.Filled.Fence, profileTemplate) },
|
||||
headlineContent = { Text(profileTemplate) },
|
||||
supportingContent = { Text(stringResource(id = R.string.settings_profile_template_summary)) },
|
||||
modifier = Modifier.clickable {
|
||||
navigator.navigate(AppProfileTemplateScreenDestination)
|
||||
}
|
||||
)
|
||||
}
|
||||
ListItem(
|
||||
leadingContent = { Icon(Icons.Filled.Fence, profileTemplate) },
|
||||
headlineContent = { Text(profileTemplate) },
|
||||
supportingContent = { Text(stringResource(id = R.string.settings_profile_template_summary)) },
|
||||
modifier = Modifier.clickable {
|
||||
navigator.navigate(AppProfileTemplateScreenDestination)
|
||||
}
|
||||
)
|
||||
|
||||
var umountChecked by rememberSaveable {
|
||||
mutableStateOf(Natives.isDefaultUmountModules())
|
||||
}
|
||||
|
||||
KsuIsValid() {
|
||||
SwitchItem(
|
||||
icon = Icons.Filled.FolderDelete,
|
||||
title = stringResource(id = R.string.settings_umount_modules_default),
|
||||
summary = stringResource(id = R.string.settings_umount_modules_default_summary),
|
||||
checked = umountChecked
|
||||
) {
|
||||
if (Natives.setDefaultUmountModules(it)) {
|
||||
umountChecked = it
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
KsuIsValid() {
|
||||
if (Natives.version >= Natives.MINIMAL_SUPPORTED_SU_COMPAT) {
|
||||
var isSuDisabled by rememberSaveable {
|
||||
mutableStateOf(!Natives.isSuEnabled())
|
||||
}
|
||||
SwitchItem(
|
||||
icon = Icons.Filled.RemoveModerator,
|
||||
title = stringResource(id = R.string.settings_disable_su),
|
||||
summary = stringResource(id = R.string.settings_disable_su_summary),
|
||||
checked = isSuDisabled,
|
||||
) { checked ->
|
||||
val shouldEnable = !checked
|
||||
if (Natives.setSuEnabled(shouldEnable)) {
|
||||
isSuDisabled = !shouldEnable
|
||||
}
|
||||
}
|
||||
SwitchItem(
|
||||
icon = Icons.Filled.RemoveModerator,
|
||||
title = stringResource(id = R.string.settings_umount_modules_default),
|
||||
summary = stringResource(id = R.string.settings_umount_modules_default_summary),
|
||||
checked = umountChecked
|
||||
) {
|
||||
if (Natives.setDefaultUmountModules(it)) {
|
||||
umountChecked = it
|
||||
}
|
||||
}
|
||||
|
||||
@@ -219,17 +160,14 @@ fun SettingScreen(navigator: DestinationsNavigator) {
|
||||
prefs.getBoolean("enable_web_debugging", false)
|
||||
)
|
||||
}
|
||||
|
||||
KsuIsValid() {
|
||||
SwitchItem(
|
||||
icon = Icons.Filled.DeveloperMode,
|
||||
title = stringResource(id = R.string.enable_web_debugging),
|
||||
summary = stringResource(id = R.string.enable_web_debugging_summary),
|
||||
checked = enableWebDebugging
|
||||
) {
|
||||
prefs.edit().putBoolean("enable_web_debugging", it).apply()
|
||||
enableWebDebugging = it
|
||||
}
|
||||
SwitchItem(
|
||||
icon = Icons.Filled.DeveloperMode,
|
||||
title = stringResource(id = R.string.enable_web_debugging),
|
||||
summary = stringResource(id = R.string.enable_web_debugging_summary),
|
||||
checked = enableWebDebugging
|
||||
) {
|
||||
prefs.edit().putBoolean("enable_web_debugging", it).apply()
|
||||
enableWebDebugging = it
|
||||
}
|
||||
|
||||
var showBottomsheet by remember { mutableStateOf(false) }
|
||||
@@ -246,25 +184,46 @@ fun SettingScreen(navigator: DestinationsNavigator) {
|
||||
showBottomsheet = true
|
||||
}
|
||||
)
|
||||
if (showBottomsheet) {
|
||||
if (showBottomsheet){
|
||||
ModalBottomSheet(
|
||||
onDismissRequest = { showBottomsheet = false },
|
||||
content = {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.padding(10.dp)
|
||||
.align(Alignment.CenterHorizontally)
|
||||
Row(modifier = Modifier.padding(10.dp)
|
||||
.align(Alignment.CenterHorizontally)
|
||||
|
||||
) {
|
||||
Box {
|
||||
Box{
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.padding(16.dp)
|
||||
modifier = Modifier.padding(16.dp)
|
||||
.clickable {
|
||||
val formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd_HH_mm")
|
||||
val current = LocalDateTime.now().format(formatter)
|
||||
exportBugreportLauncher.launch("KernelSU_bugreport_${current}.tar.gz")
|
||||
showBottomsheet = false
|
||||
scope.launch {
|
||||
val bugreport = loadingDialog.withLoading {
|
||||
withContext(Dispatchers.IO) {
|
||||
getBugreportFile(context)
|
||||
}
|
||||
}
|
||||
|
||||
val uri: Uri =
|
||||
FileProvider.getUriForFile(
|
||||
context,
|
||||
"${BuildConfig.APPLICATION_ID}.fileprovider",
|
||||
bugreport
|
||||
)
|
||||
val filename = getFileNameFromUri(context , uri)
|
||||
val savefile = Intent(Intent.ACTION_CREATE_DOCUMENT).apply {
|
||||
addCategory(Intent.CATEGORY_OPENABLE)
|
||||
type = "application/zip"
|
||||
putExtra(Intent.EXTRA_STREAM, uri)
|
||||
putExtra(Intent.EXTRA_TITLE, filename)
|
||||
flags = Intent.FLAG_GRANT_READ_URI_PERMISSION
|
||||
}
|
||||
context.startActivity(
|
||||
Intent.createChooser(
|
||||
savefile,
|
||||
context.getString(R.string.save_log)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
) {
|
||||
Icon(
|
||||
@@ -284,11 +243,11 @@ fun SettingScreen(navigator: DestinationsNavigator) {
|
||||
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
Box {
|
||||
Box{
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.padding(16.dp)
|
||||
modifier = Modifier.padding(16.dp)
|
||||
.clickable {
|
||||
scope.launch {
|
||||
val bugreport = loadingDialog.withLoading {
|
||||
@@ -304,11 +263,10 @@ fun SettingScreen(navigator: DestinationsNavigator) {
|
||||
bugreport
|
||||
)
|
||||
|
||||
val shareIntent = Intent(Intent.ACTION_SEND).apply {
|
||||
putExtra(Intent.EXTRA_STREAM, uri)
|
||||
setDataAndType(uri, "application/gzip")
|
||||
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
||||
}
|
||||
val shareIntent = Intent(Intent.ACTION_SEND)
|
||||
shareIntent.putExtra(Intent.EXTRA_STREAM, uri)
|
||||
shareIntent.setDataAndType(uri, "application/zip")
|
||||
shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
||||
|
||||
context.startActivity(
|
||||
Intent.createChooser(
|
||||
@@ -333,39 +291,43 @@ fun SettingScreen(navigator: DestinationsNavigator) {
|
||||
trim = LineHeightStyle.Trim.None
|
||||
)
|
||||
}
|
||||
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
}
|
||||
|
||||
val shrink = stringResource(id = R.string.shrink_sparse_image)
|
||||
val shrinkMessage = stringResource(id = R.string.shrink_sparse_image_message)
|
||||
KsuIsValid() {
|
||||
ListItem(
|
||||
leadingContent = {
|
||||
Icon(
|
||||
Icons.Filled.Compress,
|
||||
shrink
|
||||
)
|
||||
},
|
||||
headlineContent = { Text(shrink) },
|
||||
modifier = Modifier.clickable {
|
||||
scope.launch {
|
||||
val result = shrinkDialog.awaitConfirm(title = shrink, content = shrinkMessage)
|
||||
if (result == ConfirmResult.Confirmed) {
|
||||
loadingDialog.withLoading {
|
||||
shrinkModules()
|
||||
}
|
||||
ListItem(
|
||||
leadingContent = {
|
||||
Icon(
|
||||
Icons.Filled.Compress,
|
||||
shrink
|
||||
)
|
||||
},
|
||||
headlineContent = { Text(shrink) },
|
||||
modifier = Modifier.clickable {
|
||||
scope.launch {
|
||||
val result =
|
||||
shrinkDialog.awaitConfirm(title = shrink, content = shrinkMessage)
|
||||
if (result == ConfirmResult.Confirmed) {
|
||||
loadingDialog.withLoading {
|
||||
shrinkModules()
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
val lkmMode = Natives.version >= Natives.MINIMAL_SUPPORTED_KERNEL_LKM && Natives.isLkmMode
|
||||
val lkmMode =
|
||||
Natives.version >= Natives.MINIMAL_SUPPORTED_KERNEL_LKM && Natives.isLkmMode
|
||||
if (lkmMode) {
|
||||
UninstallItem(navigator) {
|
||||
loadingDialog.withLoading(it)
|
||||
@@ -377,7 +339,7 @@ fun SettingScreen(navigator: DestinationsNavigator) {
|
||||
leadingContent = {
|
||||
Icon(
|
||||
Icons.Filled.ContactPage,
|
||||
about
|
||||
stringResource(id = R.string.about)
|
||||
)
|
||||
},
|
||||
headlineContent = { Text(about) },
|
||||
@@ -388,7 +350,6 @@ fun SettingScreen(navigator: DestinationsNavigator) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun UninstallItem(
|
||||
navigator: DestinationsNavigator,
|
||||
@@ -413,9 +374,11 @@ fun UninstallItem(
|
||||
UninstallType.PERMANENT -> navigator.navigate(
|
||||
FlashScreenDestination(FlashIt.FlashUninstall)
|
||||
)
|
||||
|
||||
UninstallType.RESTORE_STOCK_IMAGE -> navigator.navigate(
|
||||
FlashScreenDestination(FlashIt.FlashRestore)
|
||||
)
|
||||
|
||||
UninstallType.NONE -> Unit
|
||||
}
|
||||
}
|
||||
@@ -493,21 +456,14 @@ fun rememberUninstallDialog(onSelected: (UninstallType) -> Unit): DialogHandle {
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
private fun TopBar(
|
||||
onBack: () -> Unit = {},
|
||||
scrollBehavior: TopAppBarScrollBehavior? = null
|
||||
) {
|
||||
private fun TopBar(onBack: () -> Unit = {}) {
|
||||
TopAppBar(
|
||||
title = { Text(stringResource(R.string.settings)) },
|
||||
navigationIcon = {
|
||||
IconButton(
|
||||
onClick = onBack
|
||||
) {
|
||||
Icon(Icons.AutoMirrored.Filled.ArrowBack, contentDescription = null)
|
||||
}
|
||||
) { Icon(Icons.AutoMirrored.Filled.ArrowBack, contentDescription = null) }
|
||||
},
|
||||
windowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal),
|
||||
scrollBehavior = scrollBehavior
|
||||
)
|
||||
}
|
||||
|
||||
@@ -515,4 +471,4 @@ private fun TopBar(
|
||||
@Composable
|
||||
private fun SettingsPreview() {
|
||||
SettingScreen(EmptyDestinationsNavigator)
|
||||
}
|
||||
}
|
||||
@@ -4,18 +4,19 @@ import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.ExperimentalMaterialApi
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.MoreVert
|
||||
import androidx.compose.material.pullrefresh.PullRefreshIndicator
|
||||
import androidx.compose.material.pullrefresh.pullRefresh
|
||||
import androidx.compose.material.pullrefresh.rememberPullRefreshState
|
||||
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.graphics.Color
|
||||
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
@@ -25,37 +26,27 @@ import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import coil.compose.AsyncImage
|
||||
import coil.request.ImageRequest
|
||||
import com.ramcosta.composedestinations.annotation.Destination
|
||||
import com.ramcosta.composedestinations.annotation.RootGraph
|
||||
import com.ramcosta.composedestinations.generated.destinations.AppProfileScreenDestination
|
||||
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
|
||||
import kotlinx.coroutines.launch
|
||||
import me.weishu.kernelsu.Natives
|
||||
import me.weishu.kernelsu.R
|
||||
import me.weishu.kernelsu.ui.component.SearchAppBar
|
||||
import me.weishu.kernelsu.ui.screen.destinations.AppProfileScreenDestination
|
||||
import me.weishu.kernelsu.ui.viewmodel.SuperUserViewModel
|
||||
|
||||
@OptIn(ExperimentalMaterialApi::class, ExperimentalMaterial3Api::class)
|
||||
@Destination<RootGraph>
|
||||
@OptIn(ExperimentalMaterialApi::class)
|
||||
@Destination
|
||||
@Composable
|
||||
fun SuperUserScreen(navigator: DestinationsNavigator) {
|
||||
val viewModel = viewModel<SuperUserViewModel>()
|
||||
val scope = rememberCoroutineScope()
|
||||
val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState())
|
||||
val listState = rememberLazyListState()
|
||||
|
||||
LaunchedEffect(key1 = navigator) {
|
||||
viewModel.search = ""
|
||||
LaunchedEffect(Unit) {
|
||||
if (viewModel.appList.isEmpty()) {
|
||||
viewModel.fetchAppList()
|
||||
}
|
||||
}
|
||||
|
||||
LaunchedEffect(viewModel.search) {
|
||||
if (viewModel.search.isEmpty()) {
|
||||
listState.scrollToItem(0)
|
||||
}
|
||||
}
|
||||
|
||||
Scaffold(
|
||||
topBar = {
|
||||
SearchAppBar(
|
||||
@@ -100,30 +91,32 @@ fun SuperUserScreen(navigator: DestinationsNavigator) {
|
||||
}
|
||||
}
|
||||
},
|
||||
scrollBehavior = scrollBehavior
|
||||
)
|
||||
},
|
||||
contentWindowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal)
|
||||
}
|
||||
) { innerPadding ->
|
||||
PullToRefreshBox(
|
||||
modifier = Modifier.padding(innerPadding),
|
||||
onRefresh = {
|
||||
scope.launch { viewModel.fetchAppList() }
|
||||
},
|
||||
isRefreshing = viewModel.isRefreshing
|
||||
val refreshState = rememberPullRefreshState(
|
||||
refreshing = viewModel.isRefreshing,
|
||||
onRefresh = { scope.launch { viewModel.fetchAppList() } },
|
||||
)
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.padding(innerPadding)
|
||||
.pullRefresh(refreshState)
|
||||
) {
|
||||
LazyColumn(
|
||||
state = listState,
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.nestedScroll(scrollBehavior.nestedScrollConnection)
|
||||
) {
|
||||
LazyColumn(Modifier.fillMaxSize()) {
|
||||
items(viewModel.appList, key = { it.packageName + it.uid }) { app ->
|
||||
AppItem(app) {
|
||||
navigator.navigate(AppProfileScreenDestination(app))
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
PullRefreshIndicator(
|
||||
refreshing = viewModel.isRefreshing,
|
||||
state = refreshState,
|
||||
modifier = Modifier.align(Alignment.TopCenter)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,16 +2,13 @@ package me.weishu.kernelsu.ui.screen
|
||||
|
||||
import android.widget.Toast
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.ExperimentalLayoutApi
|
||||
import androidx.compose.foundation.layout.FlowRow
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.WindowInsets
|
||||
import androidx.compose.foundation.layout.WindowInsetsSides
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.only
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.safeDrawing
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.material.ExperimentalMaterialApi
|
||||
@@ -20,6 +17,9 @@ import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
||||
import androidx.compose.material.icons.filled.Add
|
||||
import androidx.compose.material.icons.filled.ImportExport
|
||||
import androidx.compose.material.icons.filled.Sync
|
||||
import androidx.compose.material.pullrefresh.PullRefreshIndicator
|
||||
import androidx.compose.material.pullrefresh.pullRefresh
|
||||
import androidx.compose.material.pullrefresh.rememberPullRefreshState
|
||||
import androidx.compose.material3.DropdownMenu
|
||||
import androidx.compose.material3.DropdownMenuItem
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
@@ -31,10 +31,6 @@ import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TopAppBar
|
||||
import androidx.compose.material3.TopAppBarDefaults
|
||||
import androidx.compose.material3.TopAppBarScrollBehavior
|
||||
import androidx.compose.material3.pulltorefresh.PullToRefreshBox
|
||||
import androidx.compose.material3.rememberTopAppBarState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
@@ -42,24 +38,22 @@ import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||
import androidx.compose.ui.platform.LocalClipboardManager
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.AnnotatedString
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.compose.dropUnlessResumed
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import com.ramcosta.composedestinations.annotation.Destination
|
||||
import com.ramcosta.composedestinations.annotation.RootGraph
|
||||
import com.ramcosta.composedestinations.generated.destinations.TemplateEditorScreenDestination
|
||||
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
|
||||
import com.ramcosta.composedestinations.result.ResultRecipient
|
||||
import com.ramcosta.composedestinations.result.getOr
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import me.weishu.kernelsu.R
|
||||
import me.weishu.kernelsu.ui.screen.destinations.TemplateEditorScreenDestination
|
||||
import me.weishu.kernelsu.ui.viewmodel.TemplateViewModel
|
||||
|
||||
/**
|
||||
@@ -67,8 +61,8 @@ import me.weishu.kernelsu.ui.viewmodel.TemplateViewModel
|
||||
* @date 2023/10/20.
|
||||
*/
|
||||
|
||||
@OptIn(ExperimentalMaterialApi::class, ExperimentalMaterial3Api::class)
|
||||
@Destination<RootGraph>
|
||||
@OptIn(ExperimentalMaterialApi::class)
|
||||
@Destination
|
||||
@Composable
|
||||
fun AppProfileTemplateScreen(
|
||||
navigator: DestinationsNavigator,
|
||||
@@ -76,7 +70,6 @@ fun AppProfileTemplateScreen(
|
||||
) {
|
||||
val viewModel = viewModel<TemplateViewModel>()
|
||||
val scope = rememberCoroutineScope()
|
||||
val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState())
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
if (viewModel.templateList.isEmpty()) {
|
||||
@@ -100,8 +93,7 @@ fun AppProfileTemplateScreen(
|
||||
Toast.makeText(context, msg, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
TopBar(
|
||||
onBack = dropUnlessResumed { navigator.popBackStack() },
|
||||
TopBar(onBack = { navigator.popBackStack() },
|
||||
onSync = {
|
||||
scope.launch { viewModel.fetchTemplates(true) }
|
||||
},
|
||||
@@ -132,8 +124,7 @@ fun AppProfileTemplateScreen(
|
||||
clipboardManager.setText(AnnotatedString(it))
|
||||
}
|
||||
}
|
||||
},
|
||||
scrollBehavior = scrollBehavior
|
||||
}
|
||||
)
|
||||
},
|
||||
floatingActionButton = {
|
||||
@@ -150,27 +141,29 @@ fun AppProfileTemplateScreen(
|
||||
text = { Text(stringResource(id = R.string.app_profile_template_create)) },
|
||||
)
|
||||
},
|
||||
contentWindowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal)
|
||||
) { innerPadding ->
|
||||
PullToRefreshBox(
|
||||
modifier = Modifier.padding(innerPadding),
|
||||
isRefreshing = viewModel.isRefreshing,
|
||||
onRefresh = {
|
||||
scope.launch { viewModel.fetchTemplates() }
|
||||
}
|
||||
val refreshState = rememberPullRefreshState(
|
||||
refreshing = viewModel.isRefreshing,
|
||||
onRefresh = { scope.launch { viewModel.fetchTemplates() } },
|
||||
)
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.padding(innerPadding)
|
||||
.pullRefresh(refreshState)
|
||||
) {
|
||||
LazyColumn(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.nestedScroll(scrollBehavior.nestedScrollConnection),
|
||||
contentPadding = remember {
|
||||
PaddingValues(bottom = 16.dp + 56.dp + 16.dp /* Scaffold Fab Spacing + Fab container height */)
|
||||
}
|
||||
) {
|
||||
LazyColumn(Modifier.fillMaxSize(), contentPadding = remember {
|
||||
PaddingValues(bottom = 16.dp + 16.dp + 56.dp /* Scaffold Fab Spacing + Fab container height */)
|
||||
}) {
|
||||
items(viewModel.templateList, key = { it.id }) { app ->
|
||||
TemplateItem(navigator, app)
|
||||
}
|
||||
}
|
||||
|
||||
PullRefreshIndicator(
|
||||
refreshing = viewModel.isRefreshing,
|
||||
state = refreshState,
|
||||
modifier = Modifier.align(Alignment.TopCenter)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -216,8 +209,7 @@ private fun TopBar(
|
||||
onBack: () -> Unit,
|
||||
onSync: () -> Unit = {},
|
||||
onImport: () -> Unit = {},
|
||||
onExport: () -> Unit = {},
|
||||
scrollBehavior: TopAppBarScrollBehavior? = null
|
||||
onExport: () -> Unit = {}
|
||||
) {
|
||||
TopAppBar(
|
||||
title = {
|
||||
@@ -262,8 +254,6 @@ private fun TopBar(
|
||||
})
|
||||
}
|
||||
}
|
||||
},
|
||||
windowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal),
|
||||
scrollBehavior = scrollBehavior
|
||||
}
|
||||
)
|
||||
}
|
||||
@@ -3,12 +3,8 @@ package me.weishu.kernelsu.ui.screen
|
||||
import android.widget.Toast
|
||||
import androidx.activity.compose.BackHandler
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.WindowInsets
|
||||
import androidx.compose.foundation.layout.WindowInsetsSides
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.only
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.safeDrawing
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.text.KeyboardActions
|
||||
import androidx.compose.foundation.text.KeyboardOptions
|
||||
@@ -26,9 +22,6 @@ import androidx.compose.material3.OutlinedTextField
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TopAppBar
|
||||
import androidx.compose.material3.TopAppBarDefaults
|
||||
import androidx.compose.material3.TopAppBarScrollBehavior
|
||||
import androidx.compose.material3.rememberTopAppBarState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
@@ -37,16 +30,13 @@ import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.ExperimentalComposeUiApi
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||
import androidx.compose.ui.input.pointer.pointerInteropFilter
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.input.ImeAction
|
||||
import androidx.compose.ui.text.input.KeyboardType
|
||||
import androidx.lifecycle.compose.dropUnlessResumed
|
||||
import com.ramcosta.composedestinations.annotation.Destination
|
||||
import com.ramcosta.composedestinations.annotation.RootGraph
|
||||
import com.ramcosta.composedestinations.result.ResultBackNavigator
|
||||
import me.weishu.kernelsu.Natives
|
||||
import me.weishu.kernelsu.R
|
||||
@@ -61,8 +51,8 @@ import me.weishu.kernelsu.ui.viewmodel.toJSON
|
||||
* @author weishu
|
||||
* @date 2023/10/20.
|
||||
*/
|
||||
@OptIn(ExperimentalComposeUiApi::class, ExperimentalMaterial3Api::class)
|
||||
@Destination<RootGraph>
|
||||
@OptIn(ExperimentalComposeUiApi::class)
|
||||
@Destination
|
||||
@Composable
|
||||
fun TemplateEditorScreen(
|
||||
navigator: ResultBackNavigator<Boolean>,
|
||||
@@ -77,8 +67,6 @@ fun TemplateEditorScreen(
|
||||
mutableStateOf(initialTemplate)
|
||||
}
|
||||
|
||||
val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState())
|
||||
|
||||
BackHandler {
|
||||
navigator.navigateBack(result = !readOnly)
|
||||
}
|
||||
@@ -106,7 +94,7 @@ fun TemplateEditorScreen(
|
||||
},
|
||||
readOnly = readOnly,
|
||||
summary = titleSummary,
|
||||
onBack = dropUnlessResumed { navigator.navigateBack(result = !readOnly) },
|
||||
onBack = { navigator.navigateBack(result = !readOnly) },
|
||||
onDelete = {
|
||||
if (deleteAppProfileTemplate(template.id)) {
|
||||
navigator.navigateBack(result = true)
|
||||
@@ -118,16 +106,12 @@ fun TemplateEditorScreen(
|
||||
} else {
|
||||
Toast.makeText(context, saveTemplateFailed, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
},
|
||||
scrollBehavior = scrollBehavior
|
||||
)
|
||||
})
|
||||
},
|
||||
contentWindowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal)
|
||||
) { innerPadding ->
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.padding(innerPadding)
|
||||
.nestedScroll(scrollBehavior.nestedScrollConnection)
|
||||
.verticalScroll(rememberScrollState())
|
||||
.pointerInteropFilter {
|
||||
// disable click and ripple if readOnly
|
||||
@@ -256,44 +240,39 @@ private fun TopBar(
|
||||
summary: String = "",
|
||||
onBack: () -> Unit,
|
||||
onDelete: () -> Unit = {},
|
||||
onSave: () -> Unit = {},
|
||||
scrollBehavior: TopAppBarScrollBehavior? = null
|
||||
onSave: () -> Unit = {}
|
||||
) {
|
||||
TopAppBar(
|
||||
title = {
|
||||
Column {
|
||||
Text(title)
|
||||
if (summary.isNotBlank()) {
|
||||
Text(
|
||||
text = summary,
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
)
|
||||
}
|
||||
}
|
||||
}, navigationIcon = {
|
||||
IconButton(
|
||||
onClick = onBack
|
||||
) { Icon(Icons.AutoMirrored.Filled.ArrowBack, contentDescription = null) }
|
||||
}, actions = {
|
||||
if (readOnly) {
|
||||
return@TopAppBar
|
||||
}
|
||||
IconButton(onClick = onDelete) {
|
||||
Icon(
|
||||
Icons.Filled.DeleteForever,
|
||||
contentDescription = stringResource(id = R.string.app_profile_template_delete)
|
||||
TopAppBar(title = {
|
||||
Column {
|
||||
Text(title)
|
||||
if (summary.isNotBlank()) {
|
||||
Text(
|
||||
text = summary,
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
)
|
||||
}
|
||||
IconButton(onClick = onSave) {
|
||||
Icon(
|
||||
imageVector = Icons.Filled.Save,
|
||||
contentDescription = stringResource(id = R.string.app_profile_template_save)
|
||||
)
|
||||
}
|
||||
},
|
||||
windowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal),
|
||||
scrollBehavior = scrollBehavior
|
||||
)
|
||||
}
|
||||
}, navigationIcon = {
|
||||
IconButton(
|
||||
onClick = onBack
|
||||
) { Icon(Icons.AutoMirrored.Filled.ArrowBack, contentDescription = null) }
|
||||
}, actions = {
|
||||
if (readOnly) {
|
||||
return@TopAppBar
|
||||
}
|
||||
IconButton(onClick = onDelete) {
|
||||
Icon(
|
||||
Icons.Filled.DeleteForever,
|
||||
contentDescription = stringResource(id = R.string.app_profile_template_delete)
|
||||
)
|
||||
}
|
||||
IconButton(onClick = onSave) {
|
||||
Icon(
|
||||
imageVector = Icons.Filled.Save,
|
||||
contentDescription = stringResource(id = R.string.app_profile_template_save)
|
||||
)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@Composable
|
||||
@@ -310,14 +289,17 @@ private fun TextEdit(
|
||||
value = text,
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
label = { Text(label) },
|
||||
suffix = {
|
||||
if (errorHint.isNotBlank()) {
|
||||
suffix =
|
||||
if (errorHint.isNotBlank()) {
|
||||
{
|
||||
Text(
|
||||
text = if (isError) errorHint else "",
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
color = MaterialTheme.colorScheme.error
|
||||
)
|
||||
}
|
||||
} else {
|
||||
null
|
||||
},
|
||||
isError = isError,
|
||||
keyboardOptions = KeyboardOptions(
|
||||
@@ -332,7 +314,7 @@ private fun TextEdit(
|
||||
}
|
||||
|
||||
private fun isValidTemplateId(id: String): Boolean {
|
||||
return Regex("""^([A-Za-z][A-Za-z\d_]*\.)*[A-Za-z][A-Za-z\d_]*$""").matches(id)
|
||||
return Regex("""^([A-Za-z]{1}[A-Za-z\d_]*\.)*[A-Za-z][A-Za-z\d_]*$""").matches(id)
|
||||
}
|
||||
|
||||
private fun isTemplateExist(id: String): Boolean {
|
||||
|
||||
@@ -7,8 +7,12 @@ import androidx.compose.material3.darkColorScheme
|
||||
import androidx.compose.material3.dynamicDarkColorScheme
|
||||
import androidx.compose.material3.dynamicLightColorScheme
|
||||
import androidx.compose.material3.lightColorScheme
|
||||
import androidx.compose.material3.surfaceColorAtElevation
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.SideEffect
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.google.accompanist.systemuicontroller.rememberSystemUiController
|
||||
|
||||
private val DarkColorScheme = darkColorScheme(
|
||||
primary = YELLOW,
|
||||
@@ -38,6 +42,20 @@ fun KernelSUTheme(
|
||||
else -> LightColorScheme
|
||||
}
|
||||
|
||||
val systemUiController = rememberSystemUiController()
|
||||
SideEffect {
|
||||
systemUiController.setStatusBarColor(
|
||||
color = colorScheme.surface,
|
||||
darkIcons = !darkTheme
|
||||
)
|
||||
|
||||
// To match the App Navbar color
|
||||
systemUiController.setNavigationBarColor(
|
||||
color = colorScheme.surfaceColorAtElevation(8.dp),
|
||||
darkIcons = !darkTheme,
|
||||
)
|
||||
}
|
||||
|
||||
MaterialTheme(
|
||||
colorScheme = colorScheme,
|
||||
typography = Typography,
|
||||
|
||||
@@ -7,11 +7,10 @@ import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.IntentFilter
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.os.Environment
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.DisposableEffect
|
||||
import androidx.core.content.ContextCompat
|
||||
import me.weishu.kernelsu.ksuApp
|
||||
import me.weishu.kernelsu.ui.util.module.LatestVersionInfo
|
||||
|
||||
/**
|
||||
@@ -27,7 +26,8 @@ fun download(
|
||||
onDownloaded: (Uri) -> Unit = {},
|
||||
onDownloading: () -> Unit = {}
|
||||
) {
|
||||
val downloadManager = context.getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager
|
||||
val downloadManager =
|
||||
context.getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager
|
||||
|
||||
val query = DownloadManager.Query()
|
||||
query.setFilterByStatus(DownloadManager.STATUS_RUNNING or DownloadManager.STATUS_PAUSED or DownloadManager.STATUS_PENDING)
|
||||
@@ -67,7 +67,7 @@ fun checkNewVersion(): LatestVersionInfo {
|
||||
// default null value if failed
|
||||
val defaultValue = LatestVersionInfo()
|
||||
runCatching {
|
||||
ksuApp.okhttpClient.newCall(okhttp3.Request.Builder().url(url).build()).execute()
|
||||
okhttp3.OkHttpClient().newCall(okhttp3.Request.Builder().url(url).build()).execute()
|
||||
.use { response ->
|
||||
if (!response.isSuccessful) {
|
||||
return defaultValue
|
||||
@@ -130,12 +130,18 @@ fun DownloadListener(context: Context, onDownloaded: (Uri) -> Unit) {
|
||||
}
|
||||
}
|
||||
}
|
||||
ContextCompat.registerReceiver(
|
||||
context,
|
||||
receiver,
|
||||
IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE),
|
||||
ContextCompat.RECEIVER_EXPORTED
|
||||
)
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||
context.registerReceiver(
|
||||
receiver,
|
||||
IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE),
|
||||
Context.RECEIVER_EXPORTED
|
||||
)
|
||||
} else {
|
||||
context.registerReceiver(
|
||||
receiver,
|
||||
IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE)
|
||||
)
|
||||
}
|
||||
onDispose {
|
||||
context.unregisterReceiver(receiver)
|
||||
}
|
||||
|
||||
@@ -4,15 +4,16 @@ import android.content.ContentResolver
|
||||
import android.content.Context
|
||||
import android.database.Cursor
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.os.Environment
|
||||
import android.os.Parcelable
|
||||
import android.os.SystemClock
|
||||
import android.provider.OpenableColumns
|
||||
import android.system.Os
|
||||
import android.util.Log
|
||||
import com.topjohnwu.superuser.CallbackList
|
||||
import com.topjohnwu.superuser.Shell
|
||||
import com.topjohnwu.superuser.ShellUtils
|
||||
import com.topjohnwu.superuser.io.SuFile
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import kotlinx.parcelize.Parcelize
|
||||
@@ -33,11 +34,6 @@ private fun getKsuDaemonPath(): String {
|
||||
return ksuApp.applicationInfo.nativeLibraryDir + File.separator + "libksud.so"
|
||||
}
|
||||
|
||||
data class FlashResult(val code: Int, val err: String, val showReboot: Boolean) {
|
||||
constructor(result: Shell.Result, showReboot: Boolean) : this(result.code, result.err.joinToString("\n"), showReboot)
|
||||
constructor(result: Shell.Result) : this(result, result.isSuccess)
|
||||
}
|
||||
|
||||
object KsuCli {
|
||||
val SHELL: Shell = createRootShell()
|
||||
val GLOBAL_MNT_SHELL: Shell = createRootShell(true)
|
||||
@@ -56,10 +52,10 @@ inline fun <T> withNewRootShell(
|
||||
return createRootShell(globalMnt).use(block)
|
||||
}
|
||||
|
||||
fun Uri.getFileName(context: Context): String? {
|
||||
fun getFileNameFromUri(context: Context, uri: Uri): String? {
|
||||
var fileName: String? = null
|
||||
val contentResolver: ContentResolver = context.contentResolver
|
||||
val cursor: Cursor? = contentResolver.query(this, null, null, null, null)
|
||||
val cursor: Cursor? = contentResolver.query(uri, null, null, null, null)
|
||||
cursor?.use {
|
||||
if (it.moveToFirst()) {
|
||||
fileName = it.getString(it.getColumnIndexOrThrow(OpenableColumns.DISPLAY_NAME))
|
||||
@@ -81,9 +77,9 @@ fun createRootShell(globalMnt: Boolean = false): Shell {
|
||||
Log.w(TAG, "ksu failed: ", e)
|
||||
try {
|
||||
if (globalMnt) {
|
||||
builder.build("su", "-mm")
|
||||
} else {
|
||||
builder.build("su")
|
||||
} else {
|
||||
builder.build("su", "-mm")
|
||||
}
|
||||
} catch (e: Throwable) {
|
||||
Log.e(TAG, "su failed: ", e)
|
||||
@@ -172,9 +168,10 @@ private fun flashWithIO(
|
||||
|
||||
fun flashModule(
|
||||
uri: Uri,
|
||||
onFinish: (Boolean, Int) -> Unit,
|
||||
onStdout: (String) -> Unit,
|
||||
onStderr: (String) -> Unit
|
||||
): FlashResult {
|
||||
): Boolean {
|
||||
val resolver = ksuApp.contentResolver
|
||||
with(resolver.openInputStream(uri)) {
|
||||
val file = File(ksuApp.cacheDir, "module.zip")
|
||||
@@ -187,48 +184,27 @@ fun flashModule(
|
||||
|
||||
file.delete()
|
||||
|
||||
return FlashResult(result)
|
||||
onFinish(result.isSuccess, result.code)
|
||||
return result.isSuccess
|
||||
}
|
||||
}
|
||||
|
||||
fun runModuleAction(
|
||||
moduleId: String, onStdout: (String) -> Unit, onStderr: (String) -> Unit
|
||||
): Boolean {
|
||||
val shell = createRootShell(true)
|
||||
|
||||
val stdoutCallback: CallbackList<String?> = object : CallbackList<String?>() {
|
||||
override fun onAddElement(s: String?) {
|
||||
onStdout(s ?: "")
|
||||
}
|
||||
}
|
||||
|
||||
val stderrCallback: CallbackList<String?> = object : CallbackList<String?>() {
|
||||
override fun onAddElement(s: String?) {
|
||||
onStderr(s ?: "")
|
||||
}
|
||||
}
|
||||
|
||||
val result = shell.newJob().add("${getKsuDaemonPath()} module action $moduleId")
|
||||
.to(stdoutCallback, stderrCallback).exec()
|
||||
Log.i("KernelSU", "Module runAction result: $result")
|
||||
|
||||
return result.isSuccess
|
||||
}
|
||||
|
||||
fun restoreBoot(
|
||||
onStdout: (String) -> Unit, onStderr: (String) -> Unit
|
||||
): FlashResult {
|
||||
onFinish: (Boolean, Int) -> Unit, onStdout: (String) -> Unit, onStderr: (String) -> Unit
|
||||
): Boolean {
|
||||
val magiskboot = File(ksuApp.applicationInfo.nativeLibraryDir, "libmagiskboot.so")
|
||||
val result = flashWithIO("${getKsuDaemonPath()} boot-restore -f --magiskboot $magiskboot", onStdout, onStderr)
|
||||
return FlashResult(result)
|
||||
onFinish(result.isSuccess, result.code)
|
||||
return result.isSuccess
|
||||
}
|
||||
|
||||
fun uninstallPermanently(
|
||||
onStdout: (String) -> Unit, onStderr: (String) -> Unit
|
||||
): FlashResult {
|
||||
onFinish: (Boolean, Int) -> Unit, onStdout: (String) -> Unit, onStderr: (String) -> Unit
|
||||
): Boolean {
|
||||
val magiskboot = File(ksuApp.applicationInfo.nativeLibraryDir, "libmagiskboot.so")
|
||||
val result = flashWithIO("${getKsuDaemonPath()} uninstall --magiskboot $magiskboot", onStdout, onStderr)
|
||||
return FlashResult(result)
|
||||
onFinish(result.isSuccess, result.code)
|
||||
return result.isSuccess
|
||||
}
|
||||
|
||||
suspend fun shrinkModules(): Boolean = withContext(Dispatchers.IO) {
|
||||
@@ -246,9 +222,10 @@ fun installBoot(
|
||||
bootUri: Uri?,
|
||||
lkm: LkmSelection,
|
||||
ota: Boolean,
|
||||
onFinish: (Boolean, Int) -> Unit,
|
||||
onStdout: (String) -> Unit,
|
||||
onStderr: (String) -> Unit,
|
||||
): FlashResult {
|
||||
): Boolean {
|
||||
val resolver = ksuApp.contentResolver
|
||||
|
||||
val bootFile = bootUri?.let { uri ->
|
||||
@@ -311,7 +288,8 @@ fun installBoot(
|
||||
lkmFile?.delete()
|
||||
|
||||
// if boot uri is empty, it is direct install, when success, we should show reboot button
|
||||
return FlashResult(result, bootUri == null && result.isSuccess)
|
||||
onFinish(bootUri == null && result.isSuccess, result.code)
|
||||
return result.isSuccess
|
||||
}
|
||||
|
||||
fun reboot(reason: String = "") {
|
||||
@@ -334,7 +312,18 @@ fun isAbDevice(): Boolean {
|
||||
}
|
||||
|
||||
fun isInitBoot(): Boolean {
|
||||
return !Os.uname().release.contains("android12-")
|
||||
val shell = getRootShell()
|
||||
if (shell.isRoot) {
|
||||
// if we have root, use /dev/block/by-name/init_boot to check
|
||||
val abDevice = isAbDevice()
|
||||
val initBootBlock = "/dev/block/by-name/init_boot${if (abDevice) "_a" else ""}"
|
||||
val file = SuFile(initBootBlock)
|
||||
file.shell = shell
|
||||
return file.exists()
|
||||
}
|
||||
// https://source.android.com/docs/core/architecture/partitions/generic-boot
|
||||
return ShellUtils.fastCmd(shell, "getprop ro.product.first_api_level").trim()
|
||||
.toInt() >= Build.VERSION_CODES.TIRAMISU
|
||||
}
|
||||
|
||||
suspend fun getCurrentKmi(): String = withContext(Dispatchers.IO) {
|
||||
|
||||
@@ -1,12 +1,16 @@
|
||||
package me.weishu.kernelsu.ui.util
|
||||
|
||||
import android.content.ContentResolver
|
||||
import android.content.Context
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.os.ParcelFileDescriptor
|
||||
import android.system.Os
|
||||
import com.topjohnwu.superuser.ShellUtils
|
||||
import me.weishu.kernelsu.Natives
|
||||
import me.weishu.kernelsu.ui.screen.getManagerVersion
|
||||
import java.io.File
|
||||
import java.io.FileOutputStream
|
||||
import java.io.FileWriter
|
||||
import java.io.PrintWriter
|
||||
import java.time.LocalDateTime
|
||||
@@ -24,7 +28,7 @@ fun getBugreportFile(context: Context): File {
|
||||
val pstoreFile = File(bugreportDir, "pstore.tar.gz")
|
||||
// Xiaomi/Readmi devices have diag in /data/vendor/diag
|
||||
val diagFile = File(bugreportDir, "diag.tar.gz")
|
||||
val oplusFile = File(bugreportDir, "oplus.tar.gz")
|
||||
val opulsFile = File(bugreportDir, "opuls.tar.gz")
|
||||
val bootlogFile = File(bugreportDir, "bootlog.tar.gz")
|
||||
val mountsFile = File(bugreportDir, "mounts.txt")
|
||||
val fileSystemsFile = File(bugreportDir, "filesystems.txt")
|
||||
@@ -46,7 +50,7 @@ fun getBugreportFile(context: Context): File {
|
||||
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 ${diagFile.absolutePath} -C /data/vendor/diag . --exclude=./minidump.gz").exec()
|
||||
shell.newJob().add("tar -czf ${oplusFile.absolutePath} -C /mnt/oplus/op2/media/log/boot_log/ .").exec()
|
||||
shell.newJob().add("tar -czf ${opulsFile.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("cat /proc/1/mountinfo > ${mountsFile.absolutePath}").exec()
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package me.weishu.kernelsu.ui.util
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.runtime.Composable
|
||||
import com.topjohnwu.superuser.Shell
|
||||
import me.weishu.kernelsu.R
|
||||
|
||||
|
||||
@@ -10,8 +10,6 @@ import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import me.weishu.kernelsu.ksuApp
|
||||
import me.weishu.kernelsu.ui.util.HanziToPinyin
|
||||
import me.weishu.kernelsu.ui.util.listModules
|
||||
import me.weishu.kernelsu.ui.util.overlayFsAvailable
|
||||
import org.json.JSONArray
|
||||
@@ -38,7 +36,6 @@ class ModuleViewModel : ViewModel() {
|
||||
val remove: Boolean,
|
||||
val updateJson: String,
|
||||
val hasWebUi: Boolean,
|
||||
val hasActionScript: Boolean,
|
||||
)
|
||||
|
||||
data class ModuleUpdateInfo(
|
||||
@@ -50,23 +47,13 @@ class ModuleViewModel : ViewModel() {
|
||||
|
||||
var isRefreshing by mutableStateOf(false)
|
||||
private set
|
||||
var search by mutableStateOf("")
|
||||
|
||||
var isOverlayAvailable by mutableStateOf(overlayFsAvailable())
|
||||
private set
|
||||
|
||||
var sortEnabledFirst by mutableStateOf(false)
|
||||
var sortActionFirst by mutableStateOf(false)
|
||||
val moduleList by derivedStateOf {
|
||||
val comparator =
|
||||
compareBy<ModuleInfo>(
|
||||
{ if (sortEnabledFirst) !it.enabled else 0 },
|
||||
{ if (sortActionFirst) !it.hasWebUi && !it.hasActionScript else 0 },
|
||||
).thenBy(Collator.getInstance(Locale.getDefault()), ModuleInfo::id)
|
||||
modules.filter {
|
||||
it.id.contains(search, true) || it.name.contains(search, true) || HanziToPinyin.getInstance()
|
||||
.toPinyinString(it.name).contains(search, true)
|
||||
}.sortedWith(comparator).also {
|
||||
val comparator = compareBy(Collator.getInstance(Locale.getDefault()), ModuleInfo::id)
|
||||
modules.sortedWith(comparator).also {
|
||||
isRefreshing = false
|
||||
}
|
||||
}
|
||||
@@ -100,6 +87,7 @@ class ModuleViewModel : ViewModel() {
|
||||
.map { obj ->
|
||||
ModuleInfo(
|
||||
obj.getString("id"),
|
||||
|
||||
obj.optString("name"),
|
||||
obj.optString("author", "Unknown"),
|
||||
obj.optString("version", "Unknown"),
|
||||
@@ -109,8 +97,7 @@ class ModuleViewModel : ViewModel() {
|
||||
obj.getBoolean("update"),
|
||||
obj.getBoolean("remove"),
|
||||
obj.optString("updateJson"),
|
||||
obj.optBoolean("web"),
|
||||
obj.optBoolean("action")
|
||||
obj.optBoolean("web")
|
||||
)
|
||||
}.toList()
|
||||
isNeedRefresh = false
|
||||
@@ -129,10 +116,6 @@ class ModuleViewModel : ViewModel() {
|
||||
}
|
||||
}
|
||||
|
||||
private fun sanitizeVersionString(version: String): String {
|
||||
return version.replace(Regex("[^a-zA-Z0-9.\\-_]"), "_")
|
||||
}
|
||||
|
||||
fun checkUpdate(m: ModuleInfo): Triple<String, String, String> {
|
||||
val empty = Triple("", "", "")
|
||||
if (m.updateJson.isEmpty() || m.remove || m.update || !m.enabled) {
|
||||
@@ -142,8 +125,11 @@ class ModuleViewModel : ViewModel() {
|
||||
val result = kotlin.runCatching {
|
||||
val url = m.updateJson
|
||||
Log.i(TAG, "checkUpdate url: $url")
|
||||
val response = ksuApp.okhttpClient.newCall(
|
||||
okhttp3.Request.Builder().url(url).build()
|
||||
val response = okhttp3.OkHttpClient()
|
||||
.newCall(
|
||||
okhttp3.Request.Builder()
|
||||
.url(url)
|
||||
.build()
|
||||
).execute()
|
||||
Log.d(TAG, "checkUpdate code: ${response.code}")
|
||||
if (response.isSuccessful) {
|
||||
@@ -162,8 +148,7 @@ class ModuleViewModel : ViewModel() {
|
||||
JSONObject(result)
|
||||
}.getOrNull() ?: return empty
|
||||
|
||||
var version = updateJson.optString("version", "")
|
||||
version = sanitizeVersionString(version)
|
||||
val version = updateJson.optString("version", "")
|
||||
val versionCode = updateJson.optInt("versionCode", 0)
|
||||
val zipUrl = updateJson.optString("zipUrl", "")
|
||||
val changelog = updateJson.optString("changelog", "")
|
||||
|
||||
@@ -11,17 +11,18 @@ import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import kotlinx.parcelize.Parcelize
|
||||
import me.weishu.kernelsu.Natives
|
||||
import me.weishu.kernelsu.ksuApp
|
||||
import me.weishu.kernelsu.profile.Capabilities
|
||||
import me.weishu.kernelsu.profile.Groups
|
||||
import me.weishu.kernelsu.ui.util.getAppProfileTemplate
|
||||
import me.weishu.kernelsu.ui.util.listAppProfileTemplates
|
||||
import me.weishu.kernelsu.ui.util.setAppProfileTemplate
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.Request
|
||||
import org.json.JSONArray
|
||||
import org.json.JSONObject
|
||||
import java.text.Collator
|
||||
import java.util.Locale
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
|
||||
/**
|
||||
@@ -137,7 +138,13 @@ class TemplateViewModel : ViewModel() {
|
||||
|
||||
private fun fetchRemoteTemplates() {
|
||||
runCatching {
|
||||
ksuApp.okhttpClient.newCall(
|
||||
val client: OkHttpClient = OkHttpClient.Builder()
|
||||
.connectTimeout(5, TimeUnit.SECONDS)
|
||||
.writeTimeout(5, TimeUnit.SECONDS)
|
||||
.readTimeout(10, TimeUnit.SECONDS)
|
||||
.build()
|
||||
|
||||
client.newCall(
|
||||
Request.Builder().url(TEMPLATE_INDEX_URL).build()
|
||||
).execute().use { response ->
|
||||
if (!response.isSuccessful) {
|
||||
@@ -148,7 +155,7 @@ private fun fetchRemoteTemplates() {
|
||||
0.until(remoteTemplateIds.length()).forEach { i ->
|
||||
val id = remoteTemplateIds.getString(i)
|
||||
Log.i(TAG, "fetch template: $id")
|
||||
val templateJson = ksuApp.okhttpClient.newCall(
|
||||
val templateJson = client.newCall(
|
||||
Request.Builder().url(TEMPLATE_URL.format(id)).build()
|
||||
).runCatching {
|
||||
execute().use { response ->
|
||||
|
||||
@@ -2,18 +2,13 @@ package me.weishu.kernelsu.ui.webui
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.ActivityManager
|
||||
import android.os.Build
|
||||
import android.content.Context
|
||||
import android.os.Bundle
|
||||
import android.view.ViewGroup.MarginLayoutParams
|
||||
import android.webkit.WebResourceRequest
|
||||
import android.webkit.WebResourceResponse
|
||||
import android.webkit.WebView
|
||||
import android.webkit.WebViewClient
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
import androidx.core.view.ViewCompat
|
||||
import androidx.core.view.WindowInsetsCompat
|
||||
import androidx.core.view.updateLayoutParams
|
||||
import androidx.webkit.WebViewAssetLoader
|
||||
import com.topjohnwu.superuser.Shell
|
||||
import me.weishu.kernelsu.ui.util.createRootShell
|
||||
@@ -26,30 +21,15 @@ class WebUIActivity : ComponentActivity() {
|
||||
private var rootShell: Shell? = null
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
|
||||
// Enable edge to edge
|
||||
enableEdgeToEdge()
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||
window.isNavigationBarContrastEnforced = false
|
||||
}
|
||||
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
val moduleId = intent.getStringExtra("id")!!
|
||||
val name = intent.getStringExtra("name")!!
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
|
||||
@Suppress("DEPRECATION")
|
||||
setTaskDescription(ActivityManager.TaskDescription("KernelSU - $name"))
|
||||
} else {
|
||||
val taskDescription = ActivityManager.TaskDescription.Builder().setLabel("KernelSU - $name").build()
|
||||
setTaskDescription(taskDescription)
|
||||
}
|
||||
setTaskDescription(ActivityManager.TaskDescription("KernelSU - $name"))
|
||||
|
||||
val prefs = getSharedPreferences("settings", MODE_PRIVATE)
|
||||
val prefs = getSharedPreferences("settings", Context.MODE_PRIVATE)
|
||||
WebView.setWebContentsDebuggingEnabled(prefs.getBoolean("enable_web_debugging", false))
|
||||
|
||||
val moduleDir = "/data/adb/modules/${moduleId}"
|
||||
val webRoot = File("${moduleDir}/webroot")
|
||||
val webRoot = File("/data/adb/modules/${moduleId}/webroot")
|
||||
val rootShell = createRootShell(true).also { this.rootShell = it }
|
||||
val webViewAssetLoader = WebViewAssetLoader.Builder()
|
||||
.setDomain("mui.kernelsu.org")
|
||||
@@ -69,20 +49,10 @@ class WebUIActivity : ComponentActivity() {
|
||||
}
|
||||
|
||||
val webView = WebView(this).apply {
|
||||
ViewCompat.setOnApplyWindowInsetsListener(this) { view, insets ->
|
||||
val inset = insets.getInsets(WindowInsetsCompat.Type.systemBars())
|
||||
view.updateLayoutParams<MarginLayoutParams> {
|
||||
leftMargin = inset.left
|
||||
rightMargin = inset.right
|
||||
topMargin = inset.top
|
||||
bottomMargin = inset.bottom
|
||||
}
|
||||
return@setOnApplyWindowInsetsListener insets
|
||||
}
|
||||
settings.javaScriptEnabled = true
|
||||
settings.domStorageEnabled = true
|
||||
settings.allowFileAccess = false
|
||||
webviewInterface = WebViewInterface(this@WebUIActivity, this, moduleDir)
|
||||
webviewInterface = WebViewInterface(this@WebUIActivity, this)
|
||||
addJavascriptInterface(webviewInterface, "ksu")
|
||||
setWebViewClient(webViewClient)
|
||||
loadUrl("https://mui.kernelsu.org/index.html")
|
||||
|
||||
@@ -9,24 +9,18 @@ import android.view.Window
|
||||
import android.webkit.JavascriptInterface
|
||||
import android.webkit.WebView
|
||||
import android.widget.Toast
|
||||
import androidx.core.view.WindowCompat
|
||||
import androidx.core.view.WindowInsetsCompat
|
||||
import androidx.core.view.WindowInsetsControllerCompat
|
||||
import com.topjohnwu.superuser.CallbackList
|
||||
import com.topjohnwu.superuser.ShellUtils
|
||||
import com.topjohnwu.superuser.internal.UiThreadHandler
|
||||
import me.weishu.kernelsu.ui.util.createRootShell
|
||||
import me.weishu.kernelsu.ui.util.listModules
|
||||
import me.weishu.kernelsu.ui.util.withNewRootShell
|
||||
import org.json.JSONArray
|
||||
import org.json.JSONObject
|
||||
import java.io.File
|
||||
import java.util.concurrent.CompletableFuture
|
||||
|
||||
class WebViewInterface(
|
||||
val context: Context,
|
||||
private val webView: WebView,
|
||||
private val modDir: String
|
||||
) {
|
||||
class WebViewInterface(val context: Context, private val webView: WebView) {
|
||||
|
||||
@JavascriptInterface
|
||||
fun exec(cmd: String): String {
|
||||
@@ -114,13 +108,13 @@ class WebViewInterface(
|
||||
}
|
||||
}
|
||||
|
||||
val stdout = object : CallbackList<String>(UiThreadHandler::runAndWait) {
|
||||
val stdout = object : CallbackList<String>() {
|
||||
override fun onAddElement(s: String) {
|
||||
emitData("stdout", s)
|
||||
}
|
||||
}
|
||||
|
||||
val stderr = object : CallbackList<String>(UiThreadHandler::runAndWait) {
|
||||
val stderr = object : CallbackList<String>() {
|
||||
override fun onAddElement(s: String) {
|
||||
emitData("stderr", s)
|
||||
}
|
||||
@@ -176,34 +170,21 @@ class WebViewInterface(
|
||||
}
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
fun moduleInfo(): String {
|
||||
val moduleInfos = JSONArray(listModules())
|
||||
var currentModuleInfo = JSONObject()
|
||||
currentModuleInfo.put("moduleDir", modDir)
|
||||
val moduleId = File(modDir).getName()
|
||||
for (i in 0 until moduleInfos.length()) {
|
||||
val currentInfo = moduleInfos.getJSONObject(i)
|
||||
}
|
||||
|
||||
if (currentInfo.getString("id") != moduleId) {
|
||||
continue
|
||||
}
|
||||
|
||||
var keys = currentInfo.keys()
|
||||
for (key in keys) {
|
||||
currentModuleInfo.put(key, currentInfo.get(key))
|
||||
}
|
||||
break
|
||||
}
|
||||
return currentModuleInfo.toString()
|
||||
fun hideSystemUI(window: Window) {
|
||||
WindowCompat.setDecorFitsSystemWindows(window, false)
|
||||
WindowInsetsControllerCompat(window, window.decorView).let { controller ->
|
||||
controller.hide(WindowInsetsCompat.Type.systemBars())
|
||||
controller.systemBarsBehavior =
|
||||
WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
|
||||
}
|
||||
}
|
||||
|
||||
fun hideSystemUI(window: Window) =
|
||||
WindowInsetsControllerCompat(window, window.decorView).let { controller ->
|
||||
controller.hide(WindowInsetsCompat.Type.systemBars())
|
||||
controller.systemBarsBehavior = WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
|
||||
}
|
||||
|
||||
fun showSystemUI(window: Window) =
|
||||
WindowInsetsControllerCompat(window, window.decorView).show(WindowInsetsCompat.Type.systemBars())
|
||||
fun showSystemUI(window: Window) {
|
||||
WindowCompat.setDecorFitsSystemWindows(window, true)
|
||||
WindowInsetsControllerCompat(
|
||||
window,
|
||||
window.decorView
|
||||
).show(WindowInsetsCompat.Type.systemBars())
|
||||
}
|
||||
Binary file not shown.
15
manager/app/src/main/res/drawable/ic_launcher_background.xml
Normal file
15
manager/app/src/main/res/drawable/ic_launcher_background.xml
Normal file
@@ -0,0 +1,15 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:viewportWidth="108"
|
||||
android:viewportHeight="108">
|
||||
|
||||
<group
|
||||
android:scaleX="0.135"
|
||||
android:scaleY="0.135">
|
||||
<path
|
||||
android:fillColor="#ffffff"
|
||||
android:pathData="M 0 0 H 800 V 800 H 0 V 0 Z" />
|
||||
</group>
|
||||
</vector>
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@color/ic_launcher_background" />
|
||||
<background android:drawable="@drawable/ic_launcher_background" />
|
||||
<foreground android:drawable="@drawable/ic_launcher_foreground" />
|
||||
<monochrome android:drawable="@drawable/ic_launcher_monochrome" />
|
||||
</adaptive-icon>
|
||||
@@ -1 +0,0 @@
|
||||
unqualifiedResLocale=en-US
|
||||
@@ -18,7 +18,7 @@
|
||||
<string name="selinux_status_permissive">متساهل</string>
|
||||
<string name="selinux_status_unknown">مجهول</string>
|
||||
<string name="superuser">مستخدم خارق</string>
|
||||
<string name="module_failed_to_enable">فشل في تمكين الوحدة %s</string>
|
||||
<string name="module_failed_to_enable">فشل في تمكين الإضافة: %s</string>
|
||||
<string name="module_failed_to_disable">فشل تعطيل الإضافة : %s</string>
|
||||
<string name="module_empty">لا توجد إضافات مثبتة</string>
|
||||
<string name="module">الإضافات</string>
|
||||
@@ -35,17 +35,17 @@
|
||||
<string name="about">من نحن</string>
|
||||
<string name="module_uninstall_confirm">هل أنت متأكد أنك تريد إلغاء تثبيت الإضافة %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_author">المطور</string>
|
||||
<string name="module_overlay_fs_not_available">الوحدات غير متوفرة حيث يتم تعطيل نظام الملفات المتراكب بواسطة النواة!</string>
|
||||
<string name="module_overlay_fs_not_available">التراكبات غير متوفرة ، لا يمكن للإضافة أن تعمل!</string>
|
||||
<string name="refresh">إنعاش</string>
|
||||
<string name="show_system_apps">إظهار تطبيقات النظام</string>
|
||||
<string name="hide_system_apps">إخفاء تطبيقات النظام</string>
|
||||
<string name="send_log">إرسال السجلات</string>
|
||||
<string name="safe_mode">الوضع الآمن</string>
|
||||
<string name="reboot_to_apply">إعادة التشغيل لتطبيق التغييرات</string>
|
||||
<string name="module_magisk_conflict">الوحدات غير متاحة بسبب تعارضها مع Magisk!</string>
|
||||
<string name="module_magisk_conflict">تم تعطيل الإضافات لأنها تتعارض مع Magisk!</string>
|
||||
<string name="home_learn_kernelsu">تعلم KernelSU</string>
|
||||
<string name="home_learn_kernelsu_url">https://kernelsu.org/guide/what-is-kernelsu.html</string>
|
||||
<string name="home_click_to_learn_kernelsu">تعرف على كيفية تثبيت KernelSU واستخدام الإضافات</string>
|
||||
@@ -56,7 +56,7 @@
|
||||
<string name="module_update">تحديث</string>
|
||||
<string name="module_downloading">تحميل الإضافة: %s</string>
|
||||
<string name="module_start_downloading">ابدأ التنزيل: %s</string>
|
||||
<string name="new_version_available">الإصدار الجديد: %s متاح ، انقر للتحديث.</string>
|
||||
<string name="new_version_available">الإصدار الجديد: %s متاح ، انقر للتحديث</string>
|
||||
<string name="launch_app">تشغيل</string>
|
||||
<string name="profile_default">الإفتراضي</string>
|
||||
<string name="profile_template">نموذج</string>
|
||||
@@ -76,7 +76,7 @@
|
||||
<string name="profile_selinux_domain">المجال</string>
|
||||
<string name="profile_selinux_rules">القواعد</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_name">اسم الملف الشخصي</string>
|
||||
<string name="require_kernel_version">إصدار KernelSU الحالي %d منخفض جدًا بحيث لا يعمل المدير بشكل صحيح. الرجاء الترقية إلى الإصدار %d أو أعلى!</string>
|
||||
<string name="module_changelog">سجل التغييرات</string>
|
||||
@@ -118,26 +118,17 @@
|
||||
<string name="select_kmi">اختر KMI</string>
|
||||
<string name="select_file_tip">يوصى باستخدام صورة القسم %1$s</string>
|
||||
<string name="shrink_sparse_image">تصغير الصورة المتفرقة</string>
|
||||
<string name="shrink_sparse_image_message">قم بتغيير حجم الصورة المتفرقة حيث توجد الإضافة إلى حجمها الفعلي. لاحظ أن هذا قد يتسبب في عمل الإضافة بشكل غير طبيعي، لذا يرجى استخدامها فقط عند الضرورة (مثل النسخ الاحتياطي).</string>
|
||||
<string name="shrink_sparse_image_message">قم بتغيير حجم الصورة المتفرقة حيث توجد الإضافة إلى حجمها الفعلي. لاحظ أن هذا قد يتسبب في عمل الإضافة بشكل غير طبيعي، لذا يرجى استخدامها فقط عند الضرورة (مثل النسخ الاحتياطي)</string>
|
||||
<string name="settings_uninstall">إلغاء التثبيت</string>
|
||||
<string name="settings_uninstall_temporary">إلغاء التثبيت مؤقتًا</string>
|
||||
<string name="settings_uninstall_permanent">إلغاء التثبيت بشكل دائم</string>
|
||||
<string name="settings_restore_stock_image">استعادة الصورة الاصلية</string>
|
||||
<string name="settings_uninstall_permanent_message">إلغاء تثبيت KernelSU .(الجذر وجميع الوحدات) بشكل كامل ودائم.</string>
|
||||
<string name="settings_uninstall_permanent_message">إلغاء تثبيت KernelSU (الجذر وجميع الوحدات) بشكل كامل ودائم.</string>
|
||||
<string name="flashing">تركيب</string>
|
||||
<string name="flash_success">نجح التركيب</string>
|
||||
<string name="flash_failed">فشل التركيب</string>
|
||||
<string name="selected_lkm">LKM المحددة: %s</string>
|
||||
<string name="selected_lkm">صورة lkm المحددة: %s</string>
|
||||
<string name="settings_restore_stock_image_message">استعادة صورة المصنع المخزنة (في حالة وجود نسخة احتياطية)، والتي تُستخدم عادة قبل OTA؛ إذا كنت بحاجة إلى إلغاء تثبيت KernelSU، فيرجى استخدام \"إلغاء التثبيت الدائم\".</string>
|
||||
<string name="settings_uninstall_temporary_message">قم بإلغاء تثبيت KernelSU مؤقتًا، واستعد إلى حالته الأصلية بعد إعادة التشغيل التالية.</string>
|
||||
<string name="save_log">حفظ السجلات</string>
|
||||
<string name="action">إجراء</string>
|
||||
<string name="log_saved">السجلات محفوظة</string>
|
||||
<string name="module_sort_enabled_first">فرز (الممكن أولاً)</string>
|
||||
<string name="module_sort_action_first">فرز (الإجراء أولاً)</string>
|
||||
<string name="settings_disable_su">تعطيل توافق su</string>
|
||||
<string name="settings_disable_su_summary">قم بتعطيل قدره التطبيقات مؤقتا من الحصول على امتيازات المسخدم الخارق عبر الأمر su (لن تتأثر عمليات الجذر الحالية).</string>
|
||||
<string name="module_install_prompt_with_name">الحزم الاتيه سيتم تثبيتها %1$s</string>
|
||||
<string name="confirm">تأكيد</string>
|
||||
<string name="su_not_allowed">من الغير ممكن اعطاء صلاحيات (المسخدم الخارق) لـ %s</string>
|
||||
</resources>
|
||||
</resources>
|
||||
@@ -76,12 +76,7 @@
|
||||
<string name="profile_umount_modules_summary">Bu seçimi aktivləşdirmək KernelSU-ya bu proqram üçün modullar tərəfindən hər hansı dəyişdirilmiş faylları bərpa etməyə imkan verəcək.</string>
|
||||
<string name="launch_app">Aç</string>
|
||||
<string name="force_stop_app">Məcburi dayandır</string>
|
||||
<string name="restart_app">Təkrar başlat</string>
|
||||
<string name="restart_app">Yenidən başlat</string>
|
||||
<string name="failed_to_update_sepolicy">%s görə SELinux qaydalarını güncəlləmək mümkün olmadı</string>
|
||||
<string name="save_log">Girişləri Saxla</string>
|
||||
<string name="require_kernel_version">Cari KernelSU versiyası %d menecerin düzgün işləməsi üçün çox aşağıdır. Lütfən, %d və ya daha yüksək versiyaya təkmilləşdirin!</string>
|
||||
<string name="module_install_prompt_with_name">Aşağıdakı modullar quraşdırılacaq: %1$s</string>
|
||||
<string name="confirm">Təsdiq edin</string>
|
||||
<string name="module_sort_action_first">Sıralama (ilk hərəkət)</string>
|
||||
<string name="module_sort_enabled_first">Sıralama (Əvvəlcə aktivdir)</string>
|
||||
</resources>
|
||||
</resources>
|
||||
@@ -49,4 +49,4 @@
|
||||
<string name="module_version">ভার্সন</string>
|
||||
<string name="module_author">অথার</string>
|
||||
<string name="save_log">লগ সংরক্ষণ করুন</string>
|
||||
</resources>
|
||||
</resources>
|
||||
@@ -65,4 +65,4 @@
|
||||
<string name="profile_umount_modules">আনমাউন্ট মোডিউল</string>
|
||||
<string name="require_kernel_version">ম্যানেজার সঠিকভাবে কাজ করার জন্য বর্তমান KernelSU সংস্করণ %d খুবই কম। অনুগ্রহ করে %d বা উচ্চতর সংস্করণে আপগ্রেড করুন!</string>
|
||||
<string name="save_log">লগ সংরক্ষণ করুন</string>
|
||||
</resources>
|
||||
</resources>
|
||||
@@ -80,4 +80,4 @@
|
||||
<string name="profile_custom">Prilagođeno</string>
|
||||
<string name="profile_name">Naziv profila</string>
|
||||
<string name="save_log">Sačuvaj Dnevnike</string>
|
||||
</resources>
|
||||
</resources>
|
||||
@@ -80,4 +80,4 @@
|
||||
<string name="restart_app">Genstart</string>
|
||||
<string name="require_kernel_version">Den nuværende KernelSU version %d er for lav til manageren for at fungere ordentligt. Opgrader til version %d eller højere!</string>
|
||||
<string name="save_log">Gem Logfiler</string>
|
||||
</resources>
|
||||
</resources>
|
||||
@@ -5,134 +5,121 @@
|
||||
<string name="selinux_status_permissive">Permissiv</string>
|
||||
<string name="home_working">Funktioniert</string>
|
||||
<string name="home_working_version">Version: %d</string>
|
||||
<string name="superuser">Superuser</string>
|
||||
<string name="home_click_to_install">Tippe zum Installieren</string>
|
||||
<string name="superuser">SuperUser</string>
|
||||
<string name="home_click_to_install">Tippen zum Installieren</string>
|
||||
<string name="home_superuser_count">Superuser: %d</string>
|
||||
<string name="selinux_status_unknown">Unbekannt</string>
|
||||
<string name="selinux_status_enforcing">Erzwingen</string>
|
||||
<string name="reboot_bootloader">In den Bootloader-Modus neustarten</string>
|
||||
<string name="reboot_download">In den Download-Modus neustarten</string>
|
||||
<string name="reboot_edl">In den EDL-Modus neustarten</string>
|
||||
<string name="reboot_bootloader">Neustart in Bootloader</string>
|
||||
<string name="reboot_download">Neustart in Download-Modus</string>
|
||||
<string name="reboot_edl">Neustart mit EDL-Modus</string>
|
||||
<string name="module_author">Autor</string>
|
||||
<string name="module_overlay_fs_not_available">Module sind nicht verfügbar, da OverlayFS vom Kernel deaktiviert ist.</string>
|
||||
<string name="about">Über KernelSU</string>
|
||||
<string name="module_magisk_conflict">Module sind aufgrund eines Konfliktes mit Magisk nicht verfügbar!</string>
|
||||
<string name="module_overlay_fs_not_available">overlayfs nicht verfügbar, Modul kann nicht funktionieren!</string>
|
||||
<string name="about">Über</string>
|
||||
<string name="module_magisk_conflict">Module sind deaktiviert, weil es einen Konflikt mit Magisk gibt!</string>
|
||||
<string name="home_learn_kernelsu_url">https://kernelsu.org/guide/what-is-kernelsu.html</string>
|
||||
<string name="home_click_to_learn_kernelsu">Erfahre, wie KernelSU installiert wird und wie Module verwendet werden</string>
|
||||
<string name="home_click_to_learn_kernelsu">Erfahren, wie KernelSU installiert und Module verwendet werden</string>
|
||||
<string name="home_support_title">Unterstütze uns</string>
|
||||
<string name="home_support_content">KernelSU ist und wird immer frei und quelloffen sein. Du kannst uns jedoch deine Unterstützung zeigen, indem du eine Spende tätigst.</string>
|
||||
<string name="profile_selinux_context">SELinux-Kontext</string>
|
||||
<string name="settings_umount_modules_default">Module standardmäßig aushängen</string>
|
||||
<string name="settings_umount_modules_default_summary">Globaler Standardwert für \"Module aushängen\" im App-Profil. Falls er aktiviert ist, werden alle Moduländerungen im System für alle Apps entfernt, für die kein Profil festgelegt ist.</string>
|
||||
<string name="settings_umount_modules_default_summary">Globaler Standardwert für \'Module aushängen\' in App-Profilen. Falls aktiviert, werden alle Moduländerungen im System für alle Apps entfernt, für die kein Profil festgelegt ist.</string>
|
||||
<string name="profile_default">Standard</string>
|
||||
<string name="profile_template">Vorlage</string>
|
||||
<string name="profile_custom">Benutzerdefiniert</string>
|
||||
<string name="failed_to_update_app_profile">App-Profilaktualisierung für %s fehlgeschlagen</string>
|
||||
<string name="profile_namespace_inherited">Geerbt</string>
|
||||
<string name="profile_namespace_inherited">Vererbt</string>
|
||||
<string name="profile_namespace_global">Global</string>
|
||||
<string name="profile_namespace_individual">Individuell</string>
|
||||
<string name="profile_selinux_domain">Domäne</string>
|
||||
<string name="module_update">Aktualisieren</string>
|
||||
<string name="profile_umount_modules_summary">Wenn du diese Option aktivierst, kann KernelSU alle von den Modulen für diese App geänderten Dateien wiederherstellen.</string>
|
||||
<string name="profile_selinux_rules">Regeln</string>
|
||||
<string name="module_start_downloading">Starte Download: %s</string>
|
||||
<string name="failed_to_update_sepolicy">Aktualisieren der SELinux-Regeln schlug fehl für: %s</string>
|
||||
<string name="module_start_downloading">Herunterladen startet: %s</string>
|
||||
<string name="failed_to_update_sepolicy">Fehler beim Aktualisieren der SELinux-Regeln für: %s</string>
|
||||
<string name="launch_app">Starten</string>
|
||||
<string name="new_version_available">Neue Version %s verfügbar, tippen zum Aktualisieren.</string>
|
||||
<string name="new_version_available">Neue Version: %s verfügbar, tippen zum Aktualisieren</string>
|
||||
<string name="force_stop_app">Stopp erzwingen</string>
|
||||
<string name="restart_app">Neustarten</string>
|
||||
<string name="restart_app">Neustart</string>
|
||||
<string name="home_module_count">Module: %d</string>
|
||||
<string name="home_manager_version">Manager-Version</string>
|
||||
<string name="home_selinux_status">SELinux Status</string>
|
||||
<string name="home_selinux_status">SELinux-Status</string>
|
||||
<string name="selinux_status_disabled">Deaktiviert</string>
|
||||
<string name="module_failed_to_enable">Modulaktivierung fehlgeschlagen: %s</string>
|
||||
<string name="module_failed_to_disable">Moduldeaktivierung fehlgeschlagen: %s</string>
|
||||
<string name="module_empty">Keine Modul installiert</string>
|
||||
<string name="module_empty">Keine Module installiert</string>
|
||||
<string name="module">Modul</string>
|
||||
<string name="uninstall">Deinstallieren</string>
|
||||
<string name="install">Installieren</string>
|
||||
<string name="reboot">Neustarten</string>
|
||||
<string name="settings">Einstellungen</string>
|
||||
<string name="reboot_recovery">In den Recovery-Modus neustarten</string>
|
||||
<string name="reboot_recovery">Neustart in Recovery</string>
|
||||
<string name="module_uninstall_success">%s deinstalliert</string>
|
||||
<string name="module_version">Version</string>
|
||||
<string name="refresh">Aktualisieren</string>
|
||||
<string name="refresh">Neu laden</string>
|
||||
<string name="show_system_apps">System-Apps anzeigen</string>
|
||||
<string name="hide_system_apps">System-Apps ausblenden</string>
|
||||
<string name="send_log">Protokoll senden</string>
|
||||
<string name="home_learn_kernelsu">KernelSU verstehen</string>
|
||||
<string name="safe_mode">Sicherer Modus</string>
|
||||
<string name="reboot_to_apply">Neustarten, damit Änderungen wirksam werden</string>
|
||||
<string name="about_source_code"><![CDATA[Quellcode einsehen unter %1$s<br/>Unserem %2$s-Kanal beitreten]]></string>
|
||||
<string name="about_source_code">Quellcode unter %1$s ansehen<br/>Unserem %2$s-Kanal beitreten</string>
|
||||
<string name="profile_name">Profilname</string>
|
||||
<string name="profile_namespace">Namespace einhängen</string>
|
||||
<string name="profile_groups">Gruppen</string>
|
||||
<string name="profile_capabilities">Fähigkeiten</string>
|
||||
<string name="profile_umount_modules">Module aushängen</string>
|
||||
<string name="module_downloading">Lädt Modul %s herunter</string>
|
||||
<string name="module_downloading">Modul herunterladen: %s</string>
|
||||
<string name="home_unsupported">Nicht unterstützt</string>
|
||||
<string name="home_unsupported_reason">KernelSU unterstützt derzeit nur GKI-Kernel</string>
|
||||
<string name="home_kernel">Kernel</string>
|
||||
<string name="home_fingerprint">Fingerabdruck</string>
|
||||
<string name="module_install">Installieren</string>
|
||||
<string name="reboot_userspace">Soft-Reboot</string>
|
||||
<string name="module_uninstall_confirm">Möchtest du wirklich Modul %s deinstallieren?</string>
|
||||
<string name="module_uninstall_confirm">Sicher, dass du das Modul %s deinstallieren möchtest\?</string>
|
||||
<string name="module_uninstall_failed">Deinstallation fehlgeschlagen: %s</string>
|
||||
<string name="require_kernel_version">Die aktuelle KernelSU-Version %d ist zu alt für diese Manager-Version. Bitte auf Version %d oder höher aktualisieren!</string>
|
||||
<string name="require_kernel_version">Die aktuelle Kernel-Version %d ist zu alt für diese Manager-Version. Bitte auf Version %d oder höher upgraden!</string>
|
||||
<string name="module_changelog">Änderungsprotokoll</string>
|
||||
<string name="app_profile_template_import_success">Erfolgreich importiert</string>
|
||||
<string name="app_profile_export_to_clipboard">In Zwischenablage exportieren</string>
|
||||
<string name="app_profile_template_export_empty">Kann lokale Vorlage nicht finden!</string>
|
||||
<string name="app_profile_template_id_exist">Vorlagen-ID existiert bereits!</string>
|
||||
<string name="app_profile_import_from_clipboard">Aus Zwischenablage importieren</string>
|
||||
<string name="module_changelog_failed">Konnte Veränderungs-Protokoll nicht laden: %s</string>
|
||||
<string name="module_changelog_failed">Konnte Changelog nicht laden: %s</string>
|
||||
<string name="app_profile_template_name">Name</string>
|
||||
<string name="app_profile_template_id_invalid">Ungültige Vorlagen-ID</string>
|
||||
<string name="app_profile_template_sync">Online-Vorlagen synchronisieren</string>
|
||||
<string name="app_profile_template_create">Vorlage erstellen</string>
|
||||
<string name="app_profile_template_readonly">Schreibgeschützt</string>
|
||||
<string name="app_profile_import_export">Import/Export</string>
|
||||
<string name="app_profile_template_save_failed">Schlug beim Speichern der Vorlage fehl</string>
|
||||
<string name="app_profile_template_save_failed">Fehler beim Speichern</string>
|
||||
<string name="app_profile_template_edit">Vorlage bearbeiten</string>
|
||||
<string name="app_profile_template_id">ID</string>
|
||||
<string name="settings_profile_template">App-Profil-Vorlage</string>
|
||||
<string name="settings_profile_template">App-Profil-Template</string>
|
||||
<string name="app_profile_template_description">Beschreibung</string>
|
||||
<string name="app_profile_template_save">Speichern</string>
|
||||
<string name="settings_profile_template_summary">Verwalte die lokale und online Vorlage des App-Profils</string>
|
||||
<string name="settings_profile_template_summary">verwalte lokale und online Profil Vorlagen</string>
|
||||
<string name="app_profile_template_delete">Löschen</string>
|
||||
<string name="app_profile_template_import_empty">Zwischenablage ist leer!</string>
|
||||
<string name="app_profile_template_view">Vorlage ansehen</string>
|
||||
<string name="enable_web_debugging">WebView-Debugging aktivieren</string>
|
||||
<string name="enable_web_debugging_summary">Kann zum Fehlerbeheben der WebUI verwendet werden, bitte nur im Notfall aktivieren.</string>
|
||||
<string name="enable_web_debugging_summary">Kann verwendet werden zum Debugging von WebUI, bitte nur falls nötig aktivieren.</string>
|
||||
<string name="select_file_tip">%1$s Partitionsabbild empfohlen</string>
|
||||
<string name="select_kmi">KMI auswählen</string>
|
||||
<string name="install_next">Weiter</string>
|
||||
<string name="direct_install">Direkte Installation (empfohlen)</string>
|
||||
<string name="select_file">Datei auswählen</string>
|
||||
<string name="install_inactive_slot">In inaktiven Slot installieren (nach OTA)</string>
|
||||
<string name="install_inactive_slot_warning">Nach einem Neustart wird dein Gerät **GEZWUNGEN** in den derzeit inaktiven Slot zu starten!
|
||||
<string name="install_inactive_slot_warning">Nach einem Neustart wird dein Gerät **GEZWUNGEN** in den derzeit inaktiven Slot zu booten.
|
||||
\nBenutze dies nur nach Fertigstellung des OTA.
|
||||
\nFortfahren?</string>
|
||||
<string name="grant_root_failed">Root-Zugriff konnte nicht gewährt werden!</string>
|
||||
<string name="open">Öffnen</string>
|
||||
<string name="settings_check_update">Auf Aktualisierung prüfen</string>
|
||||
<string name="settings_check_update_summary">Prüfe automatisch auf Aktualisierungen, wenn die App geöffnet wird</string>
|
||||
<string name="settings_check_update">Updates suchen</string>
|
||||
<string name="settings_check_update_summary">Automatisch nach Updates suchen beim Öffnen der App</string>
|
||||
<string name="settings_uninstall_temporary">Temporär deinstallieren</string>
|
||||
<string name="settings_uninstall">Deinstallieren</string>
|
||||
<string name="settings_uninstall_permanent_message">KernelSU (Root und alle Module) vollständig und dauerhaft deinstallieren.</string>
|
||||
<string name="shrink_sparse_image_message">Ändert die Größe des Sparse-Images, in dem sich das Modul befindet, auf seine tatsächliche Größe. Beachten Sie, dass dies dazu führen kann, dass das Modul nicht ordnungsgemäß funktioniert; verwenden Sie es daher nur, wenn es notwendig ist (Wie für ein Backup).</string>
|
||||
<string name="shrink_sparse_image_message">Ändert die Größe des Sparse-Images, in dem sich das Modul befindet, auf seine tatsächliche Größe. Beachten Sie, dass dies dazu führen kann, dass das Modul nicht ordnungsgemäß funktioniert; verwenden Sie es daher nur, wenn es notwendig ist (z. B. für ein Backup).</string>
|
||||
<string name="save_log">Protokolle Speichern</string>
|
||||
<string name="settings_uninstall_permanent">Permanent deinstallieren</string>
|
||||
<string name="settings_restore_stock_image">Standard-Abbild wiederherstellen</string>
|
||||
<string name="settings_uninstall_temporary_message">KernelSU temporär deinstallieren, originalen Status nach dem nächsten Neustart wiederherstellen.</string>
|
||||
<string name="settings_restore_stock_image_message">Das Standard Werksabbild wiederherstellen (falls ein Backup existiert), normalerweise vor einem OTA zu verwenden; falls Sie KernelSU deinstallieren müssen, nutzen Sie bitte \"Permanent deinstallieren\".</string>
|
||||
<string name="flashing">Schreibt</string>
|
||||
<string name="flash_success">Schreiben erfolgreich</string>
|
||||
<string name="flash_failed">Schreiben fehlgeschlagen</string>
|
||||
<string name="selected_lkm">Wähle LKM: %s</string>
|
||||
<string name="shrink_sparse_image">Spärliches Bild minimieren</string>
|
||||
<string name="action">Aktion</string>
|
||||
<string name="log_saved">Protokolle gespeichert</string>
|
||||
<string name="module_install_prompt_with_name">Folgende Module werden installiert: %1$s</string>
|
||||
<string name="confirm">Bestätigen</string>
|
||||
</resources>
|
||||
</resources>
|
||||
@@ -2,57 +2,57 @@
|
||||
<resources>
|
||||
<string name="home">Inicio</string>
|
||||
<string name="home_not_installed">No instalado</string>
|
||||
<string name="home_click_to_install">Haz clic para instalar</string>
|
||||
<string name="home_click_to_install">Toca para instalar</string>
|
||||
<string name="home_working">Funcionando</string>
|
||||
<string name="home_working_version">Versión: %d</string>
|
||||
<string name="home_superuser_count">Superusuarios: %d</string>
|
||||
<string name="home_module_count">Módulos: %d</string>
|
||||
<string name="home_unsupported">Sin soporte</string>
|
||||
<string name="home_unsupported_reason">KernelSU solo admite kernels GKI por ahora</string>
|
||||
<string name="home_unsupported">No soportado</string>
|
||||
<string name="home_unsupported_reason">Por el momento, KernelSU solo es compatible con kernels genéricos (GKIs)</string>
|
||||
<string name="home_kernel">Versión del kernel</string>
|
||||
<string name="home_manager_version">Versión del gestor</string>
|
||||
<string name="home_fingerprint">Huella del dispositivo</string>
|
||||
<string name="home_selinux_status">Estado de SELinux</string>
|
||||
<!-- It may be better to leave SELinux statuses untranslated -->
|
||||
<string name="selinux_status_disabled">Desactivado</string>
|
||||
<string name="selinux_status_enforcing">Estricto</string>
|
||||
<string name="selinux_status_enforcing">Enforcing de SELinux</string>
|
||||
<string name="selinux_status_permissive">Permisivo</string>
|
||||
<string name="selinux_status_unknown">Desconocido</string>
|
||||
<string name="superuser">Superusuario</string>
|
||||
<string name="module_failed_to_enable">Error al activar el módulo: %s</string>
|
||||
<string name="module_failed_to_disable">Error al desactivar el módulo: %s</string>
|
||||
<string name="module_empty">Ningún módulo instalado</string>
|
||||
<string name="module_failed_to_enable">No se pudo habilitar el módulo \"%s\"</string>
|
||||
<string name="module_failed_to_disable">No se pudo deshabilitar el módulo \"%s\"</string>
|
||||
<string name="module_empty">No hay ningún módulo instalado</string>
|
||||
<string name="module">Módulo</string>
|
||||
<string name="uninstall">Desinstalar</string>
|
||||
<string name="module_install">Instalar</string>
|
||||
<string name="module_install">Instalar módulo</string>
|
||||
<string name="install">Instalar</string>
|
||||
<string name="reboot">Reiniciar</string>
|
||||
<string name="settings">Ajustes</string>
|
||||
<string name="reboot_userspace">Reinicio suave</string>
|
||||
<string name="reboot_userspace">Reinicio minimo</string>
|
||||
<string name="reboot_recovery">Reiniciar en modo de recuperación</string>
|
||||
<string name="reboot_bootloader">Reiniciar en modo de arranque</string>
|
||||
<string name="reboot_download">Reiniciar en modo Download</string>
|
||||
<string name="reboot_edl">Reiniciar en modo EDL</string>
|
||||
<string name="about">Acerca de</string>
|
||||
<string name="module_uninstall_confirm">¿Está seguro de que desea desinstalar el módulo %s?</string>
|
||||
<string name="module_uninstall_success">%s desinstalado</string>
|
||||
<string name="module_uninstall_failed">Fallo al desinstalar: %s</string>
|
||||
<string name="module_uninstall_confirm">¿Estás seguro de que quieres desinstalar el módulo \"%s\"?</string>
|
||||
<string name="module_uninstall_success">\"%s\" esta desinstalado</string>
|
||||
<string name="module_uninstall_failed">No se pudo desinstalar \"%s\"</string>
|
||||
<string name="module_version">Versión</string>
|
||||
<string name="module_author">Autor</string>
|
||||
<string name="module_overlay_fs_not_available">Los módulos no están disponibles ya que OverlayFS está desactivado por el kernel.</string>
|
||||
<string name="refresh">Refrescar</string>
|
||||
<string name="show_system_apps">Mostrar aplicaciones del sistema</string>
|
||||
<string name="hide_system_apps">Ocultar aplicaciones del sistema</string>
|
||||
<string name="send_log">Enviar registros</string>
|
||||
<string name="module_overlay_fs_not_available">El módulo no puede funcionar ya que OverlayFS no está disponible!</string>
|
||||
<string name="refresh">Recargar</string>
|
||||
<string name="show_system_apps">Mostrar applicaciones del sistema</string>
|
||||
<string name="hide_system_apps">Ocultar applicaciones del sistema</string>
|
||||
<string name="send_log">Enviar registro de informe</string>
|
||||
<string name="safe_mode">Modo seguro</string>
|
||||
<string name="reboot_to_apply">Reinicia para aplicar cambios</string>
|
||||
<string name="module_magisk_conflict">¡Los módulos no están disponibles debido a un conflicto con Magisk!</string>
|
||||
<string name="module_magisk_conflict">Se deshabilitaron los módulos ya que entran en conflicto con los de Magisk!</string>
|
||||
<string name="home_learn_kernelsu">Aprende KernelSU</string>
|
||||
<string name="home_learn_kernelsu_url">https://kernelsu.org/guide/what-is-kernelsu.html</string>
|
||||
<string name="home_click_to_learn_kernelsu">Aprende a instalar KernelSU y a utilizar módulos</string>
|
||||
<string name="home_click_to_learn_kernelsu">Descubre cómo instalar KernelSU y utilizar módulos</string>
|
||||
<string name="home_support_title">Apóyanos</string>
|
||||
<string name="home_support_content">KernelSU es, y siempre será, gratuito y de código abierto. Sin embargo, puedes demostrarnos que te importamos haciendo una donación.</string>
|
||||
<string name="about_source_code">Ver código fuente en %1$s<br/>Únete a nuestro canal %2$s</string>
|
||||
<string name="home_support_content">KernelSU es y siempre será, libre y de código abierto. De todas formas, puedes mostrarnos tu apoyo mediante una donación.</string>
|
||||
<string name="about_source_code">Mirar el código en %1$s<br/>Únete a nuestro canal de %2$s</string>
|
||||
<string name="profile_default">Predeterminado</string>
|
||||
<string name="profile_template">Plantilla</string>
|
||||
<string name="profile_custom">Personalizado</string>
|
||||
@@ -63,70 +63,45 @@
|
||||
<string name="profile_namespace_individual">Individual</string>
|
||||
<string name="profile_groups">Grupos</string>
|
||||
<string name="profile_capabilities">Capacidades</string>
|
||||
<string name="profile_selinux_context">Contexto SELinux</string>
|
||||
<string name="profile_selinux_context">Contexto de SELinux</string>
|
||||
<string name="profile_umount_modules">Desmontar módulos</string>
|
||||
<string name="failed_to_update_app_profile">Error al actualizar el perfil de la aplicación para %s</string>
|
||||
<string name="failed_to_update_app_profile">No se pudo actualizar el perfil de la applicación para %s</string>
|
||||
<string name="settings_umount_modules_default">Desmontar módulos por defecto</string>
|
||||
<string name="settings_umount_modules_default_summary">El valor global predeterminado para \"Umount modules\" en App Profile. Si está activado, eliminará todas las modificaciones de módulos del sistema para las apps que no tengan un perfil establecido.</string>
|
||||
<string name="profile_umount_modules_summary">Activar esta opción permitirá a KernelSU restaurar cualquier archivo modificado por los módulos para esta aplicación.</string>
|
||||
<string name="settings_umount_modules_default_summary">El valor global predeterminado para \"Desmontar módulos\" en los perfiles de las aplicaciones. Si la habilitas, se desharán todas las modificaciones al sistema hechas por el módulo para las applicaciones que no tengan un perfil establecido.</string>
|
||||
<string name="profile_umount_modules_summary">Si habilitas esta opción, KernelSU podrá restaurar cualquier archivo modificado por los módulos para esta app.</string>
|
||||
<string name="profile_selinux_domain">Dominio</string>
|
||||
<string name="profile_selinux_rules">Reglas</string>
|
||||
<string name="module_update">Actualizar</string>
|
||||
<string name="module_downloading">Descargando módulo: %s</string>
|
||||
<string name="module_downloading">Descargando módulo: \"%s\"</string>
|
||||
<string name="module_start_downloading">Iniciar descarga: %s</string>
|
||||
<string name="new_version_available">La nueva versión %s está disponible, haga clic para actualizar.</string>
|
||||
<string name="launch_app">Iniciar</string>
|
||||
<string name="force_stop_app">Forzar detención</string>
|
||||
<string name="restart_app">Reiniciar</string>
|
||||
<string name="failed_to_update_sepolicy">Error al actualizar las reglas SELinux para: %s</string>
|
||||
<string name="require_kernel_version">La versión %d actual de KernelSU es demasiado baja para que el gestor funcione correctamente. Por favor, ¡actualice a la versión %d o superior!</string>
|
||||
<string name="new_version_available">Nueva versión: %s está disponible, toque para actualizar</string>
|
||||
<string name="launch_app">Abrir Aplicacion</string>
|
||||
<string name="force_stop_app">Forzar cierre de la aplicacion</string>
|
||||
<string name="restart_app">Reiniciar aplicacion</string>
|
||||
<string name="failed_to_update_sepolicy">Falló al actualizar reglas de SEpolicy por: %s</string>
|
||||
<string name="require_kernel_version">La versión actual de KernelSU %d es demasiado baja para que el gestor funcione correctamente. ¡Por favor actualiza a la versión %d o superior!</string>
|
||||
<string name="module_changelog">Registro de cambios</string>
|
||||
<string name="app_profile_template_import_success">Importado con éxito</string>
|
||||
<string name="app_profile_export_to_clipboard">Exportar al portapapeles</string>
|
||||
<string name="app_profile_template_export_empty">¡No se encuentra la plantilla local para exportar!</string>
|
||||
<string name="app_profile_template_id_exist">¡El ID de plantilla ya existe!</string>
|
||||
<string name="app_profile_import_from_clipboard">Importar desde el portapapeles</string>
|
||||
<string name="module_changelog_failed">Fallo en la obtención del registro de cambios: %s</string>
|
||||
<string name="app_profile_template_import_success">Importado con exíto</string>
|
||||
<string name="app_profile_export_to_clipboard">Exportar a portapapeles</string>
|
||||
<string name="app_profile_template_export_empty">No se puede encontrar plantilla local para exportar!</string>
|
||||
<string name="app_profile_template_id_exist">Ya existe un ID de la plantilla!</string>
|
||||
<string name="app_profile_import_from_clipboard">Importar desde portapapeles</string>
|
||||
<string name="module_changelog_failed">Error en obtener los registros de cambios: %s</string>
|
||||
<string name="app_profile_template_name">Nombre</string>
|
||||
<string name="app_profile_template_id_invalid">ID de plantilla no válida</string>
|
||||
<string name="app_profile_template_sync">Sincronizar plantillas en línea</string>
|
||||
<string name="app_profile_template_id_invalid">ID de la plantilla es invalido</string>
|
||||
<string name="app_profile_template_sync">Sincronizar plantillas en linea</string>
|
||||
<string name="app_profile_template_create">Crear plantilla</string>
|
||||
<string name="app_profile_template_readonly">Sólo lectura</string>
|
||||
<string name="app_profile_template_readonly">sololectura</string>
|
||||
<string name="app_profile_import_export">Importar/Exportar</string>
|
||||
<string name="app_profile_template_save_failed">No se ha podido guardar la plantilla</string>
|
||||
<string name="app_profile_template_save_failed">Fallo en guardar plantilla</string>
|
||||
<string name="app_profile_template_edit">Editar plantilla</string>
|
||||
<string name="app_profile_template_id">ID</string>
|
||||
<string name="settings_profile_template">Plantilla de perfil de aplicación</string>
|
||||
<string name="app_profile_template_id">id</string>
|
||||
<string name="settings_profile_template">Plantilla del perfil de la aplicacion</string>
|
||||
<string name="app_profile_template_description">Descripción</string>
|
||||
<string name="app_profile_template_save">Guardar</string>
|
||||
<string name="settings_profile_template_summary">Gestionar la plantilla local y en línea de App Profile</string>
|
||||
<string name="settings_profile_template_summary">Administrar las plantillas locales y de en linea del perfil de la aplicacion</string>
|
||||
<string name="app_profile_template_delete">Eliminar</string>
|
||||
<string name="app_profile_template_import_empty">¡El portapapeles está vacío!</string>
|
||||
<string name="app_profile_template_import_empty">El portapapeles esta vacio!</string>
|
||||
<string name="app_profile_template_view">Ver plantilla</string>
|
||||
<string name="save_log">Guardar registros</string>
|
||||
<string name="enable_web_debugging">Activar la depuración de WebView</string>
|
||||
<string name="select_file_tip">Se recomienda la imagen de partición %1$s</string>
|
||||
<string name="select_kmi">Selecciona KMI</string>
|
||||
<string name="install_next">Siguiente</string>
|
||||
<string name="direct_install">Instalación directa (Recomendada)</string>
|
||||
<string name="install_inactive_slot_warning">¡Su dispositivo será **FORZADO** a arrancar en la ranura inactiva actual después de un reinicio!\nUtilice esta opción sólo después de que la OTA se haya realizado.\n¿Continuar?</string>
|
||||
<string name="settings_uninstall">Desinstalar</string>
|
||||
<string name="settings_restore_stock_image">Restaurar imagen de archivo</string>
|
||||
<string name="settings_uninstall_temporary_message">Desinstalar temporalmente KernelSU, restaurar al estado original tras el siguiente reinicio.</string>
|
||||
<string name="selected_lkm">LKM seleccionado: %s</string>
|
||||
<string name="flash_failed">Flash falló</string>
|
||||
<string name="flash_success">Éxito de Flash</string>
|
||||
<string name="grant_root_failed">¡No se ha podido conceder el acceso root!</string>
|
||||
<string name="open">Abrir</string>
|
||||
<string name="select_file">Seleccione un archivo</string>
|
||||
<string name="install_inactive_slot">Instalar en ranura inactiva (Después de OTA)</string>
|
||||
<string name="settings_uninstall_temporary">Desinstalar temporalmente</string>
|
||||
<string name="settings_uninstall_permanent">Desinstalar permanentemente</string>
|
||||
<string name="settings_uninstall_permanent_message">Desinstalar KernelSU (Root y todos los módulos) completa y permanentemente.</string>
|
||||
<string name="shrink_sparse_image_message">Redimensiona la imagen dispersa donde se encuentra el módulo a su tamaño real. Tenga en cuenta que esto puede hacer que el módulo funcione de forma anormal, por lo que sólo debe utilizarlo cuando sea necesario (por ejemplo, para realizar copias de seguridad).</string>
|
||||
<string name="settings_check_update">Comprobar actualización</string>
|
||||
<string name="settings_check_update_summary">Comprobación automática de actualizaciones al abrir la aplicación</string>
|
||||
<string name="shrink_sparse_image">Minimizar la imagen dispersa</string>
|
||||
<string name="enable_web_debugging_summary">Puede ser usado para depurar WebUI, por favor habilítalo sólo cuando sea necesario.</string>
|
||||
<string name="settings_restore_stock_image_message">Restaurar la imagen de fábrica stock (Si existe una copia de seguridad), por lo general se utiliza antes de OTA; si necesita desinstalar KernelSU, por favor, utilice \"Desinstalar permanentemente\".</string>
|
||||
</resources>
|
||||
<string name="save_log">Guardar Registros</string>
|
||||
</resources>
|
||||
@@ -12,8 +12,8 @@
|
||||
<string name="reboot">Taaskäivita</string>
|
||||
<string name="reboot_recovery">Taaskäivita taastusesse</string>
|
||||
<string name="module_uninstall_confirm">Kas soovid kindlasti eemaldada mooduli %s?</string>
|
||||
<string name="module_uninstall_success">%s eemaldatud</string>
|
||||
<string name="send_log">Saada logid</string>
|
||||
<string name="module_uninstall_success">%s on eemaldatud</string>
|
||||
<string name="send_log">Raporteeri logi</string>
|
||||
<string name="safe_mode">Turvarežiim</string>
|
||||
<string name="reboot_to_apply">Muudatuste rakendamiseks taaskäivita</string>
|
||||
<string name="home_learn_kernelsu">Õpi KernelSUd</string>
|
||||
@@ -28,8 +28,8 @@
|
||||
<string name="app_profile_template_edit">Muuda malli</string>
|
||||
<string name="settings_profile_template">Rakenduseprofiili mall</string>
|
||||
<string name="app_profile_template_id">ID</string>
|
||||
<string name="app_profile_template_readonly">Vaid lugemiseks</string>
|
||||
<string name="app_profile_template_id_exist">Malli ID juba eksisteerib!</string>
|
||||
<string name="app_profile_template_readonly">vaid lugemiseks</string>
|
||||
<string name="app_profile_template_id_exist">malli ID juba eksisteerib!</string>
|
||||
<string name="app_profile_export_to_clipboard">Ekspordi lõikelauale</string>
|
||||
<string name="app_profile_template_sync">Sünkrooni võrgumallid</string>
|
||||
<string name="module_changelog_failed">Muudatuste logi hankimine ebaõnnestus: %s</string>
|
||||
@@ -59,10 +59,10 @@
|
||||
<string name="module_author">Autor</string>
|
||||
<string name="module_uninstall_failed">Eemaldamine ebaõnnestus: %s</string>
|
||||
<string name="module_version">Versioon</string>
|
||||
<string name="module_overlay_fs_not_available">Moodulid pole saadaval, kuna OverlayFS on kernelis keelatud.</string>
|
||||
<string name="module_overlay_fs_not_available">overlayfs pole saadaval, moodul ei saa töötada!</string>
|
||||
<string name="show_system_apps">Kuva süsteemirakendused</string>
|
||||
<string name="hide_system_apps">Peida süsteemirakendused</string>
|
||||
<string name="module_magisk_conflict">Moodulid pole saadaval Magiski konflikti tõttu!</string>
|
||||
<string name="module_magisk_conflict">Moodulid on keelatud, kuna need lähevad konflikti Magiski omadega!</string>
|
||||
<string name="home_click_to_learn_kernelsu">Õpi KernelSUd paigaldama ja mooduleid kasutama</string>
|
||||
<string name="home_support_title">Toeta meid</string>
|
||||
<string name="profile_groups">Grupid</string>
|
||||
@@ -77,14 +77,14 @@
|
||||
<string name="profile_capabilities">Võimekused</string>
|
||||
<string name="app_profile_template_id_invalid">Sobimatu malli ID</string>
|
||||
<string name="profile_selinux_context">SELinux kontekst</string>
|
||||
<string name="require_kernel_version">Praegune KernelSU versioon %d on liiga madal, haldur ei saa korrektselt töötada. Palun täienda versioonile %d või kõrgem!</string>
|
||||
<string name="require_kernel_version">Praegune KernelSU versioon %d on liiga madal, haldur ei saa konkreetselt toimida. Palun täienda versioonile %d või kõrgem!</string>
|
||||
<string name="profile_selinux_domain">Domeen</string>
|
||||
<string name="launch_app">Käivita</string>
|
||||
<string name="force_stop_app">Sundpeata</string>
|
||||
<string name="profile_selinux_rules">Reeglid</string>
|
||||
<string name="module_update">Uuenda</string>
|
||||
<string name="module_downloading">Mooduli allalaadimine: %s</string>
|
||||
<string name="new_version_available">Uus versioon %s on saadaval, klõpsa täiendamiseks.</string>
|
||||
<string name="new_version_available">Uus versioon: %s on saadaval, klõpsa täiendamiseks</string>
|
||||
<string name="restart_app">Taaskäivita</string>
|
||||
<string name="module_changelog">Muudatuste logi</string>
|
||||
<string name="app_profile_template_name">Nimi</string>
|
||||
@@ -100,8 +100,8 @@
|
||||
<string name="app_profile_template_create">Loo mall</string>
|
||||
<string name="settings_profile_template_summary">Halda kohalikke ja võrgusolevaid rakenduseprofiili malle</string>
|
||||
<string name="profile_umount_modules_summary">Selle valiku lubamine lubab KernelSU-l taastada selle rakenduse moodulite poolt mistahes muudetud faile.</string>
|
||||
<string name="app_profile_template_export_empty">Ei saa eksportida, kohalikku malli ei leitud!</string>
|
||||
<string name="settings_umount_modules_default_summary">Globaalne vaikeväärtus \"Lahtihaagitud moodulitele\" rakenduseprofiilis. Lubamisel eemaldab see kõik moodulite süsteemimuudatused rakendustele, millel ei ole profiili määratud.</string>
|
||||
<string name="app_profile_template_export_empty">Eksportimiseks kohalikku malli ei leitud!</string>
|
||||
<string name="settings_umount_modules_default_summary">Globaalne vaikeväärtus \"Lahtihaagitud moodulitele\" rakenduseprofiilides. Lubamisel eemaldab see kõik moodulite süsteemimuudatused rakendustele, millel ei ole profiili määratud.</string>
|
||||
<string name="enable_web_debugging_summary">Saab kasutada WebUI silumiseks, palun luba ainult vajadusel.</string>
|
||||
<string name="grant_root_failed">Juurkasutaja andmine ebaõnnestus!</string>
|
||||
<string name="settings_check_update">Kontrolli uuendusi</string>
|
||||
@@ -109,22 +109,4 @@
|
||||
<string name="open">Ava</string>
|
||||
<string name="enable_web_debugging">Luba WebView silumine</string>
|
||||
<string name="save_log">Salvesta Logid</string>
|
||||
<string name="select_kmi">Vali KMI</string>
|
||||
<string name="select_file_tip">%1$s partitsioonitõmmis on soovitatud</string>
|
||||
<string name="install_next">Edasi</string>
|
||||
<string name="install_inactive_slot_warning">Sinu seade **SUNNITAKSE** pärast taaskäivitust ebaaktiivsesse lahtrisse käivituma!\nKasuta seda valikut vaid siis, kui tegid üle-õhu uuenduse.\nJätkad?</string>
|
||||
<string name="settings_uninstall">Eemalda</string>
|
||||
<string name="settings_uninstall_temporary_message">Eemalda KernelSU ajutiselt, taasta pärast taaskäivitust algseisu.</string>
|
||||
<string name="settings_uninstall_permanent_message">KernelSU eemaldamine (juurkasutaja ja kõik moodulid) täielikult ja püsivalt.</string>
|
||||
<string name="settings_restore_stock_image_message">Taasta tehase-vaiketõmmis (kui varundus eksisteerib), tavaliselt kasutatakse enne üle-õhu uuendust; kui soovid KernelSU-d eemaldada, palun kasuta \"Eemalda püsivalt\".</string>
|
||||
<string name="flashing">Välgutamine</string>
|
||||
<string name="flash_success">Välgutamine õnnestus</string>
|
||||
<string name="flash_failed">Välgutamine ebaõnnestus</string>
|
||||
<string name="selected_lkm">Valitud LKM: %s</string>
|
||||
<string name="direct_install">Otsene paigaldus (soovitatud)</string>
|
||||
<string name="select_file">Vali fail</string>
|
||||
<string name="install_inactive_slot">Paigalda ebaaktiivsesse lahtrisse (pärast üle-õhu uuendust)</string>
|
||||
<string name="settings_uninstall_temporary">Eemalda ajutiselt</string>
|
||||
<string name="settings_uninstall_permanent">Eemalda püsivalt</string>
|
||||
<string name="settings_restore_stock_image">Taasta vaikimisi tõmmis</string>
|
||||
</resources>
|
||||
</resources>
|
||||
@@ -1,68 +1,67 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="home">خانه</string>
|
||||
<string name="home_not_installed">نصب نشده است</string>
|
||||
<string name="home_click_to_install">برای نصب ضربه بزنید</string>
|
||||
<string name="home_working">به درستی کار میکند</string>
|
||||
<string name="home_working_version">نسخه: %d</string>
|
||||
<string name="home_superuser_count">برنامه های با دسترسی روت: %d</string>
|
||||
<string name="home_module_count">ماژولها: %d</string>
|
||||
<string name="home_unsupported">پشتیبانی نشده</string>
|
||||
<string name="home_unsupported_reason">کرنل اس یو فقط هسته های gki را پشتیبانی میکند</string>
|
||||
<string name="home">خانه</string>
|
||||
<string name="home_not_installed">نصب نشده است</string>
|
||||
<string name="home_click_to_install">برای نصب ضربه بزنید</string>
|
||||
<string name="home_working">به درستی کار میکند</string>
|
||||
<string name="home_working_version">نسخه: %d</string>
|
||||
<string name="home_superuser_count">برنامه های با دسترسی روت: %d</string>
|
||||
<string name="home_module_count">ماژولها: %d</string>
|
||||
<string name="home_unsupported">پشتیبانی نشده</string>
|
||||
<string name="home_unsupported_reason">کرنل اس یو فقط هسته های gki را پشتیبانی میکند</string>
|
||||
<string name="home_kernel">هسته</string>
|
||||
<string name="home_manager_version">نسخه برنامه</string>
|
||||
<string name="home_fingerprint">اثرانگشت</string>
|
||||
<string name="home_selinux_status">وضعیت SELinux</string>
|
||||
<string name="selinux_status_disabled">غیرفعال</string>
|
||||
<string name="selinux_status_enforcing">قانونمند</string>
|
||||
<string name="selinux_status_permissive">آزاد</string>
|
||||
<string name="selinux_status_unknown">ناشناخته</string>
|
||||
<string name="superuser">دسترسی روت</string>
|
||||
<string name="selinux_status_disabled">غیرفعال</string>
|
||||
<string name="selinux_status_enforcing">قانونمند</string>
|
||||
<string name="selinux_status_permissive">آزاد</string>
|
||||
<string name="selinux_status_unknown">ناشناخته</string>
|
||||
<string name="superuser">دسترسی روت</string>
|
||||
<string name="module_failed_to_enable">فعال کردن ماژول ناموفق بود: %s</string>
|
||||
<string name="module_failed_to_disable">غیرفعال کردن ماژول ناموفق بود: %s</string>
|
||||
<string name="module_empty">هیچ ماژولی نصب نشده است</string>
|
||||
<string name="module">ماژول</string>
|
||||
<string name="uninstall">لغو نصب</string>
|
||||
<string name="module_install">نصب</string>
|
||||
<string name="install">نصب</string>
|
||||
<string name="reboot">راه اندازی دوباره</string>
|
||||
<string name="settings">تنظیمات</string>
|
||||
<string name="reboot_userspace">راه اندازی نرم</string>
|
||||
<string name="reboot_recovery">راه اندازی به ریکاوری </string>
|
||||
<string name="reboot_bootloader">راه اندازی به بوتلودر</string>
|
||||
<string name="reboot_download">راه اندازی به حالت دانلود</string>
|
||||
<string name="reboot_edl">راه اندازی به EDL</string>
|
||||
<string name="about">درباره</string>
|
||||
<string name="module_failed_to_disable">غیرفعال کردن ماژول ناموفق بود: %s</string>
|
||||
<string name="module_empty">هیچ ماژولی نصب نشده است</string>
|
||||
<string name="module">ماژول</string>
|
||||
<string name="uninstall">لغو نصب</string>
|
||||
<string name="module_install">نصب</string>
|
||||
<string name="install">نصب</string>
|
||||
<string name="reboot">راه اندازی دوباره</string>
|
||||
<string name="settings">تنظیمات</string>
|
||||
<string name="reboot_userspace">راه اندازی نرم</string>
|
||||
<string name="reboot_recovery">راه اندازی به ریکاوری </string>
|
||||
<string name="reboot_bootloader">راه اندازی به بوتلودر</string>
|
||||
<string name="reboot_download">راه اندازی به حالت دانلود</string>
|
||||
<string name="reboot_edl">راه اندازی به EDL</string>
|
||||
<string name="about">درباره</string>
|
||||
<string name="module_uninstall_confirm">آیا مطمئنید که میخواهید ماژول %s را پاک کنید؟</string>
|
||||
<string name="module_uninstall_success">%s پاک شد</string>
|
||||
<string name="module_uninstall_failed">پاک کردن ناموفق بود: %s</string>
|
||||
<string name="module_version">نسخه</string>
|
||||
<string name="module_author">سازنده</string>
|
||||
<string name="module_overlay_fs_not_available">overlayfs موجود نیست. مازول کار نمیکند!!</string>
|
||||
<string name="refresh">تازهسازی</string>
|
||||
<string name="show_system_apps">نمایش برنامه های سیستمی</string>
|
||||
<string name="hide_system_apps">مخفی کردن برنامه های سیستمی</string>
|
||||
<string name="send_log">ارسال وقایع</string>
|
||||
<string name="safe_mode">حالت امن</string>
|
||||
<string name="reboot_to_apply">راهاندازی مجدد برای تاثیرگذاری</string>
|
||||
<string name="module_magisk_conflict">مازول به دلیل تعارض با مجیسک غیرفعال شده اند\'s!</string>
|
||||
<string name="home_learn_kernelsu">یادگیری کرنل اس یو</string>
|
||||
<string name="home_learn_kernelsu_url">https://kernelsu.org/guide/what-is-kernelsu.html</string>
|
||||
<string name="home_click_to_learn_kernelsu">یاد بگیرید چگونه از کرنل اس یو و ماژول ها استفاده کنید</string>
|
||||
<string name="home_support_title">از ما حمایت کنید</string>
|
||||
<string name="home_support_content">KernelSU رایگان است و همیشه خواهد بود و منبع باز است. با این حال، می توانید با اهدای کمک مالی به ما نشان دهید که برایتان مهم است.</string>
|
||||
<string name="about_source_code">
|
||||
<string name="module_uninstall_success">%s پاک شد</string>
|
||||
<string name="module_uninstall_failed">پاک کردن ناموفق بود: %s</string>
|
||||
<string name="module_version">نسخه</string>
|
||||
<string name="module_author">سازنده</string>
|
||||
<string name="module_overlay_fs_not_available">overlayfs موجود نیست. مازول کار نمیکند!!</string>
|
||||
<string name="refresh">تازهسازی</string>
|
||||
<string name="show_system_apps">نمایش برنامه های سیستمی</string>
|
||||
<string name="hide_system_apps">مخفی کردن برنامه های سیستمی</string>
|
||||
<string name="send_log">ارسال وقایع</string>
|
||||
<string name="safe_mode">حالت امن</string>
|
||||
<string name="reboot_to_apply">راهاندازی مجدد برای تاثیرگذاری</string>
|
||||
<string name="module_magisk_conflict">مازول به دلیل تعارض با مجیسک غیرفعال شده اند\'s!</string>
|
||||
<string name="home_learn_kernelsu">یادگیری کرنل اس یو</string>
|
||||
<string name="home_learn_kernelsu_url">https://kernelsu.org/guide/what-is-kernelsu.html</string>
|
||||
<string name="home_click_to_learn_kernelsu">یاد بگیرید چگونه از کرنل اس یو و ماژول ها استفاده کنید</string>
|
||||
<string name="home_support_title">از ما حمایت کنید</string>
|
||||
<string name="home_support_content">KernelSU رایگان است و همیشه خواهد بود و منبع باز است. با این حال، می توانید با اهدای کمک مالی به ما نشان دهید که برایتان مهم است.</string>
|
||||
<string name="about_source_code">
|
||||
<![CDATA[ View source code at %1$s<br/>Join our %2$s channel ]]>
|
||||
</string>
|
||||
<string name="profile">پروفایل برنامه</string>
|
||||
<string name="profile_default">پیشفرض</string>
|
||||
<string name="profile_template">قالب</string>
|
||||
<string name="profile_custom">شخصی سازی شده</string>
|
||||
<string name="profile_name">اسم پروفایل</string>
|
||||
<string name="profile_namespace">Mount namespace</string>
|
||||
<string name="profile_namespace_inherited">اثر گرفته</string>
|
||||
<string name="profile_namespace_global">گلوبال</string>
|
||||
<string name="profile_namespace_individual">تکی</string>
|
||||
<string name="profile_umount_modules">جداکردن ماژول ها</string>
|
||||
<string name="profile">پروفایل برنامه</string>
|
||||
<string name="profile_default">پیشفرض</string>
|
||||
<string name="profile_template">قالب</string>
|
||||
<string name="profile_custom">شخصی سازی شده</string>
|
||||
<string name="profile_name">اسم پروفایل</string>
|
||||
<string name="profile_namespace">Mount namespace</string>
|
||||
<string name="profile_namespace_inherited">اثر گرفته</string>
|
||||
<string name="profile_namespace_global">گلوبال</string>
|
||||
<string name="profile_namespace_individual">تکی</string>
|
||||
<string name="profile_umount_modules">جداکردن ماژول ها</string>
|
||||
<string name="save_log">ذخیره گزارشها</string>
|
||||
</resources>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<string name="home_working_version">Version : %d</string>
|
||||
<string name="home_superuser_count">Super-utilisateurs : %d</string>
|
||||
<string name="home_module_count">Modules : %d</string>
|
||||
<string name="home_unsupported_reason">KernelSU ne prend désormais en charge que les noyaux GKI</string>
|
||||
<string name="home_unsupported_reason">Actuellement, KernelSU ne supporte que les noyaux GKI</string>
|
||||
<string name="home_kernel">Noyau</string>
|
||||
<string name="home_fingerprint">Empreinte digitale</string>
|
||||
<string name="home_selinux_status">Mode SELinux</string>
|
||||
@@ -16,16 +16,16 @@
|
||||
<string name="module_empty">Aucun module installé</string>
|
||||
<string name="home">Accueil</string>
|
||||
<string name="home_click_to_install">Appuyez ici pour installer</string>
|
||||
<string name="home_unsupported">Non pris en charge</string>
|
||||
<string name="module_uninstall_failed">Échec de la désinstallation : %s</string>
|
||||
<string name="home_unsupported">Non supporté</string>
|
||||
<string name="module_uninstall_failed">Échec de la désinstallation : %s</string>
|
||||
<string name="module_version">Version</string>
|
||||
<string name="home_manager_version">Version du gestionnaire</string>
|
||||
<string name="selinux_status_enforcing">Enforcing</string>
|
||||
<string name="module_failed_to_enable">Échec de l\'activation du module : %s</string>
|
||||
<string name="module_failed_to_enable">Échec de l\'activation du module : %s</string>
|
||||
<string name="module">Modules</string>
|
||||
<string name="uninstall">Désinstaller</string>
|
||||
<string name="module_install">Installer</string>
|
||||
<string name="module_failed_to_disable">Échec de la désactivation du module : %s</string>
|
||||
<string name="module_failed_to_disable">Échec de la désactivation du module : %s</string>
|
||||
<string name="reboot">Redémarrer</string>
|
||||
<string name="install">Installer</string>
|
||||
<string name="settings">Paramètres</string>
|
||||
@@ -39,19 +39,20 @@
|
||||
<string name="module_author">Auteur</string>
|
||||
<string name="module_uninstall_confirm">Êtes-vous sûr(e) de vouloir désinstaller le module %s \?</string>
|
||||
<string name="home_learn_kernelsu">Découvrir KernelSU</string>
|
||||
<string name="module_overlay_fs_not_available">Les modules sont indisponibles car OverlayFS est désactivé par le noyau !</string>
|
||||
<string name="module_overlay_fs_not_available">OverlayFS est indisponible, impossible de faire fonctionner les modules !</string>
|
||||
<string name="refresh">Rafraîchir</string>
|
||||
<string name="show_system_apps">Afficher les applications système</string>
|
||||
<string name="hide_system_apps">Masquer les applications système</string>
|
||||
<string name="safe_mode">Mode sans échec</string>
|
||||
<string name="send_log">Envoyer les journaux</string>
|
||||
<string name="send_log">Rapport de journal</string>
|
||||
<string name="reboot_to_apply">Redémarrez pour appliquer les modifications</string>
|
||||
<string name="module_magisk_conflict">Les modules sont indisponibles en raison d\'un conflit avec Magisk !</string>
|
||||
<string name="module_magisk_conflict">Les modules sont désactivés car ils sont en conflit avec ceux de Magisk !</string>
|
||||
<string name="home_learn_kernelsu_url">https://kernelsu.org/guide/what-is-kernelsu.html</string>
|
||||
<string name="home_support_title">Soutenez-nous</string>
|
||||
<string name="home_click_to_learn_kernelsu">Découvrez comment installer KernelSU et utiliser les modules</string>
|
||||
<string name="home_support_content">KernelSU est, et restera toujours, gratuit et open source. Vous pouvez cependant nous témoigner de votre soutien en nous faisant un don.</string>
|
||||
<string name="about_source_code"><![CDATA[Voir le code source à %1$s<br/>Rejoignez notre canal %2$s]]></string>
|
||||
<string name="about_source_code">Voir le code source sur %1$s<br/>
|
||||
\nRejoindre notre canal %2$s</string>
|
||||
<string name="profile_template">Modèle</string>
|
||||
<string name="profile_default">Par défaut</string>
|
||||
<string name="profile_custom">Personnalisé</string>
|
||||
@@ -65,36 +66,36 @@
|
||||
<string name="profile_capabilities">Capacités</string>
|
||||
<string name="profile_umount_modules">Démonter les modules</string>
|
||||
<string name="failed_to_update_app_profile">Échec de la modification du profil d\'application de %s</string>
|
||||
<string name="profile_umount_modules_summary">L\'activation de cette option permettra à KernelSU de restaurer tous les fichiers modifiés par les modules pour cette application.</string>
|
||||
<string name="profile_umount_modules_summary">L\'activation de cette option permettra à KernelSU de restaurer tous les fichiers modifiés par les modules de cette application.</string>
|
||||
<string name="settings_umount_modules_default">Démonter les modules par défaut</string>
|
||||
<string name="settings_umount_modules_default_summary">Valeur globale par défaut pour l\'option \"Démonter les modules\" dans les profils d\'application. Lorsque l\'option est activée, les modifications apportées au système par les modules sont supprimées pour les applications qui n\'ont pas de profil défini.</string>
|
||||
<string name="settings_umount_modules_default_summary">Valeur globale par défaut pour l\'option « Démonter les modules » dans les profils d\'application. Lorsqu\'elle est activée, les modifications apportées au système par les modules seront supprimées pour les applications qui n\'ont pas de profil défini.</string>
|
||||
<string name="profile_selinux_domain">Domaine</string>
|
||||
<string name="profile_selinux_rules">Règles</string>
|
||||
<string name="module_update">Mettre à jour</string>
|
||||
<string name="module_downloading">Téléchargement du module : %s</string>
|
||||
<string name="launch_app">Lancer</string>
|
||||
<string name="new_version_available">La nouvelle version %s est disponible, appuyez ici pour mettre à jour.</string>
|
||||
<string name="new_version_available">La version %s est disponible, appuyez ici pour mettre à jour</string>
|
||||
<string name="module_start_downloading">Début du téléchargement de : %s</string>
|
||||
<string name="force_stop_app">Forcer l\'arrêt</string>
|
||||
<string name="restart_app">Relancer l\'application</string>
|
||||
<string name="failed_to_update_sepolicy">Échec de la mise à jour des règles SELinux pour %s</string>
|
||||
<string name="failed_to_update_sepolicy">Échec de la mise à jour des règles SELinux pour : %s</string>
|
||||
<string name="require_kernel_version">La version actuelle de KernelSU (%d) est trop ancienne pour que le gestionnaire fonctionne correctement. Veuillez passer à la version %d ou à une version supérieure !</string>
|
||||
<string name="app_profile_template_import_success">Importation réussie</string>
|
||||
<string name="app_profile_export_to_clipboard">Exporter vers le presse-papiers</string>
|
||||
<string name="app_profile_template_export_empty">Impossible de trouver un modèle local à exporter !</string>
|
||||
<string name="app_profile_template_id_exist">L\'ID du modèle existe déjà !</string>
|
||||
<string name="app_profile_template_export_empty">Impossible de trouver un modèle local à exporter !</string>
|
||||
<string name="app_profile_template_id_exist">L\'id du modèle existe déjà !</string>
|
||||
<string name="module_changelog">Journal des modifications</string>
|
||||
<string name="app_profile_import_from_clipboard">Importer à partir du presse-papiers</string>
|
||||
<string name="module_changelog_failed">Échec de récupération du journal des modifications : %s</string>
|
||||
<string name="app_profile_template_name">Nom</string>
|
||||
<string name="app_profile_template_id_invalid">ID de modèle invalide</string>
|
||||
<string name="app_profile_template_id_invalid">id de modèle invalide</string>
|
||||
<string name="app_profile_template_sync">Synchroniser les modèles en ligne</string>
|
||||
<string name="app_profile_template_create">Créer un modèle</string>
|
||||
<string name="app_profile_template_readonly">Lecture seule</string>
|
||||
<string name="app_profile_template_readonly">lecture seule</string>
|
||||
<string name="app_profile_import_export">Importer/exporter</string>
|
||||
<string name="app_profile_template_save_failed">Échec de l\'enregistrement du modèle</string>
|
||||
<string name="app_profile_template_edit">Modifier le modèle</string>
|
||||
<string name="app_profile_template_id">ID</string>
|
||||
<string name="app_profile_template_id">id</string>
|
||||
<string name="settings_profile_template">Modèles de profils d\'application</string>
|
||||
<string name="app_profile_template_description">Description</string>
|
||||
<string name="app_profile_template_save">Enregistrer</string>
|
||||
@@ -104,8 +105,8 @@
|
||||
<string name="app_profile_template_view">Voir le modèle</string>
|
||||
<string name="settings_check_update_summary">Vérifier automatiquement les mises à jour à l\'ouverture de l\'application</string>
|
||||
<string name="settings_check_update">Vérifier les mises à jour</string>
|
||||
<string name="enable_web_debugging">Activer le débogage WebView</string>
|
||||
<string name="enable_web_debugging_summary">Peut être utilisé pour déboguer WebUI. Activez uniquement cette option si nécessaire.</string>
|
||||
<string name="enable_web_debugging">Activer le débogage de WebView</string>
|
||||
<string name="enable_web_debugging_summary">Peut être utilisé pour déboguer WebUI, n\'activez cette option que si nécessaire.</string>
|
||||
<string name="grant_root_failed">Échec de l\'octroi des privilèges root !</string>
|
||||
<string name="open">Ouvrir</string>
|
||||
<string name="direct_install">Installation directe (recommandé)</string>
|
||||
@@ -118,26 +119,17 @@
|
||||
<string name="select_file_tip">L\'image de la partition %1$s est recommandée</string>
|
||||
<string name="select_kmi">Sélectionner une KMI</string>
|
||||
<string name="shrink_sparse_image">Minimiser la taille de l\'image partiellement allouée</string>
|
||||
<string name="shrink_sparse_image_message">Redimensionner à sa taille réelle l\'image partiellement allouée où se trouve le module. Notez que cela peut entraîner un dysfonctionnement du module ; utilisez donc cette fonctionnalité uniquement quand c\'est nécessaire (par exemple pour sauvegarder l\'appareil).</string>
|
||||
<string name="shrink_sparse_image_message">Redimensionner à sa taille réelle l\'image partiellement allouée où se trouve le module. Notez que cela peut entraîner un dysfonctionnement du module, utilisez donc cette fonctionnalité uniquement lorsque c\'est nécessaire (par exemple pour la sauvegarde de l\'appareil).</string>
|
||||
<string name="settings_uninstall">Désinstaller</string>
|
||||
<string name="settings_uninstall_temporary">Désinstaller temporairement</string>
|
||||
<string name="settings_uninstall_permanent">Désinstaller définitivement</string>
|
||||
<string name="settings_restore_stock_image">Restaurer l\'image d\'origine</string>
|
||||
<string name="settings_restore_stock_image_message">Restaurer l\'image d\'origine d\'usine (s\'il en existe une sauvegarde). Utilisé généralement avant une mise à jour OTA ; si vous devez désinstaller KernelSU, utilisez plutôt l\'option \"Désinstaller définitivement\".</string>
|
||||
<string name="settings_restore_stock_image">Restaurer l\'image stock</string>
|
||||
<string name="settings_restore_stock_image_message">Restaurer l\'image stock d\'usine (s\'il en existe une sauvegarde), option généralement utilisée avant une mise à jour OTA ; si vous avez besoin de désinstaller KernelSU, utilisez plutôt l\'option « Désinstaller définitivement ».</string>
|
||||
<string name="flashing">Flash en cours</string>
|
||||
<string name="flash_success">Flash réussi</string>
|
||||
<string name="flash_failed">Échec du flash</string>
|
||||
<string name="selected_lkm">LKM sélectionné : %s</string>
|
||||
<string name="selected_lkm">lkm sélectionné : %s</string>
|
||||
<string name="settings_uninstall_permanent_message">Désinstallation complète et permanente de KernelSU (root et tous les modules).</string>
|
||||
<string name="settings_uninstall_temporary_message">Désinstaller KernelSU temporairement et rétablir l\'état original au redémarrage suivant.</string>
|
||||
<string name="save_log">Enregistrer les journaux</string>
|
||||
<string name="module_sort_action_first">Trier par action</string>
|
||||
<string name="module_sort_enabled_first">Trier par activé</string>
|
||||
<string name="action">Action</string>
|
||||
<string name="log_saved">Journaux enregistrés</string>
|
||||
<string name="settings_disable_su">Désactiver la compatibilité avec su</string>
|
||||
<string name="settings_disable_su_summary">Désactivez temporairement la possibilité de toute application d\'obtenir les privilèges root via la commande su (les processus root existants ne seront pas affectés).</string>
|
||||
<string name="module_install_prompt_with_name">Les modules suivants vont être installés : %1$s</string>
|
||||
<string name="su_not_allowed">Impossible d\'octroyer les autorisations superutilisateur à %s</string>
|
||||
<string name="confirm">Confirmer</string>
|
||||
</resources>
|
||||
<string name="save_log">Enregistrer les Journaux</string>
|
||||
</resources>
|
||||
@@ -1,4 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="home">Inicio</string>
|
||||
</resources>
|
||||
@@ -81,4 +81,4 @@
|
||||
<string name="home_manager_version">मैनेजर वर्जन</string>
|
||||
<string name="new_version_available">नया वर्जन: %s उपलब्ध है,अपग्रेड के लिए क्लिक करें</string>
|
||||
<string name="save_log">लॉग सहेजें</string>
|
||||
</resources>
|
||||
</resources>
|
||||
@@ -80,4 +80,4 @@
|
||||
<string name="force_stop_app">Prisilno Zaustavite</string>
|
||||
<string name="restart_app">Resetujte</string>
|
||||
<string name="save_log">Spremi Zapise</string>
|
||||
</resources>
|
||||
</resources>
|
||||
@@ -3,132 +3,104 @@
|
||||
<string name="home_working">Működik</string>
|
||||
<string name="home_working_version">Verzió: %d</string>
|
||||
<string name="home_module_count">Modulok: %d</string>
|
||||
<string name="home_unsupported_reason">A KernelSU jelenleg csak GKI kerneleket támogat</string>
|
||||
<string name="home_unsupported_reason">KernelSU csak GKI kerneleket támogat jelenleg</string>
|
||||
<string name="home_kernel">Kernel</string>
|
||||
<string name="home_manager_version">Alkalmazás verziója</string>
|
||||
<string name="home_fingerprint">Ujjlenyomat</string>
|
||||
<string name="selinux_status_disabled">Letiltva</string>
|
||||
<string name="home_manager_version">App verziója</string>
|
||||
<string name="home_fingerprint">Build Fingerprint</string>
|
||||
<string name="selinux_status_disabled">Kikapcsolt</string>
|
||||
<string name="reboot_download">Újraindítás letöltő módba</string>
|
||||
<string name="reboot_edl">Újraindítás EDL-be</string>
|
||||
<string name="about">Névjegy</string>
|
||||
<string name="module_uninstall_confirm">Biztos benne hogy eltávolítja a következő modult: %s?</string>
|
||||
<string name="module_uninstall_confirm">Biztos vagy benne hogy eltávolítod a következő modult: %s\?</string>
|
||||
<string name="module_uninstall_failed">Nem sikerült eltávolítani: %s</string>
|
||||
<string name="module_author">Készítő</string>
|
||||
<string name="module_overlay_fs_not_available">A modulok nem érhetők el, mivel az OverlayFS-t a kernel letiltotta.</string>
|
||||
<string name="refresh">Frissítés</string>
|
||||
<string name="show_system_apps">Rendszeralkalmazások megjelenítése</string>
|
||||
<string name="hide_system_apps">Rendszeralkalmazások elrejtése</string>
|
||||
<string name="module_overlay_fs_not_available">Overlayfs nem elérhető, a modul nem tud enélkül működni!</string>
|
||||
<string name="refresh">Újratöltés</string>
|
||||
<string name="show_system_apps">Mutasd a rendszer alkalmazásokat</string>
|
||||
<string name="hide_system_apps">Rejtsd el a rendszer alkalmazásokat</string>
|
||||
<string name="safe_mode">Biztonságos mód</string>
|
||||
<string name="module_magisk_conflict">A modulok nem érhetők el a Magiskkel való ütközés miatt!</string>
|
||||
<string name="home_learn_kernelsu">Tudjon meg többet a KernelSU-ról</string>
|
||||
<string name="home_click_to_learn_kernelsu">Ismerje meg a KernelSU telepítését és a modulok használatát</string>
|
||||
<string name="home_support_title">Támogasson minket</string>
|
||||
<string name="about_source_code">Tekintse meg a forráskódot a %1$s-on<br/>Csatlakozzon a %2$s csatornánkhoz</string>
|
||||
<string name="module_magisk_conflict">A modul letiltva mert ütközik a Magisk verziójával!</string>
|
||||
<string name="home_learn_kernelsu">Tudj meg többet a KernelSU-ról</string>
|
||||
<string name="home_click_to_learn_kernelsu">Tudd meg hogyan telepítsd a KernelSU-t és használd moduljait</string>
|
||||
<string name="home_support_title">Támogass minket</string>
|
||||
<string name="about_source_code">Tekintsd meg a forráskódot a %1$s-n<br/>Csatlakozz a %2$s csatornánkhoz</string>
|
||||
<string name="profile_default">Alapértelmezett</string>
|
||||
<string name="profile_template">Sablon</string>
|
||||
<string name="profile_custom">Egyedi</string>
|
||||
<string name="profile_name">Profil neve</string>
|
||||
<string name="profile_namespace">Névtér csatlakoztatása</string>
|
||||
<string name="profile_namespace">Mountold a névteret</string>
|
||||
<string name="profile_namespace_inherited">Örökölt</string>
|
||||
<string name="home_learn_kernelsu_url">https://kernelsu.org/guide/what-is-kernelsu.html</string>
|
||||
<string name="profile_namespace_individual">Különálló</string>
|
||||
<string name="profile_groups">Csoportok</string>
|
||||
<string name="profile_capabilities">Jogosultságok</string>
|
||||
<string name="profile_selinux_context">SELinux kontextus</string>
|
||||
<string name="settings_umount_modules_default">Modulok leválasztása alapértelmezetten</string>
|
||||
<string name="profile_umount_modules_summary">Ha engedélyezi ezt az opciót, a KernelSU visszaállíthatja az alkalmazás moduljai által módosított fájlokat.</string>
|
||||
<string name="profile_selinux_context">SElinux kontextus</string>
|
||||
<string name="settings_umount_modules_default">Umountold a modulokat alpértelmezés szerint</string>
|
||||
<string name="profile_umount_modules_summary">Ennek az opciónak az engedélyezése lehetővé teszi, hogy a KernelSU visszaállítsa az alkalmazás moduljai által módosított fájlokat.</string>
|
||||
<string name="profile_selinux_domain">Tartomány</string>
|
||||
<string name="profile_selinux_rules">Szabályok</string>
|
||||
<string name="module_update">Frissítés</string>
|
||||
<string name="module_downloading">Modul letöltése: %s</string>
|
||||
<string name="module_start_downloading">Letöltés indítása: %s</string>
|
||||
<string name="module_downloading">A %s modul letöltése folyamatban</string>
|
||||
<string name="module_start_downloading">Indítsd el a letöltést: %s</string>
|
||||
<string name="launch_app">Indítás</string>
|
||||
<string name="force_stop_app">Kényszerített leállítás</string>
|
||||
<string name="restart_app">újraindítás</string>
|
||||
<string name="home">Kezdőlap</string>
|
||||
<string name="home_not_installed">Nincs telepítve</string>
|
||||
<string name="home_click_to_install">Kattintson a telepítéshez</string>
|
||||
<string name="home_click_to_install">Kattints a telepítéshez</string>
|
||||
<string name="home_superuser_count">Engedélyezett alkalmazások: %d</string>
|
||||
<string name="home_unsupported">Nem támogatott</string>
|
||||
<string name="home_selinux_status">SELinux állapot</string>
|
||||
<string name="selinux_status_enforcing">Kényszerített</string>
|
||||
<string name="selinux_status_permissive">Engedélyezett</string>
|
||||
<string name="home_selinux_status">SELinux státusz</string>
|
||||
<string name="selinux_status_enforcing">Érvényesítés</string>
|
||||
<string name="selinux_status_permissive">Megengedő</string>
|
||||
<string name="selinux_status_unknown">Ismeretlen</string>
|
||||
<string name="superuser">Superuser</string>
|
||||
<string name="superuser">Super user</string>
|
||||
<string name="module_failed_to_enable">Nem sikerült engedélyezni a következő modult: %s</string>
|
||||
<string name="module_failed_to_disable">Nem sikerült letiltani a következő modult: %s</string>
|
||||
<string name="module_empty">Nincs telepített modul</string>
|
||||
<string name="module_failed_to_disable">Nem sikerült letiltani a következő modulokat: %s</string>
|
||||
<string name="module_empty">Nincs modul telepítve</string>
|
||||
<string name="module">Modulok</string>
|
||||
<string name="uninstall">Eltávolítás</string>
|
||||
<string name="module_install">Telepítés</string>
|
||||
<string name="install">Telepítés</string>
|
||||
<string name="reboot">Újraindítás</string>
|
||||
<string name="settings">Beállítások</string>
|
||||
<string name="reboot_userspace">Rendszerfelület újraindítása</string>
|
||||
<string name="reboot_userspace">Android felület újraindítása</string>
|
||||
<string name="reboot_recovery">Újraindítás recovery-módba</string>
|
||||
<string name="reboot_bootloader">Újraindítás bootloader-módba</string>
|
||||
<string name="module_uninstall_success">%s eltávolítva</string>
|
||||
<string name="module_uninstall_success">%s törölve</string>
|
||||
<string name="module_version">Verzió</string>
|
||||
<string name="send_log">Naplók küldése</string>
|
||||
<string name="reboot_to_apply">Indítsa újra a készüléket a változások érvényesítéséhez</string>
|
||||
<string name="home_support_content">A KernelSU ingyenes, nyílt forráskódú és mindig is az lesz. Ön azonban adományozással megmutathatja, hogy törődik a projekttel.</string>
|
||||
<string name="send_log">Napló küldése</string>
|
||||
<string name="reboot_to_apply">Indítsd újra a készüléket hogy érvényesítsd a változást</string>
|
||||
<string name="home_support_content">A KernelSU ingyenes és nyílt forráskódú és mindig is az lesz. Te viszont meg tudod mutatni azt, hogy törődsz ennek a projektnek a sorsával egy adomány formájában.</string>
|
||||
<string name="profile_namespace_global">Globális</string>
|
||||
<string name="profile_umount_modules">Modulok leválasztása</string>
|
||||
<string name="failed_to_update_app_profile">Nem sikerült frissíteni az App Profilt ehhez: %s</string>
|
||||
<string name="settings_umount_modules_default_summary">A \"Modulok leválasztása\" globális alapértelmezett értéke az App Profile-ban. Ha engedélyezve van, eltávolít minden modulmódosítást a rendszerből azon alkalmazások esetében, amelyeknek nincs profilja beállítva.</string>
|
||||
<string name="new_version_available">Elérhető az új, %s verzió, kattintson a frissítéshez.</string>
|
||||
<string name="failed_to_update_sepolicy">Nem sikerült frissíteni az SELinux szabályokat a következőhöz: %s</string>
|
||||
<string name="require_kernel_version">A jelenlegi KernelSU verzió %d túlságosan elavult a megfelelő működéshez. Kérjük frissítsen a %d verzióra vagy újabbra!</string>
|
||||
<string name="profile_umount_modules">Unmountold a modulokat</string>
|
||||
<string name="failed_to_update_app_profile">Nem sikerült frissíteni az App Profilt ehhez %s</string>
|
||||
<string name="settings_umount_modules_default_summary">A „Modulok csatlakoztatása” globális alapértelmezett értéke az alkalmazásprofilokban. Ha engedélyezve van, eltávolítja a rendszer összes modul-módosítását azoknál az alkalmazásoknál, amelyeknek nincs beállított profilja.</string>
|
||||
<string name="new_version_available">Új verzió: %s elérhető, kattints a frissítéshez</string>
|
||||
<string name="failed_to_update_sepolicy">Nem sikerült frissíteni a SELinux szabályait a következőhöz: %s</string>
|
||||
<string name="require_kernel_version">A jelenlegi KernelSU verzió %d túlságosan elavult. Kérlek frissíts a %d verzióra vagy újabbra!</string>
|
||||
<string name="app_profile_template_import_success">Sikeresen importálva</string>
|
||||
<string name="app_profile_export_to_clipboard">Exportálás a vágólapról</string>
|
||||
<string name="app_profile_template_export_empty">Nem található helyi sablon az exportáláshoz!</string>
|
||||
<string name="app_profile_export_to_clipboard">Exportálás a vágólapból</string>
|
||||
<string name="app_profile_template_export_empty">A helyi sablon nem található az exportáláshoz!</string>
|
||||
<string name="app_profile_template_id_exist">A sablon ID már létezik!</string>
|
||||
<string name="module_changelog">Változások</string>
|
||||
<string name="app_profile_import_from_clipboard">Importálás a vágólapról</string>
|
||||
<string name="app_profile_import_from_clipboard">Importálás a vágólapból</string>
|
||||
<string name="module_changelog_failed">A változásnapló lekérése nem sikerült: %s</string>
|
||||
<string name="app_profile_template_name">Név</string>
|
||||
<string name="app_profile_template_id_invalid">Hibás sablon ID</string>
|
||||
<string name="app_profile_template_sync">Online sablonok szinkronizálása</string>
|
||||
<string name="app_profile_template_create">Sablon készítése</string>
|
||||
<string name="app_profile_template_readonly">Csak olvasható</string>
|
||||
<string name="app_profile_template_readonly">csak olvasható</string>
|
||||
<string name="app_profile_import_export">Import/Export</string>
|
||||
<string name="app_profile_template_save_failed">A sablon mentése sikertelen</string>
|
||||
<string name="app_profile_template_edit">Sablon szerkesztése</string>
|
||||
<string name="app_profile_template_id">ID</string>
|
||||
<string name="settings_profile_template">App Profile sablon</string>
|
||||
<string name="app_profile_template_id">id</string>
|
||||
<string name="settings_profile_template">Alkalmazásprofil sablon</string>
|
||||
<string name="app_profile_template_description">Leírás</string>
|
||||
<string name="app_profile_template_save">Mentés</string>
|
||||
<string name="settings_profile_template_summary">Az App Profile helyi és online sablonjának kezelése</string>
|
||||
<string name="settings_profile_template_summary">Az alkalmazásprofil helyi és online sablon kezelése</string>
|
||||
<string name="app_profile_template_delete">Törlés</string>
|
||||
<string name="app_profile_template_import_empty">A vágólap üres!</string>
|
||||
<string name="app_profile_template_view">Sablon megtekintése</string>
|
||||
<string name="save_log">Naplók mentése</string>
|
||||
<string name="enable_web_debugging_summary">A WebUI hibakeresésére használható, csak szükség esetén engedélyezze.</string>
|
||||
<string name="enable_web_debugging">WebView hibakeresés engedélyezése</string>
|
||||
<string name="open">Megnyitás</string>
|
||||
<string name="settings_uninstall_permanent">Végleges eltávolítás</string>
|
||||
<string name="select_file_tip">%1$s partíció képfájl ajánlott</string>
|
||||
<string name="select_kmi">KMI kiválasztása</string>
|
||||
<string name="install_next">Következő</string>
|
||||
<string name="settings_uninstall_temporary">Ideiglenes eltávolítás</string>
|
||||
<string name="settings_uninstall_temporary_message">A KernelSU ideiglenes eltávolítása, az eredeti állapot visszaállítása a következő újraindítás után.</string>
|
||||
<string name="settings_uninstall">Eltávolítás</string>
|
||||
<string name="flashing">Telepítés</string>
|
||||
<string name="flash_success">Sikeres telepítés</string>
|
||||
<string name="selected_lkm">Kiválasztott LKM: %s</string>
|
||||
<string name="flash_failed">Sikertelen telepítés</string>
|
||||
<string name="grant_root_failed">A root jog megadása sikertelen!</string>
|
||||
<string name="install_inactive_slot">Telepítés inaktív helyre (OTA után)</string>
|
||||
<string name="select_file">Fájl kiválasztása</string>
|
||||
<string name="settings_uninstall_permanent_message">A KernelSU eltávolítása (root és az összes modul) teljesen és véglegesen.</string>
|
||||
<string name="settings_restore_stock_image">Eredeti képfájl visszaállítása</string>
|
||||
<string name="action">Művelet</string>
|
||||
<string name="direct_install">Közvetlen telepítés (Ajánlott)</string>
|
||||
<string name="install_inactive_slot_warning">Az eszköze **KÉNYSZERÍTETTEN** a jelenleg inaktív helyről fog indulni újraindítás után!\nCsak az OTA befejezése után használja.\nFolytatja?</string>
|
||||
<string name="shrink_sparse_image_message">Átméretezi a sparse képfájlt, ahol a modul található, a tényleges méretére. Vegye figyelembe, hogy ez a modul rendellenes működését okozhatja, ezért kérjük, hogy csak akkor használja, ha szükséges (például biztonsági mentéshez).</string>
|
||||
<string name="settings_restore_stock_image_message">Állítsa vissza a gyári képfájlt (ha létezik biztonsági mentés). Általában OTA előtt használják. Ha a KernelSU-t szeretné eltávolítani, használja a végleges eltávolítás opciót.</string>
|
||||
<string name="settings_check_update">Frissítés ellenőrzése</string>
|
||||
<string name="settings_check_update_summary">Automatikusan keressen frissítéseket az alkalmazás megnyitásakor</string>
|
||||
<string name="log_saved">Mentett naplók</string>
|
||||
<string name="shrink_sparse_image">Sparse képfájl minimalizálása</string>
|
||||
</resources>
|
||||
<string name="app_profile_template_view">Sablon megnézése</string>
|
||||
<string name="save_log">Naplók Mentése</string>
|
||||
</resources>
|
||||
@@ -10,39 +10,39 @@
|
||||
<string name="home_unsupported">Tidak didukung</string>
|
||||
<string name="home_unsupported_reason">KernelSU saat ini hanya mendukung kernel GKI</string>
|
||||
<string name="home_kernel">Kernel</string>
|
||||
<string name="home_manager_version">Versi manager</string>
|
||||
<string name="home_manager_version">Versi Manager</string>
|
||||
<string name="home_fingerprint">Identitas</string>
|
||||
<string name="home_selinux_status">Status SELinux</string>
|
||||
<string name="selinux_status_disabled">Nonaktif</string>
|
||||
<string name="selinux_status_enforcing">Enforcing</string>
|
||||
<string name="selinux_status_permissive">Permisif</string>
|
||||
<string name="selinux_status_unknown">Tidak diketahui</string>
|
||||
<string name="selinux_status_permissive">Permissive</string>
|
||||
<string name="selinux_status_unknown">Unknown</string>
|
||||
<string name="superuser">SuperUser</string>
|
||||
<string name="module_failed_to_enable">Gagal mengaktifkan modul: %s</string>
|
||||
<string name="module_failed_to_disable">Gagal menonaktifkan modul: %s</string>
|
||||
<string name="module_empty">Tidak ada modul yang terpasang</string>
|
||||
<string name="module_empty">Tidak ada modul</string>
|
||||
<string name="module">Modul</string>
|
||||
<string name="uninstall">Hapus</string>
|
||||
<string name="module_install">Instal</string>
|
||||
<string name="install">Instal</string>
|
||||
<string name="reboot">Reboot</string>
|
||||
<string name="settings">Pengaturan</string>
|
||||
<string name="reboot_userspace">Soft Reboot</string>
|
||||
<string name="reboot_recovery">Reboot ke Recovery</string>
|
||||
<string name="reboot_bootloader">Reboot ke Bootloader</string>
|
||||
<string name="reboot_download">Reboot ke Download</string>
|
||||
<string name="reboot_edl">Reboot ke EDL</string>
|
||||
<string name="reboot_userspace">SoftReboot</string>
|
||||
<string name="reboot_recovery">But ke Recovery</string>
|
||||
<string name="reboot_bootloader">But ke Bootloader</string>
|
||||
<string name="reboot_download">But ke Download</string>
|
||||
<string name="reboot_edl">But ke EDL</string>
|
||||
<string name="about">Tentang</string>
|
||||
<string name="module_uninstall_confirm">Yakin menghapus modul %s?</string>
|
||||
<string name="module_uninstall_success">%s berhasil dihapus</string>
|
||||
<string name="module_uninstall_failed">Gagal menghapus: %s</string>
|
||||
<string name="module_version">Versi</string>
|
||||
<string name="module_author">Oleh</string>
|
||||
<string name="module_overlay_fs_not_available">Kernel tidak mendukung OverlayFS, modul tidak akan berfungsi.</string>
|
||||
<string name="module_overlay_fs_not_available">OverlayFS tidak tersedia, modul tidak berfungsi!</string>
|
||||
<string name="refresh">Muat ulang</string>
|
||||
<string name="show_system_apps">Tampilkan aplikasi sistem</string>
|
||||
<string name="hide_system_apps">Sembunyikan aplikasi sistem</string>
|
||||
<string name="send_log">Kirim Log</string>
|
||||
<string name="send_log">Laporkan Log</string>
|
||||
<string name="safe_mode">Mode aman</string>
|
||||
<string name="reboot_to_apply">Reboot agar berfungsi</string>
|
||||
<string name="module_magisk_conflict">Konflik dengan Magisk, fungsi modul ditiadakan!</string>
|
||||
@@ -51,7 +51,7 @@
|
||||
<string name="home_click_to_learn_kernelsu">Pelajari cara instal KernelSU dan menggunakan modul</string>
|
||||
<string name="home_support_title">Dukung Kami</string>
|
||||
<string name="home_support_content">KernelSU akan selalu menjadi aplikasi gratis dan terbuka. Anda dapat memberikan donasi sebagai bentuk dukungan.</string>
|
||||
<string name="about_source_code"><![CDATA[Lihat kode sumber di %1$s<br/>Gabung kanal %2$s kami]]></string>
|
||||
<string name="about_source_code">Lihat kode sumber di %1$s<br/>Gabung kanal %2$s kami</string>
|
||||
<string name="profile">Profil Apl</string>
|
||||
<string name="profile_default">Bawaan</string>
|
||||
<string name="profile_template">Templat</string>
|
||||
@@ -67,16 +67,16 @@
|
||||
<string name="profile_umount_modules">Umount Modul</string>
|
||||
<string name="failed_to_update_app_profile">Gagal membarui Profil pada %s</string>
|
||||
<string name="settings_umount_modules_default">Melepas Modul secara bawaan</string>
|
||||
<string name="settings_umount_modules_default_summary">Menggunakan \"Umount Modul\" secara universal pada Profil Aplikasi. Jika diaktifkan, akan menghapus semua modifikasi sistem untuk aplikasi yang tidak memiliki set profil.</string>
|
||||
<string name="settings_umount_modules_default_summary">Menggunakan \"Umount Modul\" secara universal pada Profil aplikasi. Jika diaktifkan, akan menghapus semua modifikasi sistem untuk aplikasi yang tidak memiliki set Profil.</string>
|
||||
<string name="profile_umount_modules_summary">Aktifkan opsi ini agar KernelSU dapat memulihkan kembali berkas termodifikasi oleh modul pada aplikasi ini.</string>
|
||||
<string name="profile_selinux_domain">Domain</string>
|
||||
<string name="profile_selinux_rules">Aturan</string>
|
||||
<string name="module_update">Membarui</string>
|
||||
<string name="module_downloading">Mengunduh modul: %s</string>
|
||||
<string name="module_start_downloading">Mulai mengunduh: %s</string>
|
||||
<string name="new_version_available">Tersedia versi terbaru %s, Klik untuk membarui.</string>
|
||||
<string name="new_version_available">Tersedia versi terbaru: %s, Klik untuk membarui</string>
|
||||
<string name="launch_app">Jalankan</string>
|
||||
<string name="force_stop_app">Paksa berhenti</string>
|
||||
<string name="force_stop_app">Paksa Berhenti</string>
|
||||
<string name="restart_app">Mulai ulang</string>
|
||||
<string name="failed_to_update_sepolicy">Gagal membarui aturan SELinux pada: %s</string>
|
||||
<string name="require_kernel_version">Versi KernelSU %d terlalu rendah agar manajer berfungsi normal. Harap membarui ke versi %d atau di atasnya!</string>
|
||||
@@ -84,56 +84,52 @@
|
||||
<string name="app_profile_template_import_success">Berhasil diimpor</string>
|
||||
<string name="app_profile_export_to_clipboard">Ekspor ke papan klip</string>
|
||||
<string name="app_profile_template_export_empty">Tidak ditemukan templat lokal untuk diekspor!</string>
|
||||
<string name="app_profile_template_id_exist">ID templat sudah ada!</string>
|
||||
<string name="app_profile_template_id_exist">Id templat sudah ada!</string>
|
||||
<string name="app_profile_import_from_clipboard">Impor dari papan klip</string>
|
||||
<string name="module_changelog_failed">Gagal mengambil Changelog: %s</string>
|
||||
<string name="app_profile_template_name">Nama</string>
|
||||
<string name="app_profile_template_id_invalid">ID template tidak valid</string>
|
||||
<string name="app_profile_template_id_invalid">Id templat tidak valid</string>
|
||||
<string name="app_profile_template_sync">Sinkronkan templat daring</string>
|
||||
<string name="app_profile_template_create">Buat templat</string>
|
||||
<string name="app_profile_template_create">Buat Templat</string>
|
||||
<string name="app_profile_import_export">Impor/Ekspor</string>
|
||||
<string name="app_profile_template_save_failed">Gagal menyimpan templat</string>
|
||||
<string name="app_profile_template_edit">Edit templat</string>
|
||||
<string name="app_profile_template_id">ID</string>
|
||||
<string name="app_profile_template_edit">Edit Templat</string>
|
||||
<string name="app_profile_template_id">id</string>
|
||||
<string name="settings_profile_template">Templat Profil Aplikasi</string>
|
||||
<string name="app_profile_template_description">Deskripsi</string>
|
||||
<string name="app_profile_template_save">Simpan</string>
|
||||
<string name="settings_profile_template_summary">Atur templat Profil yang lokal dan daring</string>
|
||||
<string name="app_profile_template_delete">Hapus</string>
|
||||
<string name="app_profile_template_import_empty">Papan klip kosong!</string>
|
||||
<string name="app_profile_template_view">Lihat templat</string>
|
||||
<string name="app_profile_template_readonly">readonly</string>
|
||||
<string name="app_profile_template_view">Lihat Templat</string>
|
||||
<string name="app_profile_template_readonly">ReadOnly</string>
|
||||
<string name="enable_web_debugging">Pengawakutuan WebView</string>
|
||||
<string name="enable_web_debugging_summary">Dapat digunakan untuk men-debug WebUI. Harap aktifkan hanya bila diperlukan.</string>
|
||||
<string name="enable_web_debugging_summary">Dapat mengawakutu WebView, hanya aktifkan jika butuh.</string>
|
||||
<string name="select_file_tip">%1$s image partisi terekomendasi</string>
|
||||
<string name="select_kmi">Pilih KMI</string>
|
||||
<string name="install_next">Selanjutnya</string>
|
||||
<string name="install_inactive_slot_warning">Gawai akan **DIPAKSA** untuk but ke slot nonaktif!
|
||||
\nHANYA gunakan setelah proses OTA selesai.
|
||||
\nLanjutkan?</string>
|
||||
<string name="direct_install">Instal langsung (rekomendasi)</string>
|
||||
<string name="direct_install">Instal Langsung (rekomendasi)</string>
|
||||
<string name="select_file">Pilih berkas</string>
|
||||
<string name="install_inactive_slot">Instal ke slot nonaktif (setelah OTA)</string>
|
||||
<string name="grant_root_failed">Gagal memberikan akses root!</string>
|
||||
<string name="open">Buka</string>
|
||||
<string name="settings_check_update">Cek terbaru</string>
|
||||
<string name="settings_check_update_summary">Cek terbaru setiap membuka aplikasi</string>
|
||||
<string name="shrink_sparse_image">Minimalkan ukuran sparse image</string>
|
||||
<string name="shrink_sparse_image_message">Mengembalikan ukuran sparse image, lokasi modul disimpan, ke ukuran sebenarnya. Dapat menyebabkan modul bekerja abnormal, gunakan saat dibutuhkan saja (mis. untuk pencadangan).</string>
|
||||
<string name="shrink_sparse_image">Meminimalkan sparse image</string>
|
||||
<string name="shrink_sparse_image_message">Mengembalikan sparse image, lokasi modul disimpan, ke ukuran sebenarnya. Dapat menyebabkan modul bekerja abnormal, maka gunakan saat dibutuhkan saja (mis. untuk pencadangan)</string>
|
||||
<string name="settings_uninstall_permanent_message">Hapus permanen KernelSU (root dan modul).</string>
|
||||
<string name="settings_uninstall_temporary">Hapus sementara</string>
|
||||
<string name="settings_restore_stock_image">Pulihkan image bawaan</string>
|
||||
<string name="settings_uninstall_temporary">Hapus Temporer</string>
|
||||
<string name="settings_restore_stock_image">Pulihkan Image Asal</string>
|
||||
<string name="settings_uninstall">Hapus</string>
|
||||
<string name="settings_uninstall_temporary_message">Sementara menghapus KernelSU, memulihkan ke kondisi asal setelah reboot berikutnya.</string>
|
||||
<string name="settings_uninstall_permanent">Hapus permanen</string>
|
||||
<string name="settings_restore_stock_image_message">Pulihkan image bawaan ROM (jika cadangan tersedia), umumnya dilakukan sebelum OTA; jika ingin menghapus KernelSU, gunakan fungsi \"Hapus permanen\".</string>
|
||||
<string name="settings_uninstall_temporary_message">Hapus Temporer KernelSU, pulihkan ke kondisi asali setelah but berikutnya.</string>
|
||||
<string name="settings_uninstall_permanent">Hapus Permanen</string>
|
||||
<string name="settings_restore_stock_image_message">Pulihkan image bawaan ROM (jika cadangan tersedia), umumnya dilakukan sebelum OTA; jika ingin menghapus KernelSU, gunakan fungsi \"Hapus Permanen\".</string>
|
||||
<string name="flash_success">Pemasangan Berhasil</string>
|
||||
<string name="selected_lkm">LKM dipilih: %s</string>
|
||||
<string name="flashing">Pasang</string>
|
||||
<string name="flash_failed">Pemasangan Gagal</string>
|
||||
<string name="save_log">Simpan Log</string>
|
||||
<string name="action">Action</string>
|
||||
<string name="log_saved">Log disimpan</string>
|
||||
<string name="module_sort_enabled_first">Urut (Diaktifkan terlebih dahulu)</string>
|
||||
<string name="module_sort_action_first">Urut (Tindakan pertama)</string>
|
||||
</resources>
|
||||
</resources>
|
||||
@@ -45,7 +45,7 @@
|
||||
<string name="send_log">Invia log</string>
|
||||
<string name="safe_mode">Modalità provvisoria</string>
|
||||
<string name="reboot_to_apply">Riavvia per applicare la modifica</string>
|
||||
<string name="module_magisk_conflict">I moduli sono disabilitati perché in conflitto con Magisk!</string>
|
||||
<string name="module_magisk_conflict">I moduli sono disabilitati perché in conflitto con quelli di Magisk!</string>
|
||||
<string name="home_learn_kernelsu">Scopri KernelSU</string>
|
||||
<string name="home_learn_kernelsu_url">https://kernelsu.org/guide/what-is-kernelsu.html</string>
|
||||
<string name="home_click_to_learn_kernelsu">Scopri come installare KernelSU e utilizzare i moduli</string>
|
||||
@@ -82,16 +82,16 @@
|
||||
<string name="module_changelog">Registro aggiornamenti</string>
|
||||
<string name="app_profile_template_create">Crea modello</string>
|
||||
<string name="app_profile_template_edit">Modifica modello</string>
|
||||
<string name="app_profile_template_id">identificatore</string>
|
||||
<string name="app_profile_template_id">identificativo</string>
|
||||
<string name="app_profile_template_id_invalid">Identificativo modello non valido</string>
|
||||
<string name="app_profile_template_name">Nome</string>
|
||||
<string name="app_profile_template_view">Visualizza modello</string>
|
||||
<string name="app_profile_template_readonly">Sola lettura</string>
|
||||
<string name="app_profile_template_id_exist">L\'identificatore del modello è già in uso!</string>
|
||||
<string name="app_profile_template_id_exist">L\'identificativo del modello esiste già!</string>
|
||||
<string name="app_profile_import_export">Importa/Esporta</string>
|
||||
<string name="app_profile_import_from_clipboard">Importa dagli appunti</string>
|
||||
<string name="app_profile_export_to_clipboard">Esporta negli appunti</string>
|
||||
<string name="app_profile_template_export_empty">Impossibile trovare un modello locale da esportare!</string>
|
||||
<string name="app_profile_template_export_empty">Impossibile trovare modello locale da esportare!</string>
|
||||
<string name="app_profile_template_import_success">Importato con successo</string>
|
||||
<string name="app_profile_template_sync">Sincronizza i modelli remoti</string>
|
||||
<string name="app_profile_template_import_empty">Gli appunti sono vuoti!</string>
|
||||
@@ -106,29 +106,29 @@
|
||||
<string name="module_changelog_failed">Impossibile reperire il changelog: %s</string>
|
||||
<string name="settings_check_update">Controlla aggiornamenti</string>
|
||||
<string name="settings_check_update_summary">Controlla automaticamente la disponibilità di aggiornamenti all\'apertura dell\'applicazione</string>
|
||||
<string name="enable_web_debugging">Abilita il debug di WebView</string>
|
||||
<string name="enable_web_debugging">Abilita il Debug di WebView</string>
|
||||
<string name="enable_web_debugging_summary">Può essere usato per svolgere il debug di WebUI, è consigliato attivarlo solo quando necessario.</string>
|
||||
<string name="select_file_tip">È consigliato usare immagine della partizione %1$s</string>
|
||||
<string name="select_kmi">Scegli il KMI</string>
|
||||
<string name="install_next">Avanti</string>
|
||||
<string name="direct_install">Installazione diretta (Raccomandata)</string>
|
||||
<string name="select_file">Scegli un file</string>
|
||||
<string name="install_inactive_slot">Installa nello slot inattivo (dopo OTA)</string>
|
||||
<string name="select_file">Scegli un File</string>
|
||||
<string name="install_inactive_slot">Installa nello Slot Inattivo (Dopo OTA)</string>
|
||||
<string name="install_inactive_slot_warning">Il tuo dispositivo sarà **FORZATO** ad avviarsi nello slot inattivo dopo il riavvio!
|
||||
\nUsa questa opzione solo quando l\'applicazione dell\'aggiornamento OTA è terminata.
|
||||
\nProcedere?</string>
|
||||
<string name="shrink_sparse_image">Riduci la dimensione dell\'immagine moduli sparse al minimo</string>
|
||||
<string name="shrink_sparse_image_message">Riduci la dimensione dell\'immagine sparse dei moduli alla sua reale dimensione. Nota che questo potrebbe causare malfunzionamenti dei moduli quindi utilizzala solo quando necessario (ad esempio in caso di backup)</string>
|
||||
<string name="shrink_sparse_image_message">Riduci la dimensione dell\'immagine sparse dei moduli alla sua reale dimenzione. Nota che questo potrebbe causare malfunzionamenti dei moduli quindi utilizzala solo quando necessario (ad esempio in caso di backup)</string>
|
||||
<string name="settings_uninstall">Disinstalla</string>
|
||||
<string name="settings_uninstall_temporary">Disinstalla temporaneamente</string>
|
||||
<string name="settings_uninstall_permanent">Disinstalla permanentemente</string>
|
||||
<string name="settings_uninstall_temporary">Disinstalla Temporaneamente</string>
|
||||
<string name="settings_uninstall_permanent">Disinstalla Permanentemente</string>
|
||||
<string name="settings_restore_stock_image">Ripristina immagine originale del produttore</string>
|
||||
<string name="settings_uninstall_temporary_message">Disinstalla temporaneamente KernelSU, ripristina lo stato originale dopo il prossimo riavvio.</string>
|
||||
<string name="settings_uninstall_permanent_message">Disinstalla KernelSU (root e tutti i moduli) completamente e permanentemente.</string>
|
||||
<string name="settings_uninstall_permanent_message">Disinstalla KernelSU (Root e tutti i moduli) completamente e permanentemente.</string>
|
||||
<string name="flashing">Installazione</string>
|
||||
<string name="flash_success">Installazione completata</string>
|
||||
<string name="flash_failed">Installazione fallita</string>
|
||||
<string name="selected_lkm">LKM selezionato: %s</string>
|
||||
<string name="settings_restore_stock_image_message">Ripristina l\'immagine di fabbrica del produttore (se il backup è presente), solitamente usato prima di applicare l\'OTA; se devi disinstallare KernelSU, utilizza invece \"Disinstalla permanentemente\".</string>
|
||||
<string name="settings_restore_stock_image_message">Ripristina l\'immagine di fabbrica del produttore (se il backup è presente), solitamente usato prima di applicare l\'OTA; se devi disinstallare KernelSU, utilizza invece \"Disinstalla Permanentemente\".</string>
|
||||
<string name="save_log">Salva Registri</string>
|
||||
</resources>
|
||||
</resources>
|
||||
@@ -81,4 +81,4 @@
|
||||
<string name="home_manager_version">גרסת מנהל</string>
|
||||
<string name="new_version_available">גרסה חדשה עבור: %s זמינה, לחץ כדי לשדרג</string>
|
||||
<string name="save_log">שמור יומנים</string>
|
||||
</resources>
|
||||
</resources>
|
||||
@@ -18,9 +18,9 @@
|
||||
<string name="selinux_status_permissive">Permissive</string>
|
||||
<string name="selinux_status_unknown">不明</string>
|
||||
<string name="superuser">スーパーユーザー</string>
|
||||
<string name="module_failed_to_enable">モジュールの有効化に失敗しました: %s</string>
|
||||
<string name="module_failed_to_disable">モジュールの無効化に失敗しました: %s</string>
|
||||
<string name="module_empty">モジュールがインストールされていません</string>
|
||||
<string name="module_failed_to_enable">モジュールの有効化に失敗: %s</string>
|
||||
<string name="module_failed_to_disable">モジュールの無効化に失敗: %s</string>
|
||||
<string name="module_empty">モジュールをインストールしていません</string>
|
||||
<string name="module">モジュール</string>
|
||||
<string name="uninstall">アンインストール</string>
|
||||
<string name="module_install">インストール</string>
|
||||
@@ -35,22 +35,22 @@
|
||||
<string name="about">アプリについて</string>
|
||||
<string name="module_uninstall_confirm">モジュール %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_author">制作者</string>
|
||||
<string name="module_overlay_fs_not_available">カーネルによって OverlayFS が無効になっているため、モジュールが利用できません!</string>
|
||||
<string name="module_overlay_fs_not_available">OverlayFS が有効でないためモジュールは動作しません!</string>
|
||||
<string name="refresh">更新</string>
|
||||
<string name="show_system_apps">システムアプリを表示</string>
|
||||
<string name="hide_system_apps">システムアプリを非表示</string>
|
||||
<string name="send_log">ログを送信</string>
|
||||
<string name="safe_mode">セーフモード</string>
|
||||
<string name="reboot_to_apply">再起動すると有効化されます</string>
|
||||
<string name="module_magisk_conflict">モジュールが Magisk との競合により利用できません!</string>
|
||||
<string name="module_magisk_conflict">Magisk と競合しているためモジュールは無効になっています!</string>
|
||||
<string name="home_learn_kernelsu">KernelSU について</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_support_title">支援する</string>
|
||||
<string name="home_support_content">KernelSU はこれからもずっと無料でオープンソースです。寄付をして頂くことで、開発を支援していただけます。</string>
|
||||
<string name="home_support_content">KernelSU はこれからもずっとフリーでオープンソースです。寄付をすることで私たちを気にかけていることを示せます。</string>
|
||||
<string name="about_source_code"><![CDATA[%1$s でソースコードを表示<br/>%2$s チャンネルに参加]]></string>
|
||||
<string name="profile">アプリのプロファイル</string>
|
||||
<string name="profile_default">既定</string>
|
||||
@@ -67,13 +67,13 @@
|
||||
<string name="failed_to_update_app_profile">%s のアプリのプロファイルの更新をできませでした</string>
|
||||
<string name="profile_selinux_domain">ドメイン</string>
|
||||
<string name="profile_selinux_rules">ルール</string>
|
||||
<string name="new_version_available">新しいバージョン %s が利用可能です。タップしてダウンロード。</string>
|
||||
<string name="new_version_available">新しいバージョン: %s が利用可能です。タップしてダウンロード</string>
|
||||
<string name="module_update">アップデート</string>
|
||||
<string name="module_start_downloading">ダウンロードを開始: %s</string>
|
||||
<string name="launch_app">起動</string>
|
||||
<string name="force_stop_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>
|
||||
@@ -84,18 +84,18 @@
|
||||
<string name="app_profile_template_import_success">インポート成功</string>
|
||||
<string name="app_profile_export_to_clipboard">クリップボードからエクスポート</string>
|
||||
<string name="app_profile_template_export_empty">エクスポートするローカル テンプレートが見つかりません!</string>
|
||||
<string name="app_profile_template_id_exist">テンプレート ID はすでに存在します!</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_id_invalid">無効なテンプレート id</string>
|
||||
<string name="app_profile_template_sync">オンラインテンプレートの同期</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_id">ID</string>
|
||||
<string name="app_profile_template_id">id</string>
|
||||
<string name="settings_profile_template">アプリプロファイルのテンプレート</string>
|
||||
<string name="app_profile_template_description">説明</string>
|
||||
<string name="app_profile_template_save">保存</string>
|
||||
@@ -119,7 +119,7 @@
|
||||
<string name="direct_install">直接インストール (推奨)</string>
|
||||
<string name="select_file">ファイルを選択してください</string>
|
||||
<string name="shrink_sparse_image">スパースイメージを最小化</string>
|
||||
<string name="shrink_sparse_image_message">モジュールが配置されているスパースイメージのサイズを実際のサイズに変更します。 モジュールが正常に動作しなくなる可能性がありますので、必要な場合にのみご使用ください。</string>
|
||||
<string name="shrink_sparse_image_message">モジュールが配置されているスパースイメージのサイズを実際のサイズに変更します。 モジュールが正常に動作しなくなる可能性がありますので、必要な場合にのみご使用ください</string>
|
||||
<string name="settings_uninstall_permanent">完全にアンインストールする</string>
|
||||
<string name="settings_restore_stock_image">ストックイメージを復元</string>
|
||||
<string name="settings_uninstall_temporary">一時的にアンインストールする</string>
|
||||
@@ -130,15 +130,6 @@
|
||||
<string name="flashing">フラッシュ</string>
|
||||
<string name="flash_success">フラッシュ成功</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="action">アクション</string>
|
||||
<string name="log_saved">保存されたログ</string>
|
||||
<string name="module_sort_enabled_first">並べ替え(最初に有効)</string>
|
||||
<string name="module_sort_action_first">並べ替え(アクション優先)</string>
|
||||
<string name="settings_disable_su_summary">su コマンドを使用してアプリがルート権限を取得する機能を一時的に無効にします (既存のルート プロセスは影響を受けません)。</string>
|
||||
<string name="su_not_allowed">%s にスーパーユーザーアクセスを許可できませんでした</string>
|
||||
<string name="settings_disable_su">su互換性を無効にする</string>
|
||||
<string name="module_install_prompt_with_name">次のモジュールがインストールされます: %1$s</string>
|
||||
<string name="confirm">確認</string>
|
||||
</resources>
|
||||
</resources>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user