diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 000000000000..d4f07b614a0e --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,5 @@ +blank_issues_enabled: false +contact_links: + - name: Feature Request + url: https://github.com/tiann/KernelSU/issues/1705 + about: "We do not accept external Feature Requests, see this link for more details." diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml deleted file mode 100644 index f094b0234e5e..000000000000 --- a/.github/ISSUE_TEMPLATE/feature_request.yml +++ /dev/null @@ -1,39 +0,0 @@ -name: Feature Request -description: "Suggest an idea for this project" -title: "[Feature]" -labels: "feature" -body: - - type: markdown - id: feature-info - attributes: - value: "## Feature Infomation" - - type: textarea - id: feature-main - validations: - required: true - attributes: - label: "Is your feature request related to a problem? Please describe." - description: "A clear and concise description of what the problem is." - placeholder: "I'm always frustrated when [...]" - - type: textarea - id: feature-solution - validations: - required: true - attributes: - label: "Describe the solution you'd like." - description: "A clear and concise description of what you want to happen." - - type: textarea - id: feature-describe - validations: - required: true - attributes: - label: "Describe alternatives you've considered." - description: "A clear and concise description of any alternative solutions or features you've considered." - - type: textarea - id: feature-extra - validations: - required: false - attributes: - label: "Additional context" - description: "Add any other context or screenshots about the feature request here." - diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 018b1c009ce3..95e3839ef7ad 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -4,18 +4,35 @@ updates: directory: / schedule: interval: daily - + groups: + actions: + patterns: + - "*" - package-ecosystem: cargo directory: userspace/ksud schedule: interval: daily - + allow: + - dependency-type: "all" + groups: + crates: + patterns: + - "*" - package-ecosystem: gradle directory: manager schedule: interval: daily - + groups: + maven: + patterns: + - "*" - package-ecosystem: npm directory: website schedule: interval: daily + allow: + - dependency-type: "all" + groups: + npm: + patterns: + - "*" diff --git a/.github/workflows/add-device.yml b/.github/workflows/add-device.yml index 0b3a01a7ab11..c98c2ac4f9b9 100644 --- a/.github/workflows/add-device.yml +++ b/.github/workflows/add-device.yml @@ -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@v6 + uses: peter-evans/create-pull-request@v7 with: token: ${{ secrets.GITHUB_TOKEN }} commit-message: "[add device]: ${{ steps.handle-add-device.outputs.device }}" @@ -37,6 +37,7 @@ 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: | diff --git a/.github/workflows/build-debug-kernel.yml b/.github/workflows/build-debug-kernel.yml index a0cab91b2238..5ddb4b3d3014 100644 --- a/.github/workflows/build-debug-kernel.yml +++ b/.github/workflows/build-debug-kernel.yml @@ -7,9 +7,9 @@ jobs: uses: ./.github/workflows/gki-kernel.yml with: version: android12-5.10 - version_name: android12-5.10.209 - tag: android12-5.10-2024-05 - os_patch_level: 2024-05 + version_name: android12-5.10.218 + tag: android12-5.10-2024-08 + os_patch_level: 2024-08 patch_path: "5.10" debug: true build-debug-kernel-a13: @@ -17,11 +17,11 @@ jobs: matrix: include: - version: "5.10" - sub_level: 209 - os_patch_level: 2024-05 + sub_level: 218 + os_patch_level: 2024-08 - version: "5.15" - sub_level: 148 - os_patch_level: 2024-05 + sub_level: 151 + os_patch_level: 2024-08 uses: ./.github/workflows/gki-kernel.yml with: version: android13-${{ matrix.version }} @@ -34,15 +34,29 @@ jobs: matrix: include: - version: "5.15" - sub_level: 148 - os_patch_level: 2024-05 + sub_level: 158 + os_patch_level: 2024-08 - version: "6.1" - sub_level: 75 - os_patch_level: 2024-05 + sub_level: 90 + os_patch_level: 2024-08 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: 30 + os_patch_level: 2024-08 + 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 \ No newline at end of file diff --git a/.github/workflows/build-kernel-a12.yml b/.github/workflows/build-kernel-a12.yml index 89f9c984dc36..5c1411009567 100644 --- a/.github/workflows/build-kernel-a12.yml +++ b/.github/workflows/build-kernel-a12.yml @@ -27,6 +27,8 @@ jobs: os_patch_level: 2024-03 - sub_level: 209 os_patch_level: 2024-05 + - sub_level: 218 + os_patch_level: 2024-08 uses: ./.github/workflows/gki-kernel.yml secrets: inherit with: @@ -63,7 +65,7 @@ jobs: - name: Download prebuilt toolchain run: | AOSP_MIRROR=https://android.googlesource.com - BRANCH=main-kernel-build-2023 + BRANCH=main-kernel-build-2024 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 @@ -112,7 +114,7 @@ jobs: uses: ./.github/workflows/gki-kernel.yml with: version: android12-5.10 - version_name: android12-5.10.209 - tag: android12-5.10-2024-05 - os_patch_level: 2024-05 + version_name: android12-5.10.218 + tag: android12-5.10-2024-08 + os_patch_level: 2024-08 patch_path: "5.10" \ No newline at end of file diff --git a/.github/workflows/build-kernel-a13.yml b/.github/workflows/build-kernel-a13.yml index 9390b48e7f6f..9c2f335df9b7 100644 --- a/.github/workflows/build-kernel-a13.yml +++ b/.github/workflows/build-kernel-a13.yml @@ -33,6 +33,15 @@ jobs: - 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.15" sub_level: 123 os_patch_level: 2023-11 @@ -45,6 +54,12 @@ jobs: - 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 uses: ./.github/workflows/gki-kernel.yml secrets: inherit with: @@ -81,7 +96,7 @@ jobs: - name: Download prebuilt toolchain run: | AOSP_MIRROR=https://android.googlesource.com - BRANCH=main-kernel-build-2023 + BRANCH=main-kernel-build-2024 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 @@ -131,11 +146,11 @@ jobs: matrix: include: - version: "5.10" - sub_level: 209 - os_patch_level: 2024-05 + sub_level: 218 + os_patch_level: 2024-08 - version: "5.15" - sub_level: 148 - os_patch_level: 2024-05 + sub_level: 151 + os_patch_level: 2024-08 uses: ./.github/workflows/gki-kernel.yml with: version: android13-${{ matrix.version }} diff --git a/.github/workflows/build-kernel-a14.yml b/.github/workflows/build-kernel-a14.yml index a06f02b1f732..4d8a97b8a9ee 100644 --- a/.github/workflows/build-kernel-a14.yml +++ b/.github/workflows/build-kernel-a14.yml @@ -33,6 +33,15 @@ jobs: - 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: "6.1" sub_level: 25 os_patch_level: 2023-10 @@ -48,6 +57,15 @@ jobs: - 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 uses: ./.github/workflows/gki-kernel.yml secrets: inherit with: @@ -84,7 +102,7 @@ jobs: - name: Download prebuilt toolchain run: | AOSP_MIRROR=https://android.googlesource.com - BRANCH=main-kernel-build-2023 + BRANCH=main-kernel-build-2024 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 @@ -134,11 +152,11 @@ jobs: matrix: include: - version: "5.15" - sub_level: 148 - os_patch_level: 2024-05 + sub_level: 158 + os_patch_level: 2024-08 - version: "6.1" - sub_level: 75 - os_patch_level: 2024-05 + sub_level: 90 + os_patch_level: 2024-08 uses: ./.github/workflows/gki-kernel.yml with: version: android14-${{ matrix.version }} diff --git a/.github/workflows/build-kernel-a15.yml b/.github/workflows/build-kernel-a15.yml new file mode 100644 index 000000000000..b59e8b9369fe --- /dev/null +++ b/.github/workflows/build-kernel-a15.yml @@ -0,0 +1,121 @@ +name: Build Kernel - Android 15 +on: + push: + branches: ["main", "ci", "checkci"] + paths: + - ".github/workflows/build-kernel-a15.yml" + - ".github/workflows/gki-kernel.yml" + - ".github/scripts/build_a13.sh" + - "kernel/**" + pull_request: + branches: ["main"] + paths: + - ".github/workflows/build-kernel-a15.yml" + - ".github/workflows/gki-kernel.yml" + - ".github/scripts/build-a13.sh" + - "kernel/**" + workflow_call: +jobs: + build-kernel: + if: github.event_name != 'pull_request' && github.ref != 'refs/heads/checkci' + strategy: + matrix: + include: + - version: "6.6" + sub_level: 30 + os_patch_level: 2024-08 + uses: ./.github/workflows/gki-kernel.yml + secrets: inherit + 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 }} + + upload-artifacts: + needs: build-kernel + runs-on: ubuntu-latest + if: ${{ ( github.event_name != 'pull_request' && github.ref == 'refs/heads/main' ) || github.ref_type == 'tag' || github.ref == 'refs/heads/ci' }} + env: + CHAT_ID: ${{ secrets.CHAT_ID }} + BOT_TOKEN: ${{ secrets.BOT_TOKEN }} + MESSAGE_THREAD_ID: ${{ secrets.MESSAGE_THREAD_ID }} + COMMIT_MESSAGE: ${{ github.event.head_commit.message }} + COMMIT_URL: ${{ github.event.head_commit.url }} + RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} + steps: + - name: Download artifacts + uses: actions/download-artifact@v4 + + - uses: actions/checkout@v4 + with: + path: KernelSU + fetch-depth: 0 + + - name: List artifacts + run: | + tree + + - name: Download prebuilt toolchain + run: | + AOSP_MIRROR=https://android.googlesource.com + BRANCH=main-kernel-build-2024 + 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 + pip3 install telethon + + - name: Set boot sign key + env: + BOOT_SIGN_KEY: ${{ secrets.BOOT_SIGN_KEY }} + run: | + if [ ! -z "$BOOT_SIGN_KEY" ]; then + echo "$BOOT_SIGN_KEY" > ./kernel-build-tools/linux-x86/share/avb/testkey_rsa2048.pem + fi + + - name: Bot session cache + id: bot_session_cache + uses: actions/cache@v4 + if: false + with: + path: scripts/ksubot.session + key: ${{ runner.os }}-bot-session + + - name: Build boot images + run: | + export AVBTOOL=$GITHUB_WORKSPACE/kernel-build-tools/linux-x86/bin/avbtool + export GZIP=$GITHUB_WORKSPACE/build-tools/path/linux-x86/gzip + export LZ4=$GITHUB_WORKSPACE/build-tools/path/linux-x86/lz4 + export MKBOOTIMG=$GITHUB_WORKSPACE/mkbootimg/mkbootimg.py + export UNPACK_BOOTIMG=$GITHUB_WORKSPACE/mkbootimg/unpack_bootimg.py + cd $GITHUB_WORKSPACE/KernelSU + export VERSION=$(($(git rev-list --count HEAD) + 10200)) + echo "VERSION: $VERSION" + cd - + bash $GITHUB_WORKSPACE/KernelSU/.github/scripts/build_a13.sh + + - name: Display structure of boot files + run: ls -R + + - name: Upload images artifact + uses: actions/upload-artifact@v4 + with: + name: boot-images-android15 + path: Image-android15*/*.img.gz + + check-build-kernel: + if: (github.event_name == 'pull_request' && !github.event.pull_request.draft) || github.ref == 'refs/heads/checkci' + strategy: + matrix: + include: + - version: "6.6" + sub_level: 30 + os_patch_level: 2024-08 + 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 }} \ No newline at end of file diff --git a/.github/workflows/build-kernel-arcvm.yml b/.github/workflows/build-kernel-arcvm.yml index 18c97c2f949d..9dbe8de1a0f6 100644 --- a/.github/workflows/build-kernel-arcvm.yml +++ b/.github/workflows/build-kernel-arcvm.yml @@ -82,7 +82,7 @@ jobs: SUBLEVEL=$(grep -E '^SUBLEVEL = ' Makefile | awk '{print $3}') echo "ChromeOS ARCVM Linux kernel version: $VERSION.$PATCHLEVEL.$SUBLEVEL" echo "version=$VERSION.$PATCHLEVEL.$SUBLEVEL" >> $GITHUB_ENV - + - name: Setup KernelSU working-directory: kernel run: | @@ -94,7 +94,9 @@ jobs: echo "[+] Add KernelSU driver to Makefile" DRIVER_MAKEFILE=$KERNEL_ROOT/drivers/Makefile - grep -q "kernelsu" $DRIVER_MAKEFILE || echo "obj-y += kernelsu/" >> $DRIVER_MAKEFILE + DRIVER_KCONFIG=$KERNEL_ROOT/drivers/Kconfig + grep -q "kernelsu" "$DRIVER_MAKEFILE" || printf "\nobj-\$(CONFIG_KSU) += kernelsu/\n" >> "$DRIVER_MAKEFILE" + grep -q "kernelsu" "$DRIVER_KCONFIG" || sed -i "/endmenu/i\\source \"drivers/kernelsu/Kconfig\"" "$DRIVER_KCONFIG" echo "[+] Apply KernelSU patches" cd $KERNEL_ROOT && git apply $GITHUB_WORKSPACE/KernelSU/.github/patches/5.10/*.patch || echo "[-] No patch found" diff --git a/.github/workflows/build-lkm.yml b/.github/workflows/build-lkm.yml index 5a14ce1b4826..9e70d39be370 100644 --- a/.github/workflows/build-lkm.yml +++ b/.github/workflows/build-lkm.yml @@ -15,20 +15,23 @@ jobs: matrix: include: - version: "android12-5.10" - sub_level: 209 - os_patch_level: 2024-05 + sub_level: 218 + os_patch_level: 2024-08 - version: "android13-5.10" - sub_level: 209 - os_patch_level: 2024-05 + sub_level: 218 + os_patch_level: 2024-08 - version: "android13-5.15" - sub_level: 148 - os_patch_level: 2024-05 + sub_level: 151 + os_patch_level: 2024-08 - version: "android14-5.15" - sub_level: 148 - os_patch_level: 2024-05 + sub_level: 158 + os_patch_level: 2024-08 - version: "android14-6.1" - sub_level: 75 - os_patch_level: 2024-05 + sub_level: 90 + os_patch_level: 2024-08 + - version: "android15-6.6" + sub_level: 30 + os_patch_level: 2024-08 uses: ./.github/workflows/gki-kernel.yml with: version: ${{ matrix.version }} diff --git a/.github/workflows/build-manager.yml b/.github/workflows/build-manager.yml index b5013641c661..89360617be92 100644 --- a/.github/workflows/build-manager.yml +++ b/.github/workflows/build-manager.yml @@ -85,9 +85,10 @@ jobs: java-version: 21 - name: Setup Gradle - uses: gradle/actions/setup-gradle@v3 - with: - gradle-home-cache-cleanup: true + uses: gradle/actions/setup-gradle@v4 + + - name: Setup Android SDK + uses: android-actions/setup-android@v3 - name: Download arm64 ksud uses: actions/download-artifact@v4 @@ -157,4 +158,4 @@ jobs: APK=$(find ./app/build/outputs/apk/release -name "*.apk") pip3 install telethon python3 $GITHUB_WORKSPACE/scripts/ksubot.py $APK - fi \ No newline at end of file + fi diff --git a/.github/workflows/build-su.yml b/.github/workflows/build-su.yml index 2fbb93a9a485..22fd58af4508 100644 --- a/.github/workflows/build-su.yml +++ b/.github/workflows/build-su.yml @@ -26,14 +26,11 @@ jobs: else echo "UPLOAD=false" >> $GITHUB_OUTPUT fi - - uses: nttld/setup-ndk@v1 - with: - ndk-version: r26d - name: Build su working-directory: ./userspace/su - run: ndk-build + run: $ANDROID_NDK/ndk-build - name: Upload a Build Artifact uses: actions/upload-artifact@v4 with: name: su - path: ./userspace/su/libs \ No newline at end of file + path: ./userspace/su/libs diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 26b3171c8e68..dcc0fca8856c 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -18,6 +18,9 @@ jobs: build-a14-kernel: uses: ./.github/workflows/build-kernel-a14.yml secrets: inherit + build-a15-kernel: + uses: ./.github/workflows/build-kernel-a15.yml + secrets: inherit build-wsa-kernel: uses: ./.github/workflows/build-kernel-wsa.yml secrets: inherit diff --git a/docs/README.md b/docs/README.md index 0128cd3d221e..3e99c1e8cd33 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,4 +1,4 @@ -**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) +**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) # KernelSU diff --git a/docs/README_CN.md b/docs/README_CN.md index dc34b86ab139..17e44e942763 100644 --- a/docs/README_CN.md +++ b/docs/README_CN.md @@ -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) +[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) # KernelSU diff --git a/docs/README_ES.md b/docs/README_ES.md index ed4f699b791d..42a4828fa0fa 100644 --- a/docs/README_ES.md +++ b/docs/README_ES.md @@ -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) +[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) # KernelSU diff --git a/docs/README_ID.md b/docs/README_ID.md index 511683a7503d..33bfa733fefb 100644 --- a/docs/README_ID.md +++ b/docs/README_ID.md @@ -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) +[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) # KernelSU diff --git a/docs/README_IN.md b/docs/README_IN.md index 7396ab5c2b10..392a99041613 100644 --- a/docs/README_IN.md +++ b/docs/README_IN.md @@ -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) | **हिंदी** +[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) # KernelSU diff --git a/docs/README_IT.md b/docs/README_IT.md new file mode 100644 index 000000000000..54216b65ff62 --- /dev/null +++ b/docs/README_IT.md @@ -0,0 +1,58 @@ +[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 + +logo + +Una soluzione per il root basata sul kernel per i dispositivi Android. + +[![Latest release](https://img.shields.io/github/v/release/tiann/KernelSU?label=Release&logo=github)](https://github.com/tiann/KernelSU/releases/latest) +[![Weblate](https://img.shields.io/badge/Localization-Weblate-teal?logo=weblate)](https://hosted.weblate.org/engage/kernelsu) +[![Canale Telegraml](https://img.shields.io/badge/Follow-Telegram-blue.svg?logo=telegram)](https://t.me/KernelSU) +[![Licenza componenti kernel: GPL v2](https://img.shields.io/badge/License-GPL%20v2-orange.svg?logo=gnu)](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html) +[![Licenza elementi non kern](https://img.shields.io/github/license/tiann/KernelSU?logo=gnu)](/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. diff --git a/docs/README_IW.md b/docs/README_IW.md index 5997487e2a08..233c3b61ce11 100644 --- a/docs/README_IW.md +++ b/docs/README_IW.md @@ -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) +[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) # KernelSU diff --git a/docs/README_JP.md b/docs/README_JP.md index 093618753775..04ec0cfc3873 100644 --- a/docs/README_JP.md +++ b/docs/README_JP.md @@ -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) +[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) # KernelSU diff --git a/docs/README_KR.md b/docs/README_KR.md index 3226008920a1..ccb8f8ac4dc2 100644 --- a/docs/README_KR.md +++ b/docs/README_KR.md @@ -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) +[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) # KernelSU diff --git a/docs/README_PL.md b/docs/README_PL.md index ab9066296dd9..f5cfc8defc7a 100644 --- a/docs/README_PL.md +++ b/docs/README_PL.md @@ -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) +[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) # KernelSU diff --git a/docs/README_PT-BR.md b/docs/README_PT-BR.md index b5421e7f9be0..c91737c864ae 100644 --- a/docs/README_PT-BR.md +++ b/docs/README_PT-BR.md @@ -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) +[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) # KernelSU diff --git a/docs/README_RU.md b/docs/README_RU.md index 7e50f47adfe5..f3b69643ad17 100644 --- a/docs/README_RU.md +++ b/docs/README_RU.md @@ -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) +[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) # KernelSU diff --git a/docs/README_TR.md b/docs/README_TR.md index bc3363e8b490..223fcadf978a 100644 --- a/docs/README_TR.md +++ b/docs/README_TR.md @@ -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) +[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) # KernelSU diff --git a/docs/README_TW.md b/docs/README_TW.md index d025e1742a67..5ff1d613c3bc 100644 --- a/docs/README_TW.md +++ b/docs/README_TW.md @@ -1,4 +1,4 @@ -[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) +[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) # KernelSU @@ -36,7 +36,7 @@ WSA和ChromeOS和執行在容器中的 Android 也可以與 KernelSU 一同運 若要協助翻譯 KernelSU 或改進現有翻譯,請使用 [Weblate](https://hosted.weblate.org/engage/kernelsu/)。 翻譯管理器的PR不再被接受,因為它會與Weblate衝突。 -### 討論 +## 討論 - Telegram:[@KernelSU](https://t.me/KernelSU) diff --git a/docs/README_VI.md b/docs/README_VI.md index 7dd3b4227aec..665e242b00ce 100644 --- a/docs/README_VI.md +++ b/docs/README_VI.md @@ -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) +[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) # KernelSU diff --git a/js/README.md b/js/README.md index 4af3a7185863..91de2b45b9c6 100644 --- a/js/README.md +++ b/js/README.md @@ -109,3 +109,12 @@ 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()); +``` \ No newline at end of file diff --git a/js/index.d.ts b/js/index.d.ts index b65248e378f7..c9278175c891 100644 --- a/js/index.d.ts +++ b/js/index.d.ts @@ -37,9 +37,12 @@ declare function fullScreen(isFullScreen: boolean); declare function toast(message: string); +declare function moduleInfo(): string; + export { exec, spawn, fullScreen, - toast + toast, + moduleInfo } diff --git a/js/index.js b/js/index.js index 4b64e9c4c455..29b928acd6f1 100644 --- a/js/index.js +++ b/js/index.js @@ -113,3 +113,7 @@ export function fullScreen(isFullScreen) { export function toast(message) { ksu.toast(message); } + +export function moduleInfo() { + return ksu.moduleInfo(); +} diff --git a/js/package.json b/js/package.json index 02b350f33c79..12002a05328e 100644 --- a/js/package.json +++ b/js/package.json @@ -1,6 +1,6 @@ { "name": "kernelsu", - "version": "1.0.6", + "version": "1.0.7", "description": "Library for KernelSU's module WebUI", "main": "index.js", "types": "index.d.ts", diff --git a/kernel/core_hook.c b/kernel/core_hook.c index b63ea0b1d6fd..913867a579c1 100644 --- a/kernel/core_hook.c +++ b/kernel/core_hook.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -193,7 +194,7 @@ int ksu_handle_rename(struct dentry *old_dentry, struct dentry *new_dentry) return 0; } - if (strcmp(buf, "/system/packages.list")) { + if (!strstr(buf, "/system/packages.list")) { return 0; } pr_info("renameat: %s -> %s, new path: %s\n", old_dentry->d_iname, diff --git a/kernel/throne_tracker.c b/kernel/throne_tracker.c index eb04c8b0f344..d7c1dae14a17 100644 --- a/kernel/throne_tracker.c +++ b/kernel/throne_tracker.c @@ -236,13 +236,13 @@ void search_manager(const char *path, int depth, struct list_head *uid_data) file = ksu_filp_open_compat(pos->dirpath, O_RDONLY | O_NOFOLLOW, 0); if (IS_ERR(file)) { pr_err("Failed to open directory: %s, err: %ld\n", pos->dirpath, PTR_ERR(file)); - return; + goto skip_iterate; } iterate_dir(file, &ctx.ctx); filp_close(file, NULL); } - +skip_iterate: list_del(&pos->list); if (pos != &data) kfree(pos); diff --git a/manager/.gitignore b/manager/.gitignore index dd42da43f547..a595ddf709dd 100644 --- a/manager/.gitignore +++ b/manager/.gitignore @@ -1,9 +1,10 @@ *.iml .gradle -local.properties .idea +.kotlin .DS_Store build captures .cxx +local.properties key.jks diff --git a/manager/app/build.gradle.kts b/manager/app/build.gradle.kts index 1c85d8af7e8a..1f98e4a1ba62 100644 --- a/manager/app/build.gradle.kts +++ b/manager/app/build.gradle.kts @@ -1,4 +1,7 @@ +@file:Suppress("UnstableApiUsage") + import com.android.build.gradle.internal.api.BaseVariantOutputImpl +import com.android.build.gradle.tasks.PackageAndroidArtifact plugins { alias(libs.plugins.agp.app) @@ -34,6 +37,7 @@ android { aidl = true buildConfig = true compose = true + prefab = true } kotlinOptions { @@ -45,7 +49,13 @@ android { useLegacyPackaging = true } resources { - excludes += "/META-INF/{AL2.0,LGPL2.1}" + // 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" } } @@ -60,13 +70,26 @@ 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 { + doFirst { appMetadata.asFile.orNull?.writeText("") } + } + + dependenciesInfo { + includeInApk = false + includeInBundle = false + } + + androidResources { + generateLocaleConfig = true + } } dependencies { @@ -87,12 +110,7 @@ dependencies { implementation(libs.androidx.lifecycle.runtime.ktx) implementation(libs.androidx.lifecycle.viewmodel.compose) - 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) + implementation(libs.compose.destinations.core) ksp(libs.compose.destinations.ksp) implementation(libs.com.github.topjohnwu.libsu.core) @@ -113,4 +131,6 @@ dependencies { implementation(libs.markdown) implementation(libs.androidx.webkit) + + implementation(libs.lsposed.cxx) } \ No newline at end of file diff --git a/manager/app/proguard-rules.pro b/manager/app/proguard-rules.pro index dcaf39ce70fe..e69de29bb2d1 100644 --- a/manager/app/proguard-rules.pro +++ b/manager/app/proguard-rules.pro @@ -1,9 +0,0 @@ --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 diff --git a/manager/app/src/main/AndroidManifest.xml b/manager/app/src/main/AndroidManifest.xml index 32c920ab4c3b..11cda5f21cd1 100644 --- a/manager/app/src/main/AndroidManifest.xml +++ b/manager/app/src/main/AndroidManifest.xml @@ -12,8 +12,8 @@ android:fullBackupContent="@xml/backup_rules" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" - android:supportsRtl="true" android:networkSecurityConfig="@xml/network_security_config" + android:supportsRtl="true" android:theme="@style/Theme.KernelSU" tools:targetApi="34"> - - - - = Build.VERSION_CODES.Q) { + window.isNavigationBarContrastEnforced = false + } + super.onCreate(savedInstanceState) setContent { KernelSUTheme { val navController = rememberNavController() - val snackbarHostState = remember { SnackbarHostState() } + val snackBarHostState = remember { SnackbarHostState() } Scaffold( bottomBar = { BottomBar(navController) }, - snackbarHost = { SnackbarHost(snackbarHostState) } + contentWindowInsets = WindowInsets(0, 0, 0, 0) ) { innerPadding -> CompositionLocalProvider( - LocalSnackbarHost provides snackbarHostState, + LocalSnackbarHost provides snackBarHostState, ) { DestinationsNavHost( modifier = Modifier.padding(innerPadding), navGraph = NavGraphs.root, - navController = navController + navController = navController, + defaultTransitions = object : NavHostAnimatedDestinationStyle() { + override val enterTransition: AnimatedContentTransitionScope.() -> EnterTransition + get() = { fadeIn(animationSpec = tween(340)) } + override val exitTransition: AnimatedContentTransitionScope.() -> ExitTransition + get() = { fadeOut(animationSpec = tween(340)) } + } ) } } @@ -61,9 +89,15 @@ 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) { + NavigationBar( + tonalElevation = 8.dp, + windowInsets = WindowInsets.systemBars.union(WindowInsets.displayCutout).only( + WindowInsetsSides.Horizontal + WindowInsetsSides.Bottom + ) + ) { BottomBarDestination.entries.forEach { destination -> if (!fullFeatured && destination.rootRequired) return@forEach val isCurrentDestOnBackStack by navController.isRouteOnBackStackAsState(destination.direction) @@ -71,11 +105,10 @@ private fun BottomBar(navController: NavHostController) { selected = isCurrentDestOnBackStack, onClick = { if (isCurrentDestOnBackStack) { - navController.popBackStack(destination.direction, false) + navigator.popBackStack(destination.direction, false) } - - navController.navigate(destination.direction.route) { - popUpTo(NavGraphs.root.route) { + navigator.navigate(destination.direction) { + popUpTo(NavGraphs.root) { saveState = true } launchSingleTop = true diff --git a/manager/app/src/main/java/me/weishu/kernelsu/ui/component/AboutCard.kt b/manager/app/src/main/java/me/weishu/kernelsu/ui/component/AboutCard.kt index 0807d0521dfe..803447391af3 100644 --- a/manager/app/src/main/java/me/weishu/kernelsu/ui/component/AboutCard.kt +++ b/manager/app/src/main/java/me/weishu/kernelsu/ui/component/AboutCard.kt @@ -1,7 +1,5 @@ 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 @@ -11,24 +9,28 @@ 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.graphics.toArgb -import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.draw.scale +import androidx.compose.ui.res.colorResource +import androidx.compose.ui.res.painterResource 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 @@ -36,9 +38,8 @@ 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 @@ -52,7 +53,9 @@ fun AboutCard() { @Composable fun AboutDialog(dismiss: () -> Unit) { - Dialog(onDismissRequest = { dismiss() }) { + Dialog( + onDismissRequest = { dismiss() } + ) { AboutCard() } } @@ -60,21 +63,20 @@ 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 { - Image( - painter = rememberDrawablePainter(drawable), - contentDescription = "icon", - modifier = Modifier.size(40.dp) - ) + 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) + ) + } Spacer(modifier = Modifier.width(12.dp)) @@ -93,31 +95,31 @@ private fun AboutCardContent() { Spacer(modifier = Modifier.height(8.dp)) - HtmlText( - html = stringResource( + val annotatedString = AnnotatedString.Companion.fromHtml( + htmlString = stringResource( id = R.string.about_source_code, "GitHub", "Telegram" + ), + 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()) - } - ) } \ No newline at end of file diff --git a/manager/app/src/main/java/me/weishu/kernelsu/ui/component/Dialog.kt b/manager/app/src/main/java/me/weishu/kernelsu/ui/component/Dialog.kt index e2c3fa453e0d..27adc3f03ccc 100644 --- a/manager/app/src/main/java/me/weishu/kernelsu/ui/component/Dialog.kt +++ b/manager/app/src/main/java/me/weishu/kernelsu/ui/component/Dialog.kt @@ -1,6 +1,7 @@ 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 @@ -96,8 +97,8 @@ interface ConfirmDialogHandle : DialogHandle { } private abstract class DialogHandleBase( - protected val visible: MutableState, - protected val coroutineScope: CoroutineScope + val visible: MutableState, + val coroutineScope: CoroutineScope ) : DialogHandle { override val isShown: Boolean get() = visible.value @@ -432,7 +433,9 @@ private fun MarkdownContent(content: String) { TextView(context).apply { movementMethod = LinkMovementMethod.getInstance() setSpannableFactory(NoCopySpannableFactory.getInstance()) - breakStrategy = LineBreaker.BREAK_STRATEGY_SIMPLE + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + breakStrategy = LineBreaker.BREAK_STRATEGY_SIMPLE + } hyphenationFrequency = Layout.HYPHENATION_FREQUENCY_NONE layoutParams = ViewGroup.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT diff --git a/manager/app/src/main/java/me/weishu/kernelsu/ui/component/SearchBar.kt b/manager/app/src/main/java/me/weishu/kernelsu/ui/component/SearchBar.kt index 8195cdddd40b..b388cb49d44a 100644 --- a/manager/app/src/main/java/me/weishu/kernelsu/ui/component/SearchBar.kt +++ b/manager/app/src/main/java/me/weishu/kernelsu/ui/component/SearchBar.kt @@ -5,8 +5,12 @@ 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 @@ -19,6 +23,7 @@ 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 @@ -48,6 +53,7 @@ fun SearchAppBar( onBackClick: (() -> Unit)? = null, onConfirm: (() -> Unit)? = null, dropdownContent: @Composable (() -> Unit)? = null, + scrollBehavior: TopAppBarScrollBehavior? = null ) { val keyboardController = LocalSoftwareKeyboardController.current val focusRequester = remember { FocusRequester() } @@ -132,10 +138,13 @@ fun SearchAppBar( dropdownContent() } - } + }, + windowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal), + scrollBehavior = scrollBehavior ) } +@OptIn(ExperimentalMaterial3Api::class) @Preview @Composable private fun SearchAppBarPreview() { diff --git a/manager/app/src/main/java/me/weishu/kernelsu/ui/component/SettingsItem.kt b/manager/app/src/main/java/me/weishu/kernelsu/ui/component/SettingsItem.kt index 6db20b163cde..e537175fd2c4 100644 --- a/manager/app/src/main/java/me/weishu/kernelsu/ui/component/SettingsItem.kt +++ b/manager/app/src/main/java/me/weishu/kernelsu/ui/component/SettingsItem.kt @@ -1,14 +1,18 @@ package me.weishu.kernelsu.ui.component -import androidx.compose.foundation.clickable +import androidx.compose.foundation.LocalIndication +import androidx.compose.foundation.interaction.MutableInteractionSource +import androidx.compose.foundation.selection.toggleable 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( @@ -19,10 +23,18 @@ fun SwitchItem( enabled: Boolean = true, onCheckedChange: (Boolean) -> Unit ) { + val interactionSource = remember { MutableInteractionSource() } + ListItem( - modifier = Modifier.clickable { - onCheckedChange.invoke(!checked) - }, + modifier = Modifier + .toggleable( + value = checked, + interactionSource = interactionSource, + role = Role.Switch, + enabled = enabled, + indication = LocalIndication.current, + onValueChange = onCheckedChange + ), headlineContent = { Text(title) }, @@ -30,7 +42,12 @@ fun SwitchItem( { Icon(icon, title) } }, trailingContent = { - Switch(checked = checked, enabled = enabled, onCheckedChange = onCheckedChange) + Switch( + checked = checked, + enabled = enabled, + onCheckedChange = onCheckedChange, + interactionSource = interactionSource + ) }, supportingContent = { if (summary != null) { @@ -52,6 +69,6 @@ fun RadioItem( }, leadingContent = { RadioButton(selected = selected, onClick = onClick) - }, + } ) } diff --git a/manager/app/src/main/java/me/weishu/kernelsu/ui/component/profile/RootProfileConfig.kt b/manager/app/src/main/java/me/weishu/kernelsu/ui/component/profile/RootProfileConfig.kt index d2cb3f342e51..1a39dcc35ea5 100644 --- a/manager/app/src/main/java/me/weishu/kernelsu/ui/component/profile/RootProfileConfig.kt +++ b/manager/app/src/main/java/me/weishu/kernelsu/ui/component/profile/RootProfileConfig.kt @@ -1,11 +1,10 @@ -@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 @@ -20,12 +19,14 @@ 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 @@ -86,7 +87,7 @@ fun RootProfileConfig( ) { OutlinedTextField( modifier = Modifier - .menuAnchor() + .menuAnchor(MenuAnchorType.PrimaryNotEditable) .fillMaxWidth(), readOnly = true, label = { Text(stringResource(R.string.profile_namespace)) }, @@ -184,7 +185,7 @@ fun RootProfileConfig( } } -@OptIn(ExperimentalLayoutApi::class) +@OptIn(ExperimentalLayoutApi::class, ExperimentalMaterial3Api::class) @Composable fun GroupsPanel(selected: List, closeSelection: (selection: Set) -> Unit) { val selectGroupsDialog = rememberCustomDialog { dismiss: () -> Unit -> @@ -234,14 +235,20 @@ fun GroupsPanel(selected: List, closeSelection: (selection: Set) ) } - OutlinedCard(modifier = Modifier - .fillMaxWidth() - .padding(16.dp) - .clickable { - selectGroupsDialog.show() - }) { + OutlinedCard( + modifier = Modifier + .fillMaxWidth() + .padding(16.dp) + ) { - Column(modifier = Modifier.padding(16.dp)) { + Column( + modifier = Modifier + .fillMaxSize() + .clickable { + selectGroupsDialog.show() + } + .padding(16.dp) + ) { Text(stringResource(R.string.profile_groups)) FlowRow { selected.forEach { group -> @@ -256,7 +263,7 @@ fun GroupsPanel(selected: List, closeSelection: (selection: Set) } } -@OptIn(ExperimentalLayoutApi::class) +@OptIn(ExperimentalLayoutApi::class, ExperimentalMaterial3Api::class) @Composable fun CapsPanel( selected: Collection, @@ -299,14 +306,20 @@ fun CapsPanel( ) } - OutlinedCard(modifier = Modifier - .fillMaxWidth() - .padding(16.dp) - .clickable { - selectCapabilitiesDialog.show() - }) { + OutlinedCard( + modifier = Modifier + .fillMaxWidth() + .padding(16.dp) + ) { - Column(modifier = Modifier.padding(16.dp)) { + Column( + modifier = Modifier + .fillMaxSize() + .clickable { + selectCapabilitiesDialog.show() + } + .padding(16.dp) + ) { Text(stringResource(R.string.profile_capabilities)) FlowRow { selected.forEach { group -> @@ -329,10 +342,10 @@ private fun UidPanel(uid: Int, label: String, onUidChange: (Int) -> Unit) { mutableStateOf(false) } var lastValidUid by remember { - mutableStateOf(uid) + mutableIntStateOf(uid) } - val keyboardController = LocalSoftwareKeyboardController.current + OutlinedTextField( modifier = Modifier.fillMaxWidth(), label = { Text(label) }, @@ -365,6 +378,7 @@ private fun UidPanel(uid: Int, label: String, onUidChange: (Int) -> Unit) { }) } +@OptIn(ExperimentalMaterial3Api::class) @Composable private fun SELinuxPanel( profile: Natives.Profile, @@ -452,7 +466,7 @@ private fun SELinuxPanel( ), label = { Text(text = stringResource(R.string.profile_selinux_context)) }, value = profile.context, - onValueChange = { }, + onValueChange = { } ) }) } diff --git a/manager/app/src/main/java/me/weishu/kernelsu/ui/component/profile/TemplateConfig.kt b/manager/app/src/main/java/me/weishu/kernelsu/ui/component/profile/TemplateConfig.kt index d09a23434f85..b60e8ea46495 100644 --- a/manager/app/src/main/java/me/weishu/kernelsu/ui/component/profile/TemplateConfig.kt +++ b/manager/app/src/main/java/me/weishu/kernelsu/ui/component/profile/TemplateConfig.kt @@ -12,6 +12,7 @@ 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 @@ -54,7 +55,7 @@ fun TemplateConfig( ) { OutlinedTextField( modifier = Modifier - .menuAnchor() + .menuAnchor(MenuAnchorType.PrimaryNotEditable) .fillMaxWidth(), readOnly = true, label = { Text(stringResource(R.string.profile_template)) }, diff --git a/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/AppProfile.kt b/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/AppProfile.kt index ca53388f8ea4..30e96e97aac3 100644 --- a/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/AppProfile.kt +++ b/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/AppProfile.kt @@ -7,10 +7,14 @@ 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 @@ -28,8 +32,11 @@ 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 @@ -39,6 +46,7 @@ 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 @@ -50,6 +58,9 @@ import androidx.compose.ui.unit.dp 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 @@ -58,8 +69,6 @@ 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 @@ -73,19 +82,19 @@ import me.weishu.kernelsu.ui.viewmodel.getTemplateInfoById * @author weishu * @date 2023/5/16. */ -@Destination +@OptIn(ExperimentalMaterial3Api::class) +@Destination @Composable fun AppProfileScreen( navigator: DestinationsNavigator, appInfo: SuperUserViewModel.AppInfo, ) { val context = LocalContext.current - val snackbarHost = LocalSnackbarHost.current + val snackBarHost = LocalSnackbarHost.current + val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior() 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 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) @@ -97,18 +106,25 @@ fun AppProfileScreen( } Scaffold( - topBar = { TopBar { navigator.popBackStack() } }, + topBar = { + TopBar( + onBack = { navigator.popBackStack() }, + scrollBehavior = scrollBehavior + ) + }, + snackbarHost = { SnackbarHost(hostState = snackBarHost) }, + contentWindowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal) ) { 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) @@ -129,12 +145,12 @@ fun AppProfileScreen( scope.launch { if (it.allowSu && !it.rootUseDefault && it.rules.isNotEmpty()) { if (!setSepolicy(profile.name, it.rules)) { - snackbarHost.showSnackbar(failToUpdateSepolicy) + snackBarHost.showSnackbar(failToUpdateSepolicy) return@launch } } if (!Natives.setAppProfile(it)) { - snackbarHost.showSnackbar(failToUpdateAppProfile.format(appInfo.uid)) + snackBarHost.showSnackbar(failToUpdateAppProfile.format(appInfo.uid)) } else { profile = it } @@ -174,7 +190,9 @@ private fun AppProfileInner( ) Crossfade(targetState = isRootGranted, label = "") { current -> - Column { + Column( + modifier = Modifier.padding(bottom = 6.dp + 48.dp + 6.dp /* SnackBar height */) + ) { if (current) { val initialMode = if (profile.rootUseDefault) { Mode.Default @@ -238,7 +256,10 @@ private enum class Mode(@StringRes private val res: Int) { @OptIn(ExperimentalMaterial3Api::class) @Composable -private fun TopBar(onBack: () -> Unit) { +private fun TopBar( + onBack: () -> Unit, + scrollBehavior: TopAppBarScrollBehavior? = null +) { TopAppBar( title = { Text(stringResource(R.string.profile)) @@ -248,6 +269,8 @@ private fun TopBar(onBack: () -> Unit) { onClick = onBack ) { Icon(Icons.AutoMirrored.Filled.ArrowBack, contentDescription = null) } }, + windowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal), + scrollBehavior = scrollBehavior ) } @@ -303,7 +326,8 @@ private fun AppMenuBox(packageName: String, content: @Composable () -> Unit) { touchPoint = it expanded = true } - }) { + } + ) { content() diff --git a/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/BottomBarDestination.kt b/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/BottomBarDestination.kt index 9345ced52ff0..c9637ed2e979 100644 --- a/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/BottomBarDestination.kt +++ b/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/BottomBarDestination.kt @@ -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, diff --git a/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/ExecuteModuleAction.kt b/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/ExecuteModuleAction.kt new file mode 100644 index 000000000000..69480997d73f --- /dev/null +++ b/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/ExecuteModuleAction.kt @@ -0,0 +1,143 @@ +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 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 +fun ExecuteModuleActionScreen(navigator: DestinationsNavigator, moduleId: String) { + var text by rememberSaveable { mutableStateOf("") } + 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 = { + text += "$it\n" + logContent.append(it).append("\n") + }, + onStderr = { + logContent.append(it).append("\n") + } + ).let { + actionResult = it + } + } + if (actionResult) navigator.popBackStack() + } + + Scaffold( + topBar = { + TopBar( + onBack = { + 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), + ) + } + } + ) +} diff --git a/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/Flash.kt b/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/Flash.kt index 1e3d48fdae05..a60767d045b2 100644 --- a/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/Flash.kt +++ b/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/Flash.kt @@ -4,8 +4,12 @@ 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 @@ -18,8 +22,12 @@ 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 @@ -30,11 +38,13 @@ 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 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 @@ -65,8 +75,9 @@ enum class FlashingStatus { * @author weishu * @date 2023/1/1. */ +@OptIn(ExperimentalMaterial3Api::class) @Composable -@Destination +@Destination fun FlashScreen(navigator: DestinationsNavigator, flashIt: FlashIt) { var text by rememberSaveable { mutableStateOf("") } @@ -76,6 +87,7 @@ fun FlashScreen(navigator: DestinationsNavigator, flashIt: FlashIt) { val snackBarHost = LocalSnackbarHost.current val scope = rememberCoroutineScope() val scrollState = rememberScrollState() + val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState()) var flashing by rememberSaveable { mutableStateOf(FlashingStatus.FLASHING) } @@ -121,7 +133,8 @@ fun FlashScreen(navigator: DestinationsNavigator, flashIt: FlashIt) { file.writeText(logContent.toString()) snackBarHost.showSnackbar("Log saved to ${file.absolutePath}") } - } + }, + scrollBehavior = scrollBehavior ) }, floatingActionButton = { @@ -139,8 +152,9 @@ 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 @@ -149,6 +163,7 @@ fun FlashScreen(navigator: DestinationsNavigator, flashIt: FlashIt) { modifier = Modifier .fillMaxSize(1f) .padding(innerPadding) + .nestedScroll(scrollBehavior.nestedScrollConnection) .verticalScroll(scrollState), ) { LaunchedEffect(text) { @@ -202,7 +217,12 @@ fun flashIt( @OptIn(ExperimentalMaterial3Api::class) @Composable -private fun TopBar(status: FlashingStatus, onBack: () -> Unit = {}, onSave: () -> Unit = {}) { +private fun TopBar( + status: FlashingStatus, + onBack: () -> Unit = {}, + onSave: () -> Unit = {}, + scrollBehavior: TopAppBarScrollBehavior? = null +) { TopAppBar( title = { Text( @@ -227,7 +247,9 @@ private fun TopBar(status: FlashingStatus, onBack: () -> Unit = {}, onSave: () - contentDescription = "Localized description" ) } - } + }, + windowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal), + scrollBehavior = scrollBehavior ) } diff --git a/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/Home.kt b/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/Home.kt index 7f1d483f14bf..0fa3b34a8d4e 100644 --- a/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/Home.kt +++ b/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/Home.kt @@ -22,42 +22,54 @@ 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.RootNavGraph +import com.ramcosta.composedestinations.annotation.RootGraph +import com.ramcosta.composedestinations.generated.destinations.InstallScreenDestination +import com.ramcosta.composedestinations.generated.destinations.SettingScreenDestination 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.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 -@RootNavGraph(start = true) -@Destination +@OptIn(ExperimentalMaterial3Api::class) +@Destination(start = true) @Composable fun HomeScreen(navigator: DestinationsNavigator) { val kernelVersion = getKernelVersion() - - Scaffold(topBar = { - TopBar(kernelVersion, onSettingsClick = { - navigator.navigate(SettingScreenDestination) - }, onInstallClick = { - navigator.navigate(InstallScreenDestination) - }) - }) { innerPadding -> + 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 -> Column( modifier = Modifier .padding(innerPadding) - .padding(horizontal = 16.dp) - .verticalScroll(rememberScrollState()), + .nestedScroll(scrollBehavior.nestedScrollConnection) + .verticalScroll(rememberScrollState()) + .padding(horizontal = 16.dp), verticalArrangement = Arrangement.spacedBy(16.dp) ) { val isManager = Natives.becomeManager(ksuApp.packageName) @@ -103,12 +115,11 @@ 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 @@ -128,7 +139,9 @@ fun UpdateCard() { message = stringResource(id = R.string.new_version_available).format(newVersionCode), MaterialTheme.colorScheme.outlineVariant ) { - if (changelog.isNotEmpty()) { + if (changelog.isEmpty()) { + uriHandler.openUri(newVersionUrl) + } else { updateDialog.showConfirm( title = title, content = changelog, @@ -154,52 +167,58 @@ fun RebootDropdownItem(@StringRes id: Int, reason: String = "") { private fun TopBar( kernelVersion: KernelVersion, onInstallClick: () -> Unit, - onSettingsClick: () -> Unit + onSettingsClick: () -> Unit, + scrollBehavior: TopAppBarScrollBehavior? = null ) { - 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) - ) + 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) } - IconButton(onClick = { - showDropdown = true - }) { - Icon( - imageVector = Icons.Filled.Refresh, - contentDescription = stringResource(id = R.string.reboot) - ) - DropdownMenu(expanded = showDropdown, onDismissRequest = { - showDropdown = false + 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) + 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") + 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") } - 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) - ) - } - }) + IconButton(onClick = onSettingsClick) { + Icon( + imageVector = Icons.Filled.Settings, + contentDescription = stringResource(id = R.string.settings) + ) + } + }, + windowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal), + scrollBehavior = scrollBehavior + ) } @Composable @@ -413,9 +432,10 @@ private fun InfoCard() { } } -fun getManagerVersion(context: Context): Pair { - val packageInfo = context.packageManager.getPackageInfo(context.packageName, 0) - return Pair(packageInfo.versionName, packageInfo.versionCode) +fun getManagerVersion(context: Context): Pair { + val packageInfo = context.packageManager.getPackageInfo(context.packageName, 0)!! + val versionCode = PackageInfoCompat.getLongVersionCode(packageInfo) + return Pair(packageInfo.versionName!!, versionCode) } @Preview diff --git a/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/Install.kt b/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/Install.kt index 71b5c9756909..aa4843295159 100644 --- a/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/Install.kt +++ b/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/Install.kt @@ -6,11 +6,19 @@ import android.net.Uri import androidx.activity.compose.rememberLauncherForActivityResult import androidx.activity.result.contract.ActivityResultContracts import androidx.annotation.StringRes -import androidx.compose.foundation.clickable +import androidx.compose.foundation.LocalIndication +import androidx.compose.foundation.interaction.MutableInteractionSource 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 @@ -23,6 +31,9 @@ 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 @@ -31,7 +42,9 @@ 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 com.maxkeppeker.sheets.core.models.base.Header @@ -40,13 +53,14 @@ 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 @@ -58,7 +72,8 @@ import me.weishu.kernelsu.ui.util.rootAvailable * @author weishu * @date 2024/3/12. */ -@Destination +@OptIn(ExperimentalMaterial3Api::class) +@Destination @Composable fun InstallScreen(navigator: DestinationsNavigator) { var installMethod by remember { @@ -113,12 +128,24 @@ fun InstallScreen(navigator: DestinationsNavigator) { }) } - Scaffold(topBar = { - TopBar( - onBack = { navigator.popBackStack() }, onLkmUpload = onLkmUpload - ) - }) { - Column(modifier = Modifier.padding(it)) { + val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState()) + + Scaffold( + topBar = { + TopBar( + onBack = { 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()) + ) { SelectInstallMethod { method -> installMethod = method } @@ -231,16 +258,31 @@ private fun SelectInstallMethod(onSelected: (InstallMethod) -> Unit = {}) { Column { radioOptions.forEach { option -> - Row(verticalAlignment = Alignment.CenterVertically, + val interactionSource = remember { MutableInteractionSource() } + Row( + verticalAlignment = Alignment.CenterVertically, modifier = Modifier .fillMaxWidth() - .clickable { + .toggleable( + value = option.javaClass == selectedOption?.javaClass, + onValueChange = { + onClick(option) + }, + role = Role.RadioButton, + indication = LocalIndication.current, + interactionSource = interactionSource + ) + ) { + RadioButton( + selected = option.javaClass == selectedOption?.javaClass, + onClick = { onClick(option) - }) { - RadioButton(selected = option.javaClass == selectedOption?.javaClass, onClick = { - onClick(option) - }) - Column { + }, + interactionSource = interactionSource + ) + Column( + modifier = Modifier.padding(vertical = 12.dp) + ) { Text( text = stringResource(id = option.label), fontSize = MaterialTheme.typography.titleMedium.fontSize, @@ -292,20 +334,28 @@ fun rememberSelectKmiDialog(onSelected: (String?) -> Unit): DialogHandle { @OptIn(ExperimentalMaterial3Api::class) @Composable -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) - } - }) +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 + ) } @Composable @Preview -fun SelectInstall_Preview() { +fun SelectInstallPreview() { InstallScreen(EmptyDestinationsNavigator) } \ No newline at end of file diff --git a/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/Module.kt b/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/Module.kt index 9812e87ebba6..a0d28f093189 100644 --- a/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/Module.kt +++ b/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/Module.kt @@ -1,47 +1,58 @@ 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.clickable +import androidx.compose.foundation.LocalIndication +import androidx.compose.foundation.interaction.MutableInteractionSource 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.shape.RoundedCornerShape -import androidx.compose.material.ExperimentalMaterialApi +import androidx.compose.foundation.selection.toggleable 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.pullrefresh.PullRefreshIndicator -import androidx.compose.material.pullrefresh.pullRefresh -import androidx.compose.material.pullrefresh.rememberPullRefreshState +import androidx.compose.material.icons.outlined.PlayArrow import androidx.compose.material3.Button -import androidx.compose.material3.CardDefaults +import androidx.compose.material3.ButtonDefaults 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.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.TopAppBar +import androidx.compose.material3.TopAppBarDefaults +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 @@ -53,8 +64,10 @@ 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 @@ -64,7 +77,11 @@ 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 @@ -73,7 +90,6 @@ import me.weishu.kernelsu.R import me.weishu.kernelsu.ui.component.ConfirmResult 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 @@ -85,11 +101,13 @@ import me.weishu.kernelsu.ui.viewmodel.ModuleViewModel import me.weishu.kernelsu.ui.webui.WebUIActivity import okhttp3.OkHttpClient -@Destination +@OptIn(ExperimentalMaterial3Api::class) +@Destination @Composable fun ModuleScreen(navigator: DestinationsNavigator) { val viewModel = viewModel() val context = LocalContext.current + val snackBarHost = LocalSnackbarHost.current LaunchedEffect(Unit) { if (viewModel.moduleList.isEmpty() || viewModel.isNeedRefresh) { @@ -102,42 +120,51 @@ fun ModuleScreen(navigator: DestinationsNavigator) { val hideInstallButton = isSafeMode || hasMagisk - 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 + val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState()) - navigator.navigate(FlashScreenDestination(FlashIt.FlashModule(uri))) + Scaffold( + topBar = { + TopAppBar( + scrollBehavior = scrollBehavior, + title = { Text(stringResource(R.string.module)) }, + windowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal) + ) + }, + floatingActionButton = { + if (!hideInstallButton) { + 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 - viewModel.markNeedRefresh() + navigator.navigate(FlashScreenDestination(FlashIt.FlashModule(uri))) - Log.i("ModuleScreen", "select zip result: ${it.data}") - } + viewModel.markNeedRefresh() - 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 -> + Log.i("ModuleScreen", "select zip result: ${it.data}") + } + ExtendedFloatingActionButton( + onClick = { + // select the zip file to install + val intent = Intent(Intent.ACTION_GET_CONTENT).apply { + type = "application/zip" + } + selectZipLauncher.launch(intent) + }, + icon = { Icon(Icons.Filled.Add, moduleInstall) }, + text = { Text(text = moduleInstall) }, + ) + } + }, + contentWindowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal), + snackbarHost = { SnackbarHost(hostState = snackBarHost) } + ) { innerPadding -> when { hasMagisk -> { Box( @@ -155,53 +182,59 @@ fun ModuleScreen(navigator: DestinationsNavigator) { else -> { ModuleList( - viewModel = viewModel, modifier = Modifier - .padding(innerPadding) - .fillMaxSize(), - onInstallModule = - { + navigator, + viewModel = viewModel, + modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection), + boxModifier = Modifier.padding(innerPadding), + onInstallModule = { navigator.navigate(FlashScreenDestination(FlashIt.FlashModule(it))) - }, onClickModule = { id, name, hasWebUi -> + }, + onClickModule = { id, name, hasWebUi -> if (hasWebUi) { - context.startActivity(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(ExperimentalMaterialApi::class) +@OptIn(ExperimentalMaterial3Api::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 + onClickModule: (id: String, name: String, hasWebUi: Boolean) -> Unit, + context: Context, + snackBarHost: SnackbarHostState ) { 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(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 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 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() @@ -300,26 +333,31 @@ private fun ModuleList( } else { null } - val result = snackBarHost.showSnackbar(message, actionLabel = actionLabel) + val result = snackBarHost.showSnackbar( + message = message, + actionLabel = actionLabel, + duration = SnackbarDuration.Long + ) if (result == SnackbarResult.ActionPerformed) { reboot() } } - - val refreshState = rememberPullRefreshState(refreshing = viewModel.isRefreshing, - onRefresh = { viewModel.fetchModuleList() }) - Box(modifier.pullRefresh(refreshState)) { - val context = LocalContext.current - + PullToRefreshBox( + modifier = boxModifier, + onRefresh = { + viewModel.fetchModuleList() + }, + isRefreshing = viewModel.isRefreshing + ) { LazyColumn( - modifier = Modifier.fillMaxSize(), + modifier = modifier, verticalArrangement = Arrangement.spacedBy(16.dp), contentPadding = remember { PaddingValues( start = 16.dp, top = 16.dp, end = 16.dp, - bottom = 16.dp + 16.dp + 56.dp /* Scaffold Fab Spacing + Fab container height */ + bottom = 16.dp + 56.dp + 16.dp + 48.dp + 6.dp /* Scaffold Fab Spacing + Fab container height + SnackBar height */ ) }, ) { @@ -362,42 +400,53 @@ private fun ModuleList( } } - ModuleItem(module, isChecked, updatedModule.first, onUninstall = { - scope.launch { onModuleUninstall(module) } - }, onCheckChanged = { - scope.launch { - val success = loadingDialog.withLoading { - withContext(Dispatchers.IO) { - toggleModule(module.id, !isChecked) + ModuleItem( + navigator = navigator, + module = module, + isChecked = isChecked, + updateUrl = updatedModule.first, + onUninstall = { + scope.launch { onModuleUninstall(module) } + }, + onCheckChanged = { + scope.launch { + val success = loadingDialog.withLoading { + withContext(Dispatchers.IO) { + toggleModule(module.id, !isChecked) + } + } + 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 (isChecked) failedDisable else failedEnable + snackBarHost.showSnackbar(message.format(module.name)) } } - if (success) { - isChecked = it - viewModel.fetchModuleList() - - val result = snackBarHost.showSnackbar( - rebootToApply, actionLabel = reboot + }, + onUpdate = { + scope.launch { + onModuleUpdate( + module, + updatedModule.third, + updatedModule.first, + "${module.name}-${updatedModule.second}.zip" ) - 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)) @@ -408,22 +457,12 @@ private fun ModuleList( DownloadListener(context, onInstallModule) - PullRefreshIndicator( - refreshing = viewModel.isRefreshing, state = refreshState, modifier = Modifier.align( - Alignment.TopCenter - ) - ) } } -@OptIn(ExperimentalMaterial3Api::class) -@Composable -private fun TopBar() { - TopAppBar(title = { Text(stringResource(R.string.module)) }) -} - @Composable -private fun ModuleItem( +fun ModuleItem( + navigator: DestinationsNavigator, module: ModuleViewModel.ModuleInfo, isChecked: Boolean, updateUrl: String, @@ -433,13 +472,36 @@ private fun ModuleItem( onClick: (ModuleViewModel.ModuleInfo) -> Unit ) { ElevatedCard( - modifier = Modifier.fillMaxWidth(), - colors = CardDefaults.elevatedCardColors(containerColor = MaterialTheme.colorScheme.surface) + modifier = Modifier.fillMaxWidth() ) { - val textDecoration = if (!module.remove) null else TextDecoration.LineThrough - - Column(modifier = Modifier.clickable { onClick(module) }.padding(24.dp, 16.dp, 24.dp, 0.dp)) { + val interactionSource = remember { MutableInteractionSource() } + val indication = LocalIndication.current + + Column( + modifier = Modifier + .run { + if (module.hasWebUi) { + toggleable( + value = isChecked, + interactionSource = interactionSource, + role = Role.Button, + indication = indication, + onValueChange = { onClick(module) } + ) + } else { + toggleable( + value = isChecked, + interactionSource = interactionSource, + role = Role.Switch, + indication = indication, + onValueChange = onCheckChanged, + enabled = !module.update + ) + } + } + .padding(22.dp, 18.dp, 22.dp, 12.dp) + ) { Row( modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween, @@ -447,7 +509,9 @@ private 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, @@ -483,7 +547,8 @@ private fun ModuleItem( Switch( enabled = !module.update, checked = isChecked, - onCheckedChange = onCheckChanged + onCheckedChange = onCheckChanged, + interactionSource = if (!module.hasWebUi) interactionSource else null ) } } @@ -501,56 +566,95 @@ private 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), + onClick = { navigator.navigate(ExecuteModuleActionScreenDestination(module.id)) }, + contentPadding = ButtonDefaults.TextButtonContentPadding + ) { + Icon( + modifier = Modifier + .padding(end = 7.dp) + .size(20.dp), + imageVector = Icons.Outlined.PlayArrow, + contentDescription = null + ) + Text( + 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), + onClick = { onClick(module) }, + interactionSource = interactionSource, + contentPadding = ButtonDefaults.TextButtonContentPadding + ) { + if (!module.hasActionScript) { + Icon( + modifier = Modifier + .padding(end = 7.dp) + .size(20.dp), + imageVector = Icons.AutoMirrored.Outlined.Wysiwyg, + contentDescription = null + ) + } + Text( + 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 - .padding(0.dp) - .defaultMinSize(52.dp, 32.dp), + modifier = Modifier.defaultMinSize(52.dp, 32.dp), onClick = { onUpdate(module) }, - shape = RoundedCornerShape(6.dp), - contentPadding = PaddingValues(0.dp) + shape = ButtonDefaults.textShape, + contentPadding = ButtonDefaults.TextButtonContentPadding ) { Text( fontFamily = MaterialTheme.typography.labelMedium.fontFamily, fontSize = MaterialTheme.typography.labelMedium.fontSize, - text = stringResource(R.string.module_update), + text = stringResource(R.string.module_update) ) } + + Spacer(modifier = Modifier.weight(0.1f, true)) } - TextButton( + FilledTonalButton( + modifier = Modifier.defaultMinSize(52.dp, 32.dp), enabled = !module.remove, onClick = { onUninstall(module) }, + contentPadding = ButtonDefaults.TextButtonContentPadding ) { Text( fontFamily = MaterialTheme.typography.labelMedium.fontFamily, fontSize = MaterialTheme.typography.labelMedium.fontSize, - text = stringResource(R.string.uninstall), + 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), - ) - } - } } } } @@ -568,9 +672,10 @@ fun ModuleItemPreview() { description = "I am a test module and i do nothing but show a very long description", enabled = true, update = true, - remove = true, + remove = false, updateJson = "", hasWebUi = false, + hasActionScript = false ) - ModuleItem(module, true, "", {}, {}, {}, {}) + ModuleItem(EmptyDestinationsNavigator, module, true, "", {}, {}, {}, {}) } \ No newline at end of file diff --git a/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/Settings.kt b/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/Settings.kt index 4f970f7ee331..fe6b8786f335 100644 --- a/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/Settings.kt +++ b/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/Settings.kt @@ -1,18 +1,20 @@ 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 @@ -29,15 +31,18 @@ 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 @@ -48,6 +53,7 @@ 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,6 +68,9 @@ 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 @@ -77,10 +86,8 @@ import me.weishu.kernelsu.ui.component.SwitchItem 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.screen.destinations.AppProfileTemplateScreenDestination -import me.weishu.kernelsu.ui.screen.destinations.FlashScreenDestination +import me.weishu.kernelsu.ui.util.LocalSnackbarHost 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 @@ -90,15 +97,23 @@ import java.time.format.DateTimeFormatter * @date 2023/1/1. */ @OptIn(ExperimentalMaterial3Api::class) -@Destination +@Destination @Composable fun SettingScreen(navigator: DestinationsNavigator) { + val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState()) + val snackBarHost = LocalSnackbarHost.current + Scaffold( topBar = { - TopBar(onBack = { - navigator.popBackStack() - }) - } + TopBar( + onBack = { + navigator.popBackStack() + }, + scrollBehavior = scrollBehavior + ) + }, + snackbarHost = { SnackbarHost(snackBarHost) }, + contentWindowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal) ) { paddingValues -> val aboutDialog = rememberCustomDialog { AboutDialog(it) @@ -109,12 +124,29 @@ 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) ListItem( leadingContent = { Icon(Icons.Filled.Fence, profileTemplate) }, @@ -184,46 +216,25 @@ 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 { - 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) - ) - ) - } + val formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd_HH_mm") + val current = LocalDateTime.now().format(formatter) + exportBugreportLauncher.launch("KernelSU_bugreport_${current}.tar.gz") + showBottomsheet = false } ) { Icon( @@ -243,11 +254,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 { @@ -263,10 +274,11 @@ fun SettingScreen(navigator: DestinationsNavigator) { bugreport ) - 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) + val shareIntent = Intent(Intent.ACTION_SEND).apply { + putExtra(Intent.EXTRA_STREAM, uri) + setDataAndType(uri, "application/gzip") + addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) + } context.startActivity( Intent.createChooser( @@ -291,16 +303,12 @@ fun SettingScreen(navigator: DestinationsNavigator) { trim = LineHeightStyle.Trim.None ) } - ) } - } } } ) - - } val shrink = stringResource(id = R.string.shrink_sparse_image) @@ -315,8 +323,7 @@ fun SettingScreen(navigator: DestinationsNavigator) { headlineContent = { Text(shrink) }, modifier = Modifier.clickable { scope.launch { - val result = - shrinkDialog.awaitConfirm(title = shrink, content = shrinkMessage) + val result = shrinkDialog.awaitConfirm(title = shrink, content = shrinkMessage) if (result == ConfirmResult.Confirmed) { loadingDialog.withLoading { shrinkModules() @@ -326,8 +333,7 @@ fun SettingScreen(navigator: DestinationsNavigator) { } ) - 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) @@ -339,7 +345,7 @@ fun SettingScreen(navigator: DestinationsNavigator) { leadingContent = { Icon( Icons.Filled.ContactPage, - stringResource(id = R.string.about) + about ) }, headlineContent = { Text(about) }, @@ -350,6 +356,7 @@ fun SettingScreen(navigator: DestinationsNavigator) { } } } + @Composable fun UninstallItem( navigator: DestinationsNavigator, @@ -374,11 +381,9 @@ fun UninstallItem( UninstallType.PERMANENT -> navigator.navigate( FlashScreenDestination(FlashIt.FlashUninstall) ) - UninstallType.RESTORE_STOCK_IMAGE -> navigator.navigate( FlashScreenDestination(FlashIt.FlashRestore) ) - UninstallType.NONE -> Unit } } @@ -456,14 +461,21 @@ fun rememberUninstallDialog(onSelected: (UninstallType) -> Unit): DialogHandle { @OptIn(ExperimentalMaterial3Api::class) @Composable -private fun TopBar(onBack: () -> Unit = {}) { +private fun TopBar( + onBack: () -> Unit = {}, + scrollBehavior: TopAppBarScrollBehavior? = null +) { 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 ) } diff --git a/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/SuperUser.kt b/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/SuperUser.kt index 2eb2e770bb9d..1a37d0110318 100644 --- a/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/SuperUser.kt +++ b/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/SuperUser.kt @@ -9,14 +9,12 @@ 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 @@ -26,20 +24,22 @@ 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) -@Destination +@OptIn(ExperimentalMaterialApi::class, ExperimentalMaterial3Api::class) +@Destination @Composable fun SuperUserScreen(navigator: DestinationsNavigator) { val viewModel = viewModel() val scope = rememberCoroutineScope() + val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState()) LaunchedEffect(Unit) { if (viewModel.appList.isEmpty()) { @@ -91,32 +91,29 @@ fun SuperUserScreen(navigator: DestinationsNavigator) { } } }, + scrollBehavior = scrollBehavior ) - } + }, + contentWindowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal) ) { innerPadding -> - val refreshState = rememberPullRefreshState( - refreshing = viewModel.isRefreshing, - onRefresh = { scope.launch { viewModel.fetchAppList() } }, - ) - Box( - modifier = Modifier - .padding(innerPadding) - .pullRefresh(refreshState) + PullToRefreshBox( + modifier = Modifier.padding(innerPadding), + onRefresh = { + scope.launch { viewModel.fetchAppList() } + }, + isRefreshing = viewModel.isRefreshing ) { - LazyColumn(Modifier.fillMaxSize()) { + LazyColumn( + modifier = Modifier + .fillMaxSize() + .nestedScroll(scrollBehavior.nestedScrollConnection) + ) { 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) - ) } } } diff --git a/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/Template.kt b/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/Template.kt index bf353b0afa84..3074f4e19e0c 100644 --- a/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/Template.kt +++ b/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/Template.kt @@ -2,13 +2,16 @@ 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 @@ -17,9 +20,6 @@ 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,6 +31,10 @@ 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 @@ -38,8 +42,8 @@ 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 @@ -47,13 +51,14 @@ import androidx.compose.ui.text.AnnotatedString 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.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 /** @@ -61,8 +66,8 @@ import me.weishu.kernelsu.ui.viewmodel.TemplateViewModel * @date 2023/10/20. */ -@OptIn(ExperimentalMaterialApi::class) -@Destination +@OptIn(ExperimentalMaterialApi::class, ExperimentalMaterial3Api::class) +@Destination @Composable fun AppProfileTemplateScreen( navigator: DestinationsNavigator, @@ -70,6 +75,7 @@ fun AppProfileTemplateScreen( ) { val viewModel = viewModel() val scope = rememberCoroutineScope() + val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState()) LaunchedEffect(Unit) { if (viewModel.templateList.isEmpty()) { @@ -93,7 +99,8 @@ fun AppProfileTemplateScreen( Toast.makeText(context, msg, Toast.LENGTH_SHORT).show() } } - TopBar(onBack = { navigator.popBackStack() }, + TopBar( + onBack = { navigator.popBackStack() }, onSync = { scope.launch { viewModel.fetchTemplates(true) } }, @@ -124,7 +131,8 @@ fun AppProfileTemplateScreen( clipboardManager.setText(AnnotatedString(it)) } } - } + }, + scrollBehavior = scrollBehavior ) }, floatingActionButton = { @@ -141,29 +149,27 @@ fun AppProfileTemplateScreen( text = { Text(stringResource(id = R.string.app_profile_template_create)) }, ) }, + contentWindowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal) ) { innerPadding -> - val refreshState = rememberPullRefreshState( - refreshing = viewModel.isRefreshing, - onRefresh = { scope.launch { viewModel.fetchTemplates() } }, - ) - Box( - modifier = Modifier - .padding(innerPadding) - .pullRefresh(refreshState) + PullToRefreshBox( + modifier = Modifier.padding(innerPadding), + isRefreshing = viewModel.isRefreshing, + onRefresh = { + scope.launch { viewModel.fetchTemplates() } + } ) { - LazyColumn(Modifier.fillMaxSize(), contentPadding = remember { - PaddingValues(bottom = 16.dp + 16.dp + 56.dp /* Scaffold Fab Spacing + Fab container height */) - }) { + LazyColumn( + modifier = Modifier + .fillMaxSize() + .nestedScroll(scrollBehavior.nestedScrollConnection), + contentPadding = remember { + PaddingValues(bottom = 16.dp + 56.dp + 16.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) - ) } } } @@ -209,7 +215,8 @@ private fun TopBar( onBack: () -> Unit, onSync: () -> Unit = {}, onImport: () -> Unit = {}, - onExport: () -> Unit = {} + onExport: () -> Unit = {}, + scrollBehavior: TopAppBarScrollBehavior? = null ) { TopAppBar( title = { @@ -254,6 +261,8 @@ private fun TopBar( }) } } - } + }, + windowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal), + scrollBehavior = scrollBehavior ) } \ No newline at end of file diff --git a/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/TemplateEditor.kt b/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/TemplateEditor.kt index b6b7cc8028ec..030320ba329a 100644 --- a/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/TemplateEditor.kt +++ b/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/TemplateEditor.kt @@ -3,8 +3,12 @@ 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 @@ -22,6 +26,9 @@ 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 @@ -30,6 +37,7 @@ 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 @@ -37,6 +45,7 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.text.input.KeyboardType 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 @@ -51,8 +60,8 @@ import me.weishu.kernelsu.ui.viewmodel.toJSON * @author weishu * @date 2023/10/20. */ -@OptIn(ExperimentalComposeUiApi::class) -@Destination +@OptIn(ExperimentalComposeUiApi::class, ExperimentalMaterial3Api::class) +@Destination @Composable fun TemplateEditorScreen( navigator: ResultBackNavigator, @@ -67,6 +76,8 @@ fun TemplateEditorScreen( mutableStateOf(initialTemplate) } + val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState()) + BackHandler { navigator.navigateBack(result = !readOnly) } @@ -106,12 +117,16 @@ 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 @@ -240,39 +255,44 @@ private fun TopBar( summary: String = "", onBack: () -> Unit, onDelete: () -> Unit = {}, - onSave: () -> Unit = {} + onSave: () -> Unit = {}, + scrollBehavior: TopAppBarScrollBehavior? = null ) { - TopAppBar(title = { - Column { - Text(title) - if (summary.isNotBlank()) { - Text( - text = summary, - style = MaterialTheme.typography.bodyMedium, + 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) ) } - } - }, 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) - ) - } - }) + 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 + ) } @Composable @@ -289,17 +309,14 @@ 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( @@ -314,7 +331,7 @@ private fun TextEdit( } private fun isValidTemplateId(id: String): Boolean { - return Regex("""^([A-Za-z]{1}[A-Za-z\d_]*\.)*[A-Za-z][A-Za-z\d_]*$""").matches(id) + return Regex("""^([A-Za-z][A-Za-z\d_]*\.)*[A-Za-z][A-Za-z\d_]*$""").matches(id) } private fun isTemplateExist(id: String): Boolean { diff --git a/manager/app/src/main/java/me/weishu/kernelsu/ui/theme/Theme.kt b/manager/app/src/main/java/me/weishu/kernelsu/ui/theme/Theme.kt index 3b3945d0cdc5..903ee94e0b54 100644 --- a/manager/app/src/main/java/me/weishu/kernelsu/ui/theme/Theme.kt +++ b/manager/app/src/main/java/me/weishu/kernelsu/ui/theme/Theme.kt @@ -7,12 +7,8 @@ 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, @@ -42,20 +38,6 @@ 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, diff --git a/manager/app/src/main/java/me/weishu/kernelsu/ui/util/Downloader.kt b/manager/app/src/main/java/me/weishu/kernelsu/ui/util/Downloader.kt index 954cac50da30..bdf8496633fd 100644 --- a/manager/app/src/main/java/me/weishu/kernelsu/ui/util/Downloader.kt +++ b/manager/app/src/main/java/me/weishu/kernelsu/ui/util/Downloader.kt @@ -7,10 +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.ui.util.module.LatestVersionInfo /** @@ -26,8 +26,7 @@ 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) @@ -130,18 +129,12 @@ fun DownloadListener(context: Context, onDownloaded: (Uri) -> Unit) { } } } - 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) - ) - } + ContextCompat.registerReceiver( + context, + receiver, + IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE), + ContextCompat.RECEIVER_EXPORTED + ) onDispose { context.unregisterReceiver(receiver) } diff --git a/manager/app/src/main/java/me/weishu/kernelsu/ui/util/KsuCli.kt b/manager/app/src/main/java/me/weishu/kernelsu/ui/util/KsuCli.kt index 108e08f2a139..1a2416b280d2 100644 --- a/manager/app/src/main/java/me/weishu/kernelsu/ui/util/KsuCli.kt +++ b/manager/app/src/main/java/me/weishu/kernelsu/ui/util/KsuCli.kt @@ -4,16 +4,15 @@ 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 @@ -52,10 +51,10 @@ inline fun withNewRootShell( return createRootShell(globalMnt).use(block) } -fun getFileNameFromUri(context: Context, uri: Uri): String? { +fun Uri.getFileName(context: Context): String? { var fileName: String? = null val contentResolver: ContentResolver = context.contentResolver - val cursor: Cursor? = contentResolver.query(uri, null, null, null, null) + val cursor: Cursor? = contentResolver.query(this, null, null, null, null) cursor?.use { if (it.moveToFirst()) { fileName = it.getString(it.getColumnIndexOrThrow(OpenableColumns.DISPLAY_NAME)) @@ -189,6 +188,30 @@ fun flashModule( } } +fun runModuleAction( + moduleId: String, onStdout: (String) -> Unit, onStderr: (String) -> Unit +): Boolean { + val shell = getRootShell() + + val stdoutCallback: CallbackList = object : CallbackList() { + override fun onAddElement(s: String?) { + onStdout(s ?: "") + } + } + + val stderrCallback: CallbackList = object : CallbackList() { + 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( onFinish: (Boolean, Int) -> Unit, onStdout: (String) -> Unit, onStderr: (String) -> Unit ): Boolean { @@ -312,18 +335,7 @@ fun isAbDevice(): Boolean { } fun isInitBoot(): Boolean { - 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 + return !Os.uname().release.contains("android12-") } suspend fun getCurrentKmi(): String = withContext(Dispatchers.IO) { diff --git a/manager/app/src/main/java/me/weishu/kernelsu/ui/util/LogEvent.kt b/manager/app/src/main/java/me/weishu/kernelsu/ui/util/LogEvent.kt index 19eb1df18991..a8363120c0fb 100644 --- a/manager/app/src/main/java/me/weishu/kernelsu/ui/util/LogEvent.kt +++ b/manager/app/src/main/java/me/weishu/kernelsu/ui/util/LogEvent.kt @@ -1,16 +1,12 @@ 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 diff --git a/manager/app/src/main/java/me/weishu/kernelsu/ui/util/SELinuxChecker.kt b/manager/app/src/main/java/me/weishu/kernelsu/ui/util/SELinuxChecker.kt index 78346dd9412c..6b0d704ece67 100644 --- a/manager/app/src/main/java/me/weishu/kernelsu/ui/util/SELinuxChecker.kt +++ b/manager/app/src/main/java/me/weishu/kernelsu/ui/util/SELinuxChecker.kt @@ -1,7 +1,7 @@ package me.weishu.kernelsu.ui.util -import androidx.compose.ui.res.stringResource import androidx.compose.runtime.Composable +import androidx.compose.ui.res.stringResource import com.topjohnwu.superuser.Shell import me.weishu.kernelsu.R diff --git a/manager/app/src/main/java/me/weishu/kernelsu/ui/viewmodel/ModuleViewModel.kt b/manager/app/src/main/java/me/weishu/kernelsu/ui/viewmodel/ModuleViewModel.kt index 013453fbfeed..4533e6e5ffc4 100644 --- a/manager/app/src/main/java/me/weishu/kernelsu/ui/viewmodel/ModuleViewModel.kt +++ b/manager/app/src/main/java/me/weishu/kernelsu/ui/viewmodel/ModuleViewModel.kt @@ -36,6 +36,7 @@ class ModuleViewModel : ViewModel() { val remove: Boolean, val updateJson: String, val hasWebUi: Boolean, + val hasActionScript: Boolean, ) data class ModuleUpdateInfo( @@ -87,7 +88,6 @@ class ModuleViewModel : ViewModel() { .map { obj -> ModuleInfo( obj.getString("id"), - obj.optString("name"), obj.optString("author", "Unknown"), obj.optString("version", "Unknown"), @@ -97,7 +97,8 @@ class ModuleViewModel : ViewModel() { obj.getBoolean("update"), obj.getBoolean("remove"), obj.optString("updateJson"), - obj.optBoolean("web") + obj.optBoolean("web"), + obj.optBoolean("action") ) }.toList() isNeedRefresh = false diff --git a/manager/app/src/main/java/me/weishu/kernelsu/ui/viewmodel/SuperUserViewModel.kt b/manager/app/src/main/java/me/weishu/kernelsu/ui/viewmodel/SuperUserViewModel.kt index 37e05aa82aff..ecb5024aaed0 100644 --- a/manager/app/src/main/java/me/weishu/kernelsu/ui/viewmodel/SuperUserViewModel.kt +++ b/manager/app/src/main/java/me/weishu/kernelsu/ui/viewmodel/SuperUserViewModel.kt @@ -45,7 +45,7 @@ class SuperUserViewModel : ViewModel() { val packageName: String get() = packageInfo.packageName val uid: Int - get() = packageInfo.applicationInfo.uid + get() = packageInfo.applicationInfo!!.uid val allowSu: Boolean get() = profile != null && profile.allowSu @@ -90,7 +90,7 @@ class SuperUserViewModel : ViewModel() { .toPinyinString(it.label).contains(search, true) }.filter { it.uid == 2000 // Always show shell - || showSystemApps || it.packageInfo.applicationInfo.flags.and(ApplicationInfo.FLAG_SYSTEM) == 0 + || showSystemApps || it.packageInfo.applicationInfo!!.flags.and(ApplicationInfo.FLAG_SYSTEM) == 0 } } @@ -146,7 +146,7 @@ class SuperUserViewModel : ViewModel() { apps = packages.map { val appInfo = it.applicationInfo - val uid = appInfo.uid + val uid = appInfo!!.uid val profile = Natives.getAppProfile(it.packageName, uid) AppInfo( label = appInfo.loadLabel(pm).toString(), diff --git a/manager/app/src/main/java/me/weishu/kernelsu/ui/webui/WebUIActivity.kt b/manager/app/src/main/java/me/weishu/kernelsu/ui/webui/WebUIActivity.kt index eccb002a1688..d926d7eada35 100644 --- a/manager/app/src/main/java/me/weishu/kernelsu/ui/webui/WebUIActivity.kt +++ b/manager/app/src/main/java/me/weishu/kernelsu/ui/webui/WebUIActivity.kt @@ -2,13 +2,18 @@ package me.weishu.kernelsu.ui.webui import android.annotation.SuppressLint import android.app.ActivityManager -import android.content.Context +import android.os.Build 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 @@ -21,15 +26,30 @@ 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")!! - setTaskDescription(ActivityManager.TaskDescription("KernelSU - $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) + } - val prefs = getSharedPreferences("settings", Context.MODE_PRIVATE) + val prefs = getSharedPreferences("settings", MODE_PRIVATE) WebView.setWebContentsDebuggingEnabled(prefs.getBoolean("enable_web_debugging", false)) - val webRoot = File("/data/adb/modules/${moduleId}/webroot") + val moduleDir = "/data/adb/modules/${moduleId}" + val webRoot = File("${moduleDir}/webroot") val rootShell = createRootShell(true).also { this.rootShell = it } val webViewAssetLoader = WebViewAssetLoader.Builder() .setDomain("mui.kernelsu.org") @@ -49,10 +69,20 @@ class WebUIActivity : ComponentActivity() { } val webView = WebView(this).apply { + ViewCompat.setOnApplyWindowInsetsListener(this) { view, insets -> + val inset = insets.getInsets(WindowInsetsCompat.Type.systemBars()) + view.updateLayoutParams { + 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) + webviewInterface = WebViewInterface(this@WebUIActivity, this, moduleDir) addJavascriptInterface(webviewInterface, "ksu") setWebViewClient(webViewClient) loadUrl("https://mui.kernelsu.org/index.html") diff --git a/manager/app/src/main/java/me/weishu/kernelsu/ui/webui/WebViewInterface.kt b/manager/app/src/main/java/me/weishu/kernelsu/ui/webui/WebViewInterface.kt index 4b6a3c8f755d..00fcde653b60 100644 --- a/manager/app/src/main/java/me/weishu/kernelsu/ui/webui/WebViewInterface.kt +++ b/manager/app/src/main/java/me/weishu/kernelsu/ui/webui/WebViewInterface.kt @@ -9,18 +9,24 @@ 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) { +class WebViewInterface( + val context: Context, + private val webView: WebView, + private val modDir: String +) { @JavascriptInterface fun exec(cmd: String): String { @@ -108,13 +114,13 @@ class WebViewInterface(val context: Context, private val webView: WebView) { } } - val stdout = object : CallbackList() { + val stdout = object : CallbackList(UiThreadHandler::runAndWait) { override fun onAddElement(s: String) { emitData("stdout", s) } } - val stderr = object : CallbackList() { + val stderr = object : CallbackList(UiThreadHandler::runAndWait) { override fun onAddElement(s: String) { emitData("stderr", s) } @@ -170,21 +176,34 @@ class WebViewInterface(val context: Context, private val webView: WebView) { } } + @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) +fun hideSystemUI(window: Window) = WindowInsetsControllerCompat(window, window.decorView).let { controller -> controller.hide(WindowInsetsCompat.Type.systemBars()) - controller.systemBarsBehavior = - WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE + controller.systemBarsBehavior = WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE } -} -fun showSystemUI(window: Window) { - WindowCompat.setDecorFitsSystemWindows(window, true) - WindowInsetsControllerCompat( - window, - window.decorView - ).show(WindowInsetsCompat.Type.systemBars()) -} \ No newline at end of file +fun showSystemUI(window: Window) = + WindowInsetsControllerCompat(window, window.decorView).show(WindowInsetsCompat.Type.systemBars()) diff --git a/manager/app/src/main/res/drawable/ic_launcher_background.xml b/manager/app/src/main/res/drawable/ic_launcher_background.xml deleted file mode 100644 index 900e2d8c829e..000000000000 --- a/manager/app/src/main/res/drawable/ic_launcher_background.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/manager/app/src/main/res/mipmap-anydpi/ic_launcher.xml b/manager/app/src/main/res/mipmap-anydpi/ic_launcher.xml index b070c763da32..f30783b21028 100644 --- a/manager/app/src/main/res/mipmap-anydpi/ic_launcher.xml +++ b/manager/app/src/main/res/mipmap-anydpi/ic_launcher.xml @@ -1,6 +1,6 @@ - + \ No newline at end of file diff --git a/manager/app/src/main/res/resources.properties b/manager/app/src/main/res/resources.properties new file mode 100644 index 000000000000..d5a3ddc92a9b --- /dev/null +++ b/manager/app/src/main/res/resources.properties @@ -0,0 +1 @@ +unqualifiedResLocale=en-US \ No newline at end of file diff --git a/manager/app/src/main/res/values-ar/strings.xml b/manager/app/src/main/res/values-ar/strings.xml index c38c4fc89464..36d1b26f2a9f 100644 --- a/manager/app/src/main/res/values-ar/strings.xml +++ b/manager/app/src/main/res/values-ar/strings.xml @@ -38,14 +38,14 @@ فشل إلغاء التثبيت: %s الإصدار المطور - التراكبات غير متوفرة ، لا يمكن للإضافة أن تعمل! + الوحدات غير متوفرة حيث يتم تعطيل نظام الملفات المتراكب بواسطة النواة. إنعاش إظهار تطبيقات النظام إخفاء تطبيقات النظام إرسال السجلات الوضع الآمن إعادة التشغيل لتطبيق التغييرات - تم تعطيل الإضافات لأنها تتعارض مع Magisk! + الوحدات غير متاحة بسبب تعارضها مع Magisk! تعلم KernelSU https://kernelsu.org/guide/what-is-kernelsu.html تعرف على كيفية تثبيت KernelSU واستخدام الإضافات @@ -56,7 +56,7 @@ تحديث تحميل الإضافة: %s ابدأ التنزيل: %s - الإصدار الجديد: %s متاح ، انقر للتحديث + الإصدار الجديد: %s متاح ، انقر للتحديث. تشغيل الإفتراضي نموذج @@ -118,17 +118,18 @@ اختر KMI يوصى باستخدام صورة القسم %1$s تصغير الصورة المتفرقة - قم بتغيير حجم الصورة المتفرقة حيث توجد الإضافة إلى حجمها الفعلي. لاحظ أن هذا قد يتسبب في عمل الإضافة بشكل غير طبيعي، لذا يرجى استخدامها فقط عند الضرورة (مثل النسخ الاحتياطي) + قم بتغيير حجم الصورة المتفرقة حيث توجد الإضافة إلى حجمها الفعلي. لاحظ أن هذا قد يتسبب في عمل الإضافة بشكل غير طبيعي، لذا يرجى استخدامها فقط عند الضرورة (مثل النسخ الاحتياطي). إلغاء التثبيت إلغاء التثبيت مؤقتًا إلغاء التثبيت بشكل دائم استعادة الصورة الاصلية - ‬إلغاء تثبيت KernelSU (الجذر وجميع الوحدات) بشكل كامل ودائم. + ‬إلغاء تثبيت KernelSU .(الجذر وجميع الوحدات) بشكل كامل ودائم. تركيب نجح التركيب فشل التركيب - صورة lkm المحددة: %s + LKM المحددة: %s استعادة صورة المصنع المخزنة (في حالة وجود نسخة احتياطية)، والتي تُستخدم عادة قبل OTA؛ إذا كنت بحاجة إلى إلغاء تثبيت KernelSU، فيرجى استخدام \"إلغاء التثبيت الدائم\". قم بإلغاء تثبيت KernelSU مؤقتًا، واستعد إلى حالته الأصلية بعد إعادة التشغيل التالية. حفظ السجلات + إجراء \ No newline at end of file diff --git a/manager/app/src/main/res/values-de/strings.xml b/manager/app/src/main/res/values-de/strings.xml index 047123467301..721dba59e8b5 100644 --- a/manager/app/src/main/res/values-de/strings.xml +++ b/manager/app/src/main/res/values-de/strings.xml @@ -5,117 +5,130 @@ Permissiv Funktioniert Version: %d - SuperUser - Tippen zum Installieren + Superuser + Tippe zum Installieren Superuser: %d Unbekannt Erzwingen - Neustart in Bootloader - Neustart in Download-Modus - Neustart mit EDL-Modus + In den Bootloader-Modus neustarten + In den Download-Modus neustarten + In den EDL-Modus neustarten Autor - overlayfs nicht verfügbar, Modul kann nicht funktionieren! - Über - Module sind deaktiviert, weil es einen Konflikt mit Magisk gibt! + Module sind nicht verfügbar, da OverlayFS vom Kernel deaktiviert ist. + Über KernelSU + Module sind aufgrund eines Konfliktes mit Magisk nicht verfügbar! https://kernelsu.org/guide/what-is-kernelsu.html - Erfahren, wie KernelSU installiert und Module verwendet werden + Erfahre, wie KernelSU installiert wird und wie Module verwendet werden Unterstütze uns KernelSU ist und wird immer frei und quelloffen sein. Du kannst uns jedoch deine Unterstützung zeigen, indem du eine Spende tätigst. SELinux-Kontext Module standardmäßig aushängen - 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. + 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. Standard Vorlage Benutzerdefiniert App-Profilaktualisierung für %s fehlgeschlagen - Vererbt + Geerbt Global Individuell Domäne Aktualisieren Wenn du diese Option aktivierst, kann KernelSU alle von den Modulen für diese App geänderten Dateien wiederherstellen. Regeln - Herunterladen startet: %s - Fehler beim Aktualisieren der SELinux-Regeln für: %s + Starte Download: %s + Aktualisieren der SELinux-Regeln schlug fehl für: %s Starten - Neue Version: %s verfügbar, tippen zum Aktualisieren + Neue Version %s verfügbar, tippen zum Aktualisieren. Stopp erzwingen - Neustart + Neustarten Module: %d Manager-Version - SELinux-Status + SELinux Status Deaktiviert Modulaktivierung fehlgeschlagen: %s Moduldeaktivierung fehlgeschlagen: %s - Keine Module installiert + Keine Modul installiert Modul Deinstallieren Installieren Neustarten Einstellungen - Neustart in Recovery + In den Recovery-Modus neustarten %s deinstalliert Version - Neu laden + Aktualisieren System-Apps anzeigen System-Apps ausblenden Protokoll senden KernelSU verstehen Sicherer Modus Neustarten, damit Änderungen wirksam werden - Quellcode unter %1$s ansehen
Unserem %2$s-Kanal beitreten
+ Schau dir den Quellcode an auf %1$s
Trete unserem %2$s Kanal bei
Profilname Namespace einhängen Gruppen Fähigkeiten Module aushängen - Modul herunterladen: %s + Lädt Modul %s herunter Nicht unterstützt KernelSU unterstützt derzeit nur GKI-Kernel Kernel Fingerabdruck Installieren Soft-Reboot - Sicher, dass du das Modul %s deinstallieren möchtest\? + Möchtest du wirklich Modul %s deinstallieren? Deinstallation fehlgeschlagen: %s - Die aktuelle Kernel-Version %d ist zu alt für diese Manager-Version. Bitte auf Version %d oder höher upgraden! + Die aktuelle KernelSU-Version %d ist zu alt für diese Manager-Version. Bitte auf Version %d oder höher aktualisieren! Änderungsprotokoll Erfolgreich importiert In Zwischenablage exportieren Kann lokale Vorlage nicht finden! Vorlagen-ID existiert bereits! Aus Zwischenablage importieren - Konnte Changelog nicht laden: %s + Konnte Veränderungs-Protokoll nicht laden: %s Name Ungültige Vorlagen-ID Online-Vorlagen synchronisieren Vorlage erstellen Schreibgeschützt Import/Export - Fehler beim Speichern + Schlug beim Speichern der Vorlage fehl Vorlage bearbeiten ID - App-Profil-Template + App-Profil-Vorlage Beschreibung Speichern - verwalte lokale und online Profil Vorlagen + Verwalte die lokale und online Vorlage des App-Profils Löschen Zwischenablage ist leer! Vorlage ansehen WebView-Debugging aktivieren - Kann verwendet werden zum Debugging von WebUI, bitte nur falls nötig aktivieren. + Kann zum Fehlerbeheben der WebUI verwendet werden, bitte nur im Notfall aktivieren. %1$s Partitionsabbild empfohlen KMI auswählen Weiter Direkte Installation (empfohlen) Datei auswählen In inaktiven Slot installieren (nach OTA) - Nach einem Neustart wird dein Gerät **GEZWUNGEN** in den derzeit inaktiven Slot zu booten. + Nach einem Neustart wird dein Gerät **GEZWUNGEN** in den derzeit inaktiven Slot zu starten! \nBenutze dies nur nach Fertigstellung des OTA. \nFortfahren? Root-Zugriff konnte nicht gewährt werden! Öffnen - Updates suchen - Automatisch nach Updates suchen beim Öffnen der App + Auf Aktualisierung prüfen + Prüfe automatisch auf Aktualisierungen, wenn die App geöffnet wird + Temporär deinstallieren + Deinstallieren + KernelSU (Root und alle Module) vollständig und dauerhaft deinstallieren. + Ä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). Protokolle Speichern + Permanent deinstallieren + Standard-Abbild wiederherstellen + KernelSU temporär deinstallieren, originalen Status nach dem nächsten Neustart wiederherstellen. + 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\". + Schreibt + Schreiben erfolgreich + Schreiben fehlgeschlagen + Wähle LKM: %s + Spärliches Bild minimieren \ No newline at end of file diff --git a/manager/app/src/main/res/values-es/strings.xml b/manager/app/src/main/res/values-es/strings.xml index 5623b47a2943..a630db99d132 100644 --- a/manager/app/src/main/res/values-es/strings.xml +++ b/manager/app/src/main/res/values-es/strings.xml @@ -2,57 +2,57 @@ Inicio No instalado - Toca para instalar + Haz clic para instalar Funcionando Versión: %d Superusuarios: %d Módulos: %d - No soportado - Por el momento, KernelSU solo es compatible con kernels genéricos (GKIs) + Sin soporte + KernelSU solo admite kernels GKI por ahora Versión del kernel Versión del gestor Huella del dispositivo Estado de SELinux Desactivado - Enforcing de SELinux + Estricto Permisivo Desconocido Superusuario - No se pudo habilitar el módulo \"%s\" - No se pudo deshabilitar el módulo \"%s\" - No hay ningún módulo instalado + Error al activar el módulo: %s + Error al desactivar el módulo: %s + Ningún módulo instalado Módulo Desinstalar - Instalar módulo + Instalar Instalar Reiniciar Ajustes - Reinicio minimo + Reinicio suave Reiniciar en modo de recuperación Reiniciar en modo de arranque Reiniciar en modo Download Reiniciar en modo EDL Acerca de - ¿Estás seguro de que quieres desinstalar el módulo \"%s\"? - \"%s\" esta desinstalado - No se pudo desinstalar \"%s\" + ¿Está seguro de que desea desinstalar el módulo %s? + %s desinstalado + Fallo al desinstalar: %s Versión Autor - El módulo no puede funcionar ya que OverlayFS no está disponible! - Recargar - Mostrar applicaciones del sistema - Ocultar applicaciones del sistema - Enviar registro de informe + Los módulos no están disponibles ya que OverlayFS está desactivado por el kernel. + Refrescar + Mostrar aplicaciones del sistema + Ocultar aplicaciones del sistema + Enviar registros Modo seguro Reinicia para aplicar cambios - Se deshabilitaron los módulos ya que entran en conflicto con los de Magisk! + ¡Los módulos no están disponibles debido a un conflicto con Magisk! Aprende KernelSU https://kernelsu.org/guide/what-is-kernelsu.html - Descubre cómo instalar KernelSU y utilizar módulos + Aprende a instalar KernelSU y a utilizar módulos Apóyanos - KernelSU es y siempre será, libre y de código abierto. De todas formas, puedes mostrarnos tu apoyo mediante una donación. - Mirar el código en %1$s
Únete a nuestro canal de %2$s
+ KernelSU es, y siempre será, gratuito y de código abierto. Sin embargo, puedes demostrarnos que te importamos haciendo una donación. + Ver código fuente en %1$s
Únete a nuestro canal %2$s
Predeterminado Plantilla Personalizado @@ -63,45 +63,70 @@ Individual Grupos Capacidades - Contexto de SELinux + Contexto SELinux Desmontar módulos - No se pudo actualizar el perfil de la applicación para %s + Error al actualizar el perfil de la aplicación para %s Desmontar módulos por defecto - 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. - Si habilitas esta opción, KernelSU podrá restaurar cualquier archivo modificado por los módulos para esta app. + 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. + Activar esta opción permitirá a KernelSU restaurar cualquier archivo modificado por los módulos para esta aplicación. Dominio Reglas Actualizar - Descargando módulo: \"%s\" + Descargando módulo: %s Iniciar descarga: %s - Nueva versión: %s está disponible, toque para actualizar - Abrir Aplicacion - Forzar cierre de la aplicacion - Reiniciar aplicacion - Falló al actualizar reglas de SEpolicy por: %s - 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! + La nueva versión %s está disponible, haga clic para actualizar. + Iniciar + Forzar detención + Reiniciar + Error al actualizar las reglas SELinux para: %s + 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! Registro de cambios - Importado con exíto - Exportar a portapapeles - No se puede encontrar plantilla local para exportar! - Ya existe un ID de la plantilla! - Importar desde portapapeles - Error en obtener los registros de cambios: %s + Importado con éxito + Exportar al portapapeles + ¡No se encuentra la plantilla local para exportar! + ¡El ID de plantilla ya existe! + Importar desde el portapapeles + Fallo en la obtención del registro de cambios: %s Nombre - ID de la plantilla es invalido - Sincronizar plantillas en linea + ID de plantilla no válida + Sincronizar plantillas en línea Crear plantilla - sololectura + Sólo lectura Importar/Exportar - Fallo en guardar plantilla + No se ha podido guardar la plantilla Editar plantilla - id - Plantilla del perfil de la aplicacion + ID + Plantilla de perfil de aplicación Descripción Guardar - Administrar las plantillas locales y de en linea del perfil de la aplicacion + Gestionar la plantilla local y en línea de App Profile Eliminar - El portapapeles esta vacio! + ¡El portapapeles está vacío! Ver plantilla - Guardar Registros + Guardar registros + Activar la depuración de WebView + Se recomienda la imagen de partición %1$s + Selecciona KMI + Siguiente + Instalación directa (Recomendada) + ¡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? + Desinstalar + Restaurar imagen de archivo + Desinstalar temporalmente KernelSU, restaurar al estado original tras el siguiente reinicio. + LKM seleccionado: %s + Flash falló + Éxito de Flash + ¡No se ha podido conceder el acceso root! + Abrir + Seleccione un archivo + Instalar en ranura inactiva (Después de OTA) + Desinstalar temporalmente + Desinstalar permanentemente + Desinstalar KernelSU (Root y todos los módulos) completa y permanentemente. + 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). + Comprobar actualización + Comprobación automática de actualizaciones al abrir la aplicación + Minimizar la imagen dispersa + Puede ser usado para depurar WebUI, por favor habilítalo sólo cuando sea necesario. + 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\".
\ No newline at end of file diff --git a/manager/app/src/main/res/values-et/strings.xml b/manager/app/src/main/res/values-et/strings.xml index d514fc6aea79..4f177ff884a7 100644 --- a/manager/app/src/main/res/values-et/strings.xml +++ b/manager/app/src/main/res/values-et/strings.xml @@ -12,8 +12,8 @@ Taaskäivita Taaskäivita taastusesse Kas soovid kindlasti eemaldada mooduli %s? - %s on eemaldatud - Raporteeri logi + %s eemaldatud + Saada logid Turvarežiim Muudatuste rakendamiseks taaskäivita Õpi KernelSUd @@ -28,8 +28,8 @@ Muuda malli Rakenduseprofiili mall ID - vaid lugemiseks - malli ID juba eksisteerib! + Vaid lugemiseks + Malli ID juba eksisteerib! Ekspordi lõikelauale Sünkrooni võrgumallid Muudatuste logi hankimine ebaõnnestus: %s @@ -59,10 +59,10 @@ Autor Eemaldamine ebaõnnestus: %s Versioon - overlayfs pole saadaval, moodul ei saa töötada! + Moodulid pole saadaval, kuna OverlayFS on kernelis keelatud. Kuva süsteemirakendused Peida süsteemirakendused - Moodulid on keelatud, kuna need lähevad konflikti Magiski omadega! + Moodulid pole saadaval Magiski konflikti tõttu! Õpi KernelSUd paigaldama ja mooduleid kasutama Toeta meid Grupid @@ -77,14 +77,14 @@ Võimekused Sobimatu malli ID SELinux kontekst - Praegune KernelSU versioon %d on liiga madal, haldur ei saa konkreetselt toimida. Palun täienda versioonile %d või kõrgem! + Praegune KernelSU versioon %d on liiga madal, haldur ei saa korrektselt töötada. Palun täienda versioonile %d või kõrgem! Domeen Käivita Sundpeata Reeglid Uuenda Mooduli allalaadimine: %s - Uus versioon: %s on saadaval, klõpsa täiendamiseks + Uus versioon %s on saadaval, klõpsa täiendamiseks. Taaskäivita Muudatuste logi Nimi @@ -100,8 +100,8 @@ Loo mall Halda kohalikke ja võrgusolevaid rakenduseprofiili malle Selle valiku lubamine lubab KernelSU-l taastada selle rakenduse moodulite poolt mistahes muudetud faile. - Eksportimiseks kohalikku malli ei leitud! - Globaalne vaikeväärtus \"Lahtihaagitud moodulitele\" rakenduseprofiilides. Lubamisel eemaldab see kõik moodulite süsteemimuudatused rakendustele, millel ei ole profiili määratud. + Ei saa eksportida, kohalikku malli ei leitud! + Globaalne vaikeväärtus \"Lahtihaagitud moodulitele\" rakenduseprofiilis. Lubamisel eemaldab see kõik moodulite süsteemimuudatused rakendustele, millel ei ole profiili määratud. Saab kasutada WebUI silumiseks, palun luba ainult vajadusel. Juurkasutaja andmine ebaõnnestus! Kontrolli uuendusi @@ -109,4 +109,22 @@ Ava Luba WebView silumine Salvesta Logid + Vali KMI + %1$s partitsioonitõmmis on soovitatud + Edasi + Sinu seade **SUNNITAKSE** pärast taaskäivitust ebaaktiivsesse lahtrisse käivituma!\nKasuta seda valikut vaid siis, kui tegid üle-õhu uuenduse.\nJätkad? + Eemalda + Eemalda KernelSU ajutiselt, taasta pärast taaskäivitust algseisu. + KernelSU eemaldamine (juurkasutaja ja kõik moodulid) täielikult ja püsivalt. + Taasta tehase-vaiketõmmis (kui varundus eksisteerib), tavaliselt kasutatakse enne üle-õhu uuendust; kui soovid KernelSU-d eemaldada, palun kasuta \"Eemalda püsivalt\". + Välgutamine + Välgutamine õnnestus + Välgutamine ebaõnnestus + Valitud LKM: %s + Otsene paigaldus (soovitatud) + Vali fail + Paigalda ebaaktiivsesse lahtrisse (pärast üle-õhu uuendust) + Eemalda ajutiselt + Eemalda püsivalt + Taasta vaikimisi tõmmis \ No newline at end of file diff --git a/manager/app/src/main/res/values-fa/strings.xml b/manager/app/src/main/res/values-fa/strings.xml index a5cdb2f7ff58..17469cc8a892 100644 --- a/manager/app/src/main/res/values-fa/strings.xml +++ b/manager/app/src/main/res/values-fa/strings.xml @@ -1,67 +1,68 @@ + -خانه -نصب نشده است -برای نصب ضربه بزنید -به درستی کار می‌کند -نسخه: %d -برنامه های با دسترسی روت: %d -ماژول‌ها: %d -پشتیبانی نشده -کرنل اس یو فقط هسته های gki را پشتیبانی میکند + خانه + نصب نشده است + برای نصب ضربه بزنید + به درستی کار می‌کند + نسخه: %d + برنامه های با دسترسی روت: %d + ماژول‌ها: %d + پشتیبانی نشده + کرنل اس یو فقط هسته های gki را پشتیبانی میکند هسته نسخه برنامه اثرانگشت وضعیت SELinux -غیرفعال -قانونمند -آزاد -ناشناخته -دسترسی روت + غیرفعال + قانونمند + آزاد + ناشناخته + دسترسی روت فعال کردن ماژول ناموفق بود: %s -غیرفعال کردن ماژول ناموفق بود: %s -هیچ ماژولی نصب نشده است -ماژول -لغو نصب -نصب -نصب -راه اندازی دوباره -تنظیمات -راه اندازی نرم -راه اندازی به ریکاوری -راه اندازی به بوتلودر -راه اندازی به حالت دانلود -راه اندازی به EDL -درباره + غیرفعال کردن ماژول ناموفق بود: %s + هیچ ماژولی نصب نشده است + ماژول + لغو نصب + نصب + نصب + راه اندازی دوباره + تنظیمات + راه اندازی نرم + راه اندازی به ریکاوری + راه اندازی به بوتلودر + راه اندازی به حالت دانلود + راه اندازی به EDL + درباره آیا مطمئنید که میخواهید ماژول %s را پاک کنید؟ -%s پاک شد -پاک کردن ناموفق بود: %s -نسخه -سازنده -overlayfs موجود نیست. مازول کار نمیکند!! -تازه‌سازی -نمایش برنامه های سیستمی -مخفی کردن برنامه های سیستمی -ارسال وقایع -حالت امن -راه‌اندازی مجدد برای تاثیرگذاری -مازول به دلیل تعارض با مجیسک غیرفعال شده اند\'s! -یادگیری کرنل اس یو -https://kernelsu.org/guide/what-is-kernelsu.html -یاد بگیرید چگونه از کرنل اس یو و ماژول ها استفاده کنید -از ما حمایت کنید -KernelSU رایگان است و همیشه خواهد بود و منبع باز است. با این حال، می توانید با اهدای کمک مالی به ما نشان دهید که برایتان مهم است. - + %s پاک شد + پاک کردن ناموفق بود: %s + نسخه + سازنده + overlayfs موجود نیست. مازول کار نمیکند!! + تازه‌سازی + نمایش برنامه های سیستمی + مخفی کردن برنامه های سیستمی + ارسال وقایع + حالت امن + راه‌اندازی مجدد برای تاثیرگذاری + مازول به دلیل تعارض با مجیسک غیرفعال شده اند\'s! + یادگیری کرنل اس یو + https://kernelsu.org/guide/what-is-kernelsu.html + یاد بگیرید چگونه از کرنل اس یو و ماژول ها استفاده کنید + از ما حمایت کنید + KernelSU رایگان است و همیشه خواهد بود و منبع باز است. با این حال، می توانید با اهدای کمک مالی به ما نشان دهید که برایتان مهم است. + Join our %2$s channel ]]> -پروفایل برنامه -پیش‌فرض -قالب -شخصی سازی شده -اسم پروفایل -Mount namespace -اثر گرفته -گلوبال -تکی -جداکردن ماژول ها + پروفایل برنامه + پیش‌فرض + قالب + شخصی سازی شده + اسم پروفایل + Mount namespace + اثر گرفته + گلوبال + تکی + جداکردن ماژول ها ذخیره گزارش‌ها - + \ No newline at end of file diff --git a/manager/app/src/main/res/values-fr/strings.xml b/manager/app/src/main/res/values-fr/strings.xml index 01c0672b87a6..a5de872319e1 100644 --- a/manager/app/src/main/res/values-fr/strings.xml +++ b/manager/app/src/main/res/values-fr/strings.xml @@ -5,7 +5,7 @@ Version : %d Super-utilisateurs : %d Modules : %d - Actuellement, KernelSU ne supporte que les noyaux GKI + KernelSU ne prend désormais en charge que les noyaux GKI Noyau Empreinte digitale Mode SELinux @@ -16,12 +16,12 @@ Aucun module installé Accueil Appuyez ici pour installer - Non supporté - Échec de la désinstallation : %s + Non pris en charge + Échec de la désinstallation : %s Version Version du gestionnaire Enforcing - Échec de l\'activation du module : %s + Échec de l\'activation du module : %s Modules Désinstaller Installer @@ -39,20 +39,19 @@ Auteur Êtes-vous sûr(e) de vouloir désinstaller le module %s \? Découvrir KernelSU - OverlayFS est indisponible, impossible de faire fonctionner les modules ! + Les modules sont indisponibles car OverlayFS est désactivé par le noyau. Rafraîchir Afficher les applications système Masquer les applications système Mode sans échec - Rapport de journal + Envoyer les journaux Redémarrez pour appliquer les modifications - Les modules sont désactivés car ils sont en conflit avec ceux de Magisk ! + Les modules sont indisponibles en raison d\'un conflit avec Magisk ! https://kernelsu.org/guide/what-is-kernelsu.html Soutenez-nous Découvrez comment installer KernelSU et utiliser les modules KernelSU est, et restera toujours, gratuit et open source. Vous pouvez cependant nous témoigner de votre soutien en nous faisant un don. - Voir le code source sur %1$s
-\nRejoindre notre canal %2$s
+ Voir le code source sur %1$s
\nRejoindre notre canal %2$s
Modèle Par défaut Personnalisé @@ -66,7 +65,7 @@ Capacités Démonter les modules Échec de la modification du profil d\'application de %s - L\'activation de cette option permettra à KernelSU de restaurer tous les fichiers modifiés par les modules de cette application. + L\'activation de cette option permettra à KernelSU de restaurer tous les fichiers modifiés par les modules pour cette application. Démonter les modules par défaut 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. Domaine @@ -74,7 +73,7 @@ Mettre à jour Téléchargement du module : %s Lancer - La version %s est disponible, appuyez ici pour mettre à jour + La nouvelle version %s est disponible, appuyez ici pour mettre à jour. Début du téléchargement de : %s Forcer l\'arrêt Relancer l\'application @@ -82,20 +81,20 @@ 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 ! Importation réussie Exporter vers le presse-papiers - Impossible de trouver un modèle local à exporter ! - L\'id du modèle existe déjà ! + Impossible de trouver un modèle local à exporter ! + L\'ID du modèle existe déjà ! Journal des modifications Importer à partir du presse-papiers Échec de récupération du journal des modifications : %s Nom - id de modèle invalide + ID de modèle invalide Synchroniser les modèles en ligne Créer un modèle - lecture seule + Lecture seule Importer/exporter Échec de l\'enregistrement du modèle Modifier le modèle - id + ID Modèles de profils d\'application Description Enregistrer @@ -105,7 +104,7 @@ Voir le modèle Vérifier automatiquement les mises à jour à l\'ouverture de l\'application Vérifier les mises à jour - Activer le débogage de WebView + Activer le débogage WebView Peut être utilisé pour déboguer WebUI, n\'activez cette option que si nécessaire. Échec de l\'octroi des privilèges root ! Ouvrir @@ -118,18 +117,18 @@ Suivant L\'image de la partition %1$s est recommandée Sélectionner une KMI - Minimiser l\'image clairsemée - Redimensionne l\'image clairsemée où se trouve le module à sa taille réelle. Notez que cela peut entraîner un dysfonctionnement du module, alors utilisez cette fonctionnalité uniquement lorsque nécessaire (pour la sauvegarde, par exemple) + Minimiser la taille de l\'image creuse + Redimensionne l\'image creuse où le module est situé à sa taille réelle. Notez que cela peut entraîner un dysfonctionnement du module, veuillez utiliser cette option uniquement lorsque cela est nécessaire (par exemple pour la sauvegarde de l\'appareil). Désinstaller Désinstaller temporairement Désinstaller définitivement - Restaurer l\'image stock - 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 ». + Restaurer l\'image d\'origine + Restaurer l\'image d\'origine 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 ». Flash en cours Flash réussi Échec du flash - lkm sélectionné : %s + LKM sélectionné : %s Désinstallation complète et permanente de KernelSU (root et tous les modules). Désinstaller KernelSU temporairement et rétablir l\'état original au redémarrage suivant. - Enregistrer les Journaux + Enregistrer les journaux \ No newline at end of file diff --git a/manager/app/src/main/res/values-gl/strings.xml b/manager/app/src/main/res/values-gl/strings.xml new file mode 100644 index 000000000000..89956f2306d6 --- /dev/null +++ b/manager/app/src/main/res/values-gl/strings.xml @@ -0,0 +1,4 @@ + + + Inicio + \ No newline at end of file diff --git a/manager/app/src/main/res/values-in/strings.xml b/manager/app/src/main/res/values-in/strings.xml index 98698f529aa9..f2223e06ca8f 100644 --- a/manager/app/src/main/res/values-in/strings.xml +++ b/manager/app/src/main/res/values-in/strings.xml @@ -10,7 +10,7 @@ Tidak didukung KernelSU saat ini hanya mendukung kernel GKI Kernel - Versi Manager + Versi manager Identitas Status SELinux Nonaktif @@ -38,11 +38,11 @@ Gagal menghapus: %s Versi Oleh - OverlayFS tidak tersedia, modul tidak berfungsi! + Kernel tidak mendukung OverlayFS, modul tidak berfungsi. Muat ulang Tampilkan aplikasi sistem Sembunyikan aplikasi sistem - Laporkan Log + Kirim Log Mode aman Reboot agar berfungsi Konflik dengan Magisk, fungsi modul ditiadakan! @@ -67,16 +67,16 @@ Umount Modul Gagal membarui Profil pada %s Melepas Modul secara bawaan - Menggunakan \"Umount Modul\" secara universal pada Profil aplikasi. Jika diaktifkan, akan menghapus semua modifikasi sistem untuk aplikasi yang tidak memiliki set Profil. + Menggunakan \"Umount Modul\" secara universal pada Profil Aplikasi. Jika diaktifkan, akan menghapus semua modifikasi sistem untuk aplikasi yang tidak memiliki set profil. Aktifkan opsi ini agar KernelSU dapat memulihkan kembali berkas termodifikasi oleh modul pada aplikasi ini. Domain Aturan Membarui Mengunduh modul: %s Mulai mengunduh: %s - Tersedia versi terbaru: %s, Klik untuk membarui + Tersedia versi terbaru %s, Klik untuk membarui. Jalankan - Paksa Berhenti + Paksa berhenti Mulai ulang Gagal membarui aturan SELinux pada: %s Versi KernelSU %d terlalu rendah agar manajer berfungsi normal. Harap membarui ke versi %d atau di atasnya! @@ -84,25 +84,25 @@ Berhasil diimpor Ekspor ke papan klip Tidak ditemukan templat lokal untuk diekspor! - Id templat sudah ada! + ID templat sudah ada! Impor dari papan klip Gagal mengambil Changelog: %s Nama Id templat tidak valid Sinkronkan templat daring - Buat Templat + Buat templat Impor/Ekspor Gagal menyimpan templat - Edit Templat - id + Edit templat + ID Templat Profil Aplikasi Deskripsi Simpan Atur templat Profil yang lokal dan daring Hapus Papan klip kosong! - Lihat Templat - ReadOnly + Lihat templat + readonly Pengawakutuan WebView Dapat mengawakutu WebView, hanya aktifkan jika butuh. %1$s image partisi terekomendasi @@ -111,7 +111,7 @@ Gawai akan **DIPAKSA** untuk but ke slot nonaktif! \nHANYA gunakan setelah proses OTA selesai. \nLanjutkan? - Instal Langsung (rekomendasi) + Instal langsung (rekomendasi) Pilih berkas Instal ke slot nonaktif (setelah OTA) Gagal memberikan akses root! @@ -119,14 +119,14 @@ Cek terbaru Cek terbaru setiap membuka aplikasi Meminimalkan sparse image - Mengembalikan sparse image, lokasi modul disimpan, ke ukuran sebenarnya. Dapat menyebabkan modul bekerja abnormal, maka gunakan saat dibutuhkan saja (mis. untuk pencadangan) + Mengembalikan sparse image, lokasi modul disimpan, ke ukuran sebenarnya. Dapat menyebabkan modul bekerja abnormal, maka gunakan saat dibutuhkan saja (mis. untuk pencadangan). Hapus permanen KernelSU (root dan modul). - Hapus Temporer - Pulihkan Image Asal + Hapus temporer + Pulihkan image asal Hapus Hapus Temporer KernelSU, pulihkan ke kondisi asali setelah but berikutnya. - Hapus Permanen - Pulihkan image bawaan ROM (jika cadangan tersedia), umumnya dilakukan sebelum OTA; jika ingin menghapus KernelSU, gunakan fungsi \"Hapus Permanen\". + Hapus permanen + Pulihkan image bawaan ROM (jika cadangan tersedia), umumnya dilakukan sebelum OTA; jika ingin menghapus KernelSU, gunakan fungsi \"Hapus permanen\". Pemasangan Berhasil LKM dipilih: %s Pasang diff --git a/manager/app/src/main/res/values-it/strings.xml b/manager/app/src/main/res/values-it/strings.xml index 7bd3f9247356..642640b3cff4 100644 --- a/manager/app/src/main/res/values-it/strings.xml +++ b/manager/app/src/main/res/values-it/strings.xml @@ -45,7 +45,7 @@ Invia log Modalità provvisoria Riavvia per applicare la modifica - I moduli sono disabilitati perché in conflitto con quelli di Magisk! + I moduli sono disabilitati perché in conflitto con Magisk! Scopri KernelSU https://kernelsu.org/guide/what-is-kernelsu.html Scopri come installare KernelSU e utilizzare i moduli @@ -82,16 +82,16 @@ Registro aggiornamenti Crea modello Modifica modello - identificativo + identificatore Identificativo modello non valido Nome Visualizza modello Sola lettura - L\'identificativo del modello esiste già! + L\'identificatore del modello è già in uso! Importa/Esporta Importa dagli appunti Esporta negli appunti - Impossibile trovare modello locale da esportare! + Impossibile trovare un modello locale da esportare! Importato con successo Sincronizza i modelli remoti Gli appunti sono vuoti! @@ -106,29 +106,29 @@ Impossibile reperire il changelog: %s Controlla aggiornamenti Controlla automaticamente la disponibilità di aggiornamenti all\'apertura dell\'applicazione - Abilita il Debug di WebView + Abilita il debug di WebView Può essere usato per svolgere il debug di WebUI, è consigliato attivarlo solo quando necessario. È consigliato usare immagine della partizione %1$s Scegli il KMI Avanti Installazione diretta (Raccomandata) - Scegli un File - Installa nello Slot Inattivo (Dopo OTA) + Scegli un file + Installa nello slot inattivo (dopo OTA) 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? Riduci la dimensione dell\'immagine moduli sparse al minimo - 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) + 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) Disinstalla - Disinstalla Temporaneamente - Disinstalla Permanentemente + Disinstalla temporaneamente + Disinstalla permanentemente Ripristina immagine originale del produttore Disinstalla temporaneamente KernelSU, ripristina lo stato originale dopo il prossimo riavvio. - Disinstalla KernelSU (Root e tutti i moduli) completamente e permanentemente. + Disinstalla KernelSU (root e tutti i moduli) completamente e permanentemente. Installazione Installazione completata Installazione fallita LKM selezionato: %s - 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\". + 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\". Salva Registri \ No newline at end of file diff --git a/manager/app/src/main/res/values-ja/strings.xml b/manager/app/src/main/res/values-ja/strings.xml index 4ecf7420447a..bf2108a85a16 100644 --- a/manager/app/src/main/res/values-ja/strings.xml +++ b/manager/app/src/main/res/values-ja/strings.xml @@ -20,7 +20,7 @@ スーパーユーザー モジュールの有効化に失敗: %s モジュールの無効化に失敗: %s - モジュールをインストールしていません + モジュールがインストールされていません モジュール アンインストール インストール @@ -38,19 +38,19 @@ アンインストールに失敗: %s バージョン 制作者 - OverlayFS が有効でないためモジュールは動作しません! + カーネルによって OverlayFS が無効になっているため、モジュールが利用できません。 更新 システムアプリを表示 システムアプリを非表示 ログを送信 セーフモード 再起動すると有効化されます - Magisk と競合しているためモジュールは無効になっています! + モジュールが Magisk との競合により利用できません! KernelSU について https://kernelsu.org/ja_JP/guide/what-is-kernelsu.html KernelSU のインストール方法やモジュールの使い方はこちら 支援する - KernelSU はこれからもずっとフリーでオープンソースです。寄付をすることで私たちを気にかけていることを示せます。 + KernelSU はこれからもずっと無料でオープンソースです。寄付をして頂くことで、開発を支援していただけます。 %2$s チャンネルに参加]]> アプリのプロファイル 既定 @@ -67,7 +67,7 @@ %s のアプリのプロファイルの更新をできませでした ドメイン ルール - 新しいバージョン: %s が利用可能です。タップしてダウンロード + 新しいバージョン %s が利用可能です。タップしてダウンロード。 アップデート ダウンロードを開始: %s 起動 @@ -84,7 +84,7 @@ インポート成功 クリップボードからエクスポート エクスポートするローカル テンプレートが見つかりません! - テンプレート id はすでに存在します! + テンプレート ID はすでに存在します! クリップボードからインポート 変更ログの取得に失敗しました: %s 名前 @@ -95,7 +95,7 @@ インポート/エクスポート テンプレートの保存に失敗しました テンプレートの編集 - id + ID アプリプロファイルのテンプレート 説明 保存 @@ -119,7 +119,7 @@ 直接インストール (推奨) ファイルを選択してください スパースイメージを最小化 - モジュールが配置されているスパースイメージのサイズを実際のサイズに変更します。 モジュールが正常に動作しなくなる可能性がありますので、必要な場合にのみご使用ください + モジュールが配置されているスパースイメージのサイズを実際のサイズに変更します。 モジュールが正常に動作しなくなる可能性がありますので、必要な場合にのみご使用ください。 完全にアンインストールする ストックイメージを復元 一時的にアンインストールする @@ -130,6 +130,6 @@ フラッシュ フラッシュ成功 フラッシュ失敗 - 選択された lkm: %s + 選択された LKM: %s ログを保存 \ No newline at end of file diff --git a/manager/app/src/main/res/values-night-v27/themes.xml b/manager/app/src/main/res/values-night-v27/themes.xml deleted file mode 100644 index 10a773b16f28..000000000000 --- a/manager/app/src/main/res/values-night-v27/themes.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - - \ No newline at end of file diff --git a/manager/app/src/main/res/values-night/themes.xml b/manager/app/src/main/res/values-night/themes.xml index 91abf657bfdc..d76ba8e6852b 100644 --- a/manager/app/src/main/res/values-night/themes.xml +++ b/manager/app/src/main/res/values-night/themes.xml @@ -1,10 +1,10 @@ - + - + + - \ No newline at end of file diff --git a/manager/app/src/main/res/values-zh-rCN/strings.xml b/manager/app/src/main/res/values-zh-rCN/strings.xml index e854fb60bcd1..d56b625a91ff 100644 --- a/manager/app/src/main/res/values-zh-rCN/strings.xml +++ b/manager/app/src/main/res/values-zh-rCN/strings.xml @@ -37,14 +37,14 @@ 卸载失败: %s 版本 作者 - 内核不支持 overlayfs,模块功能无法运作! + OverlayFS被内核禁用,模块不可用。 刷新 显示系统应用 隐藏系统应用 发送日志 安全模式 重启生效 - 所有模块已被禁用,因为它与 Magisk 的模块系统有冲突! + 因与Magisk有冲突,所有模块不可用! 模块数:%d 了解 KernelSU https://kernelsu.org/zh_CN/guide/what-is-kernelsu.html @@ -74,7 +74,7 @@ 更新 正在下载模块:%s 开始下载:%s - 发现新版本:%s,点击升级 + 发现新版本:%s,点击升级。 启动 强制停止 重新启动 @@ -105,6 +105,7 @@ 检查更新 在应用启动后自动检查是否有最新版 获取 root 失败! + 执行 打开 启用 WebView 调试 可用于调试 WebUI ,请仅在需要时启用。 @@ -129,4 +130,5 @@ 刷写失败 选择的 LKM :%s 保存日志 + 日志已保存 \ No newline at end of file diff --git a/manager/app/src/main/res/values/colors.xml b/manager/app/src/main/res/values/colors.xml new file mode 100644 index 000000000000..a5b623aa4885 --- /dev/null +++ b/manager/app/src/main/res/values/colors.xml @@ -0,0 +1,4 @@ + + + #FFFFFFFF + \ No newline at end of file diff --git a/manager/app/src/main/res/values/strings.xml b/manager/app/src/main/res/values/strings.xml index d094969af1a2..85ce47da2d8a 100644 --- a/manager/app/src/main/res/values/strings.xml +++ b/manager/app/src/main/res/values/strings.xml @@ -11,17 +11,17 @@ Unsupported KernelSU only supports GKI kernels now Kernel - Manager Version + Manager version Fingerprint SELinux status Disabled Enforcing Permissive Unknown - SuperUser + Superuser Failed to enable module: %s Failed to disable module: %s - No installed modules + No module installed Module Uninstall Install @@ -35,18 +35,18 @@ Reboot to EDL About Are you sure you want to uninstall module %s? - %s is uninstalled + %s uninstalled Failed to uninstall: %s Version Author - overlayfs is not available, module cannot work! + Modules are unavailable as OverlayFS is disabled by the kernel. Refresh Show system apps Hide system apps - Report Log + Send logs Safe mode Reboot to take effect - Modules are disabled because it is conflict with Magisk\'s! + Modules are unavailable due to a conflict with Magisk! Learn KernelSU https://kernelsu.org/guide/what-is-kernelsu.html Learn how to install KernelSU and use modules @@ -67,38 +67,38 @@ SELinux context Umount modules Failed to update App Profile for %s - The current KernelSU version %d is too low for the manager to function properly. Please upgrade to version %d or higher! + The current KernelSU version %d is too low for the manager to work properly. Please upgrade to version %d or higher! Umount modules by default - The global default value for \"Umount modules\" in App Profiles. If enabled, it will remove all module modifications to the system for applications that do not have a Profile set. - Enabling this option will allow KernelSU to restore any modified files by the modules for this application. + The global default value for \"Umount modules\" in App Profile. If enabled, it will remove all module modifications to the system for apps that don\'t have a profile set. + Enabling this option will allow KernelSU to restore any modified files by the modules for this app. Domain Rules Update Downloading module: %s Start downloading: %s - New version: %s is available, click to upgrade + New version %s is available, click to upgrade. Launch - Force Stop + Force stop Restart Failed to update SELinux rules for: %s Changelog App Profile Template Manage local and online template of App Profile - Create Template - Edit Template - id + Create template + Edit template + ID Invalid template id Name Description Save Delete - View Template - readonly - template id already exists! + View template + Read only + Template ID already exists! Import/Export Import from clipboard Export to clipboard - Can not find local template to export! + Cannot find local template to export! Imported successfully Sync online templates Failed to save template @@ -107,28 +107,30 @@ Check update Automatically check for updates when opening the app Failed to grant root! + Action Open - Enable WebView Debugging + Enable WebView debugging Can be used to debug WebUI, please enable only when needed. - Direct Install (Recommended) - Select a File - Install to Inactive Slot (After OTA) + Direct install (Recommended) + Select a file + Install to inactive slot (After OTA) Your device will be **FORCED** to boot to the current inactive slot after a reboot!\nOnly use this option after OTA is done.\nContinue? Next %1$s partition image is recommended Select KMI Minimize sparse image - Resize the sparse image where the module is located to its actual size. Note that this may cause the module to work abnormally, so please only use when necessary (such as for backup) + Resize the sparse image where the module is located to its actual size. Note that this may cause the module to work abnormally, so please only use when necessary (Such as for backup). Uninstall - Uninstall Temporarily - Uninstall Permanently - Restore Stock Image + Uninstall temporarily + Uninstall permanently + Restore stock image Temporarily uninstall KernelSU, restore to original state after next reboot. - Uninstalling KernelSU(Root and all modules) completely and permanently. - Restore the stock factory image (if a backup exists), usually used before OTA; if you need to uninstall KernelSU, please use \"Permanent Uninstall\". + Uninstalling KernelSU (Root and all modules) completely and permanently. + Restore the stock factory image (If a backup exists), usually used before OTA; if you need to uninstall KernelSU, please use \"Uninstall permanently\". Flashing Flash success Flash failed - Selected lkm: %s - Save Logs + Selected LKM: %s + Save logs + Logs saved \ No newline at end of file diff --git a/manager/app/src/main/res/values/themes.xml b/manager/app/src/main/res/values/themes.xml index 7d41d8ec77fc..31721d39e182 100644 --- a/manager/app/src/main/res/values/themes.xml +++ b/manager/app/src/main/res/values/themes.xml @@ -1,10 +1,13 @@ - + - + \ No newline at end of file diff --git a/manager/build.gradle.kts b/manager/build.gradle.kts index 0e503df3b636..3bb25ee907cc 100644 --- a/manager/build.gradle.kts +++ b/manager/build.gradle.kts @@ -15,29 +15,23 @@ cmaker { default { arguments.addAll( arrayOf( - "-DANDROID_STL=c++_static", + "-DANDROID_STL=none", ) ) - val flags = arrayOf( - "-Wno-gnu-string-literal-operator-template", - "-Wno-c++2b-extensions", - ) - cFlags.addAll(flags) - cppFlags.addAll(flags) - abiFilters("arm64-v8a", "x86_64") + abiFilters("arm64-v8a", "x86_64", "riscv64") } buildTypes { if (it.name == "release") { - arguments += "-DDEBUG_SYMBOLS_PATH=${buildDir.absolutePath}/symbols" + arguments += "-DDEBUG_SYMBOLS_PATH=${layout.buildDirectory.asFile.get().absolutePath}/symbols" } } } val androidMinSdkVersion = 26 -val androidTargetSdkVersion = 34 -val androidCompileSdkVersion = 34 -val androidBuildToolsVersion = "34.0.0" -val androidCompileNdkVersion = "26.3.11579264" +val androidTargetSdkVersion = 35 +val androidCompileSdkVersion = 35 +val androidBuildToolsVersion = "35.0.0" +val androidCompileNdkVersion = "27.0.12077973" val androidSourceCompatibility = JavaVersion.VERSION_21 val androidTargetCompatibility = JavaVersion.VERSION_21 val managerVersionCode by extra(getVersionCode()) @@ -85,6 +79,9 @@ subprojects { versionCode = managerVersionCode versionName = managerVersionName } + ndk { + abiFilters += listOf("arm64-v8a", "x86_64", "riscv64") + } } lint { diff --git a/manager/gradle/libs.versions.toml b/manager/gradle/libs.versions.toml index 6ec08d63e90b..ef564f8c0644 100644 --- a/manager/gradle/libs.versions.toml +++ b/manager/gradle/libs.versions.toml @@ -1,21 +1,20 @@ [versions] -agp = "8.4.1" -kotlin = "2.0.0" -ksp = "2.0.0-1.0.21" -compose-bom = "2024.05.00" -lifecycle = "2.8.0" -accompanist = "0.34.0" -navigation = "2.7.7" -activity-compose = "1.9.0" -kotlinx-coroutines = "1.8.1" -coil-compose = "2.6.0" -compose-destination = "1.10.2" +agp = "8.7.1" +kotlin = "2.0.21" +ksp = "2.0.21-1.0.26" +compose-bom = "2024.10.00" +lifecycle = "2.8.6" +navigation = "2.8.3" +activity-compose = "1.9.3" +kotlinx-coroutines = "1.9.0" +coil-compose = "2.7.0" +compose-destination = "2.1.0-beta14" sheets-compose-dialogs = "1.3.0" markdown = "4.6.2" -webkit = "1.11.0" +webkit = "1.12.1" appiconloader-coil = "1.5.0" parcelablelist = "2.0.1" -libsu = "5.2.2" +libsu = "6.0.0" apksign = "1.4" cmaker = "1.2" @@ -51,14 +50,9 @@ androidx-lifecycle-viewmodel-compose = { group = "androidx.lifecycle", name = "l androidx-webkit = { module = "androidx.webkit:webkit", version.ref = "webkit" } -com-google-accompanist-drawablepainter = { group = "com.google.accompanist", name = "accompanist-drawablepainter", version.ref = "accompanist" } -com-google-accompanist-navigation-animation = { group = "com.google.accompanist", name = "accompanist-navigation-animation", version.ref = "accompanist" } -com-google-accompanist-systemuicontroller = { group = "com.google.accompanist", name = "accompanist-systemuicontroller", version.ref = "accompanist" } -com-google-accompanist-webview = { group = "com.google.accompanist", name = "accompanist-webview", version.ref = "accompanist" } - com-github-topjohnwu-libsu-core = { group = "com.github.topjohnwu.libsu", name = "core", version.ref = "libsu" } com-github-topjohnwu-libsu-service = { group = "com.github.topjohnwu.libsu", name = "service", version.ref = "libsu" } -com-github-topjohnwu-libsu-io= { group = "com.github.topjohnwu.libsu", name = "io", version.ref = "libsu" } +com-github-topjohnwu-libsu-io = { group = "com.github.topjohnwu.libsu", name = "io", version.ref = "libsu" } dev-rikka-rikkax-parcelablelist = { module = "dev.rikka.rikkax.parcelablelist:parcelablelist", version.ref = "parcelablelist" } @@ -68,11 +62,13 @@ kotlinx-coroutines-core = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-c me-zhanghai-android-appiconloader-coil = { group = "me.zhanghai.android.appiconloader", name = "appiconloader-coil", version.ref = "appiconloader-coil" } -compose-destinations-animations-core = { group = "io.github.raamcosta.compose-destinations", name = "animations-core", version.ref = "compose-destination" } +compose-destinations-core = { group = "io.github.raamcosta.compose-destinations", name = "core", version.ref = "compose-destination" } compose-destinations-ksp = { group = "io.github.raamcosta.compose-destinations", name = "ksp", version.ref = "compose-destination" } sheet-compose-dialogs-core = { group = "com.maxkeppeler.sheets-compose-dialogs", name = "core", version.ref = "sheets-compose-dialogs" } sheet-compose-dialogs-list = { group = "com.maxkeppeler.sheets-compose-dialogs", name = "list", version.ref = "sheets-compose-dialogs" } sheet-compose-dialogs-input = { group = "com.maxkeppeler.sheets-compose-dialogs", name = "input", version.ref = "sheets-compose-dialogs" } -markdown = { group = "io.noties.markwon", name = "core", version.ref = "markdown" } \ No newline at end of file +markdown = { group = "io.noties.markwon", name = "core", version.ref = "markdown" } + +lsposed-cxx = { module = "org.lsposed.libcxx:libcxx", version = "27.0.12077973" } \ No newline at end of file diff --git a/manager/gradle/wrapper/gradle-wrapper.jar b/manager/gradle/wrapper/gradle-wrapper.jar index e6441136f3d4..2c3521197d7c 100644 Binary files a/manager/gradle/wrapper/gradle-wrapper.jar and b/manager/gradle/wrapper/gradle-wrapper.jar differ diff --git a/manager/gradle/wrapper/gradle-wrapper.properties b/manager/gradle/wrapper/gradle-wrapper.properties index b82aa23a4f05..df97d72b8b91 100644 --- a/manager/gradle/wrapper/gradle-wrapper.properties +++ b/manager/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/manager/gradlew b/manager/gradlew index 4b6a9cfc46fa..f5feea6d6b11 100755 --- a/manager/gradlew +++ b/manager/gradlew @@ -1,4 +1,5 @@ #!/bin/sh + # # Copyright © 2015-2021 the original authors. # @@ -14,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -54,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -83,7 +86,8 @@ done # shellcheck disable=SC2034 APP_BASE_NAME=${0##*/} # Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) -APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s +' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum diff --git a/manager/gradlew.bat b/manager/gradlew.bat index 25da30dbdeee..9d21a21834d5 100644 --- a/manager/gradlew.bat +++ b/manager/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## diff --git a/userspace/ksud/Cargo.lock b/userspace/ksud/Cargo.lock index 4e0b195fb99d..d8ad1fdfb2b0 100644 --- a/userspace/ksud/Cargo.lock +++ b/userspace/ksud/Cargo.lock @@ -4,18 +4,18 @@ version = 3 [[package]] name = "addr2line" -version = "0.21.0" +version = "0.24.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" +checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" dependencies = [ "gimli", ] [[package]] -name = "adler" -version = "1.0.2" +name = "adler2" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" +checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" [[package]] name = "adler32" @@ -24,24 +24,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234" [[package]] -name = "aes" -version = "0.8.4" +name = "ahash" +version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" dependencies = [ "cfg-if 1.0.0", - "cipher", - "cpufeatures", + "once_cell", + "version_check", + "zerocopy", ] [[package]] -name = "aho-corasick" -version = "1.1.3" +name = "allocator-api2" +version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" -dependencies = [ - "memchr", -] +checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" [[package]] name = "android-properties" @@ -63,14 +61,13 @@ checksum = "5ecc8056bf6ab9892dcd53216c83d1597487d7dacac16c8df6b877d127df9937" [[package]] name = "android_logger" -version = "0.13.3" +version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c494134f746c14dc653a35a4ea5aca24ac368529da5370ecf41fe0341c35772f" +checksum = "05b07e8e73d720a1f2e4b6014766e6039fd2e96a4fa44e2a78d0e1fa2ff49826" dependencies = [ "android_log-sys", - "env_logger 0.10.2", + "env_filter", "log", - "once_cell", ] [[package]] @@ -84,9 +81,9 @@ dependencies = [ [[package]] name = "anstream" -version = "0.6.14" +version = "0.6.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "418c75fa768af9c03be99d17643f93f79bbba589895012a80e3452a19ddda15b" +checksum = "23a1e53f0f5d86382dafe1cf314783b2044280f406e7e1506368220ad11b1338" dependencies = [ "anstyle", "anstyle-parse", @@ -99,43 +96,43 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.7" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "038dfcf04a5feb68e9c60b21c9625a54c2c0616e79b72b0fd87075a056ae1d1b" +checksum = "8365de52b16c035ff4fcafe0092ba9390540e3e352870ac09933bebcaa2c8c56" [[package]] name = "anstyle-parse" -version = "0.2.4" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c03a11a9034d92058ceb6ee011ce58af4a9bf61491aa7e1e59ecd24bd40d22d4" +checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" -version = "1.0.3" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a64c907d4e79225ac72e2a354c9ce84d50ebb4586dee56c82b3ee73004f537f5" +checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" dependencies = [ - "windows-sys", + "windows-sys 0.59.0", ] [[package]] name = "anstyle-wincon" -version = "3.0.3" +version = "3.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61a38449feb7068f52bb06c12759005cf459ee52bb4adc1d5a7c4322d716fb19" +checksum = "2109dbce0e72be3ec00bed26e6a7479ca384ad226efdd66db8fa2e3a38c83125" dependencies = [ "anstyle", - "windows-sys", + "windows-sys 0.59.0", ] [[package]] name = "anyhow" -version = "1.0.86" +version = "1.0.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" +checksum = "c042108f3ed77fd83760a5fd79b53be043192bb3b9dba91d8c574c0ada7850c8" [[package]] name = "arbitrary" @@ -148,42 +145,36 @@ dependencies = [ [[package]] name = "async-trait" -version = "0.1.80" +version = "0.1.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6fa2087f2753a7da8cc1c0dbfcf89579dd57458e36769de5ac750b4671737ca" +checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd" dependencies = [ "proc-macro2", "quote", - "syn 2.0.65", + "syn", ] [[package]] name = "autocfg" -version = "1.3.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" [[package]] name = "backtrace" -version = "0.3.71" +version = "0.3.74" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26b05800d2e817c8b3b4b54abd461726265fa9789ae34330622f2db9ee696f9d" +checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" dependencies = [ "addr2line", - "cc", "cfg-if 1.0.0", "libc", "miniz_oxide", "object", "rustc-demangle", + "windows-targets", ] -[[package]] -name = "base64ct" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" - [[package]] name = "bitflags" version = "1.3.2" @@ -192,9 +183,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" [[package]] name = "block-buffer" @@ -219,40 +210,17 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.6.0" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9" - -[[package]] -name = "bzip2" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdb116a6ef3f6c3698828873ad02c3014b3c85cadb88496095628e3ef1e347f8" -dependencies = [ - "bzip2-sys", - "libc", -] - -[[package]] -name = "bzip2-sys" -version = "0.1.11+1.0.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "736a955f3fa7875102d57c82b8cac37ec45224a07fd32d58f9f7a186b6cd4cdc" -dependencies = [ - "cc", - "libc", - "pkg-config", -] +checksum = "9ac0150caa2ae65ca5bd83f25c7de183dea78d4d366469f148435e2acfbad0da" [[package]] name = "cc" -version = "1.0.98" +version = "1.1.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41c270e7540d725e65ac7f1b212ac8ce349719624d7bcff99f8e2e488e8cf03f" +checksum = "c2e7962b54006dcfcc61cb72735f4d89bb97061dd6a7ed882ec6b8ee53714c6f" dependencies = [ - "jobserver", - "libc", - "once_cell", + "shlex", ] [[package]] @@ -281,21 +249,11 @@ dependencies = [ "windows-targets", ] -[[package]] -name = "cipher" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" -dependencies = [ - "crypto-common", - "inout", -] - [[package]] name = "clap" -version = "4.5.4" +version = "4.5.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90bc066a67923782aa8515dbaea16946c5bcc5addbd668bb80af688e53e548a0" +checksum = "b97f376d85a664d5837dbae44bf546e6477a679ff6610010f17276f686d867e8" dependencies = [ "clap_builder", "clap_derive", @@ -303,9 +261,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.2" +version = "4.5.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae129e2e766ae0ec03484e609954119f123cc1fe650337e155d03b022f24f7b4" +checksum = "19bc80abd44e4bed93ca373a0704ccbd1b710dc5749406201bb018272808dc54" dependencies = [ "anstream", "anstyle", @@ -315,42 +273,42 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.4" +version = "4.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "528131438037fd55894f62d6e9f068b8f45ac57ffa77517819645d10aed04f64" +checksum = "4ac6a0c7b1a9e9a5186361f67dfa1b88213572f427fb9ab038efb2bd8c582dab" dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.65", + "syn", ] [[package]] name = "clap_lex" -version = "0.7.0" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce" +checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97" [[package]] name = "colorchoice" -version = "1.0.1" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b6a852b24ab71dffc585bcb46eaf7959d175cb865a7152e35b348d1b2960422" +checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" [[package]] name = "const_format" -version = "0.2.32" +version = "0.2.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3a214c7af3d04997541b18d432afaff4c455e79e2029079647e72fc2bd27673" +checksum = "50c655d81ff1114fb0dcdea9225ea9f0cc712a6f8d189378e82bdf62a473a64b" dependencies = [ "const_format_proc_macros", ] [[package]] name = "const_format_proc_macros" -version = "0.2.32" +version = "0.2.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7f6ff08fd20f4f299298a28e2dfa8a8ba1036e6cd2460ac1de7b425d76f2500" +checksum = "eff1a44b93f47b1bac19a27932f5c591e43d1ba357ee4f61526c8a25603f0eb1" dependencies = [ "proc-macro2", "quote", @@ -358,26 +316,44 @@ dependencies = [ ] [[package]] -name = "constant_time_eq" -version = "0.1.5" +name = "core-foundation-sys" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" [[package]] -name = "core-foundation-sys" -version = "0.8.6" +name = "core2" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" +checksum = "b49ba7ef1ad6107f8824dbe97de947cbaac53c44e7f9756a1fba0d37c1eec505" +dependencies = [ + "memchr", +] [[package]] name = "cpufeatures" -version = "0.2.12" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" +checksum = "608697df725056feaccfa42cffdaeeec3fccc4ffc38358ecd19b243e716a78e0" dependencies = [ "libc", ] +[[package]] +name = "crc" +version = "3.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69e6e4d7b33a94f0991c26729976b10ebde1d34c3ee82408fb536164fa10d636" +dependencies = [ + "crc-catalog", +] + +[[package]] +name = "crc-catalog" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5" + [[package]] name = "crc32fast" version = "1.4.2" @@ -453,11 +429,17 @@ dependencies = [ "typenum", ] +[[package]] +name = "dary_heap" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04d2cd9c18b9f454ed67da600630b021a8a80bf33f8c95896ab33aaf1c26b728" + [[package]] name = "deflate64" -version = "0.1.8" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83ace6c86376be0b6cdcf3fb41882e81d94b31587573d1cfa9d01cd06bba210d" +checksum = "da692b8d1080ea3045efaab14434d40468c3d8657e42abddfffca87b428f4c1b" [[package]] name = "deranged" @@ -470,13 +452,13 @@ dependencies = [ [[package]] name = "derive-new" -version = "0.6.0" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d150dea618e920167e5973d70ae6ece4385b7164e0d799fe7c122dd0a5d912ad" +checksum = "2cdc8d50f426189eef89dac62fabfa0abb27d5cc008f25bf4156a0203325becc" dependencies = [ "proc-macro2", "quote", - "syn 2.0.65", + "syn", ] [[package]] @@ -487,7 +469,7 @@ checksum = "67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611" dependencies = [ "proc-macro2", "quote", - "syn 2.0.65", + "syn", ] [[package]] @@ -498,59 +480,48 @@ checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ "block-buffer", "crypto-common", - "subtle", ] [[package]] name = "displaydoc" -version = "0.2.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.65", + "syn", ] [[package]] name = "either" -version = "1.12.0" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3dca9240753cf90908d7e4aac30f630662b02aebaa1b58a3cadabdb23385b58b" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" [[package]] name = "encoding_rs" -version = "0.8.34" +version = "0.8.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b45de904aa0b010bce2ab45264d0631681847fa7b6f2eaa7dab7619943bc4f59" +checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" dependencies = [ "cfg-if 1.0.0", ] [[package]] name = "env_filter" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a009aa4810eb158359dda09d0c87378e4bbb89b5a801f016885a4707ba24f7ea" -dependencies = [ - "log", -] - -[[package]] -name = "env_logger" -version = "0.10.2" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cd405aab171cb85d6735e5c8d9db038c17d3ca007a4d2c25f337935c3d90580" +checksum = "4f2c92ceda6ceec50f43169f9ee8424fe2db276791afde7b2cd8bc084cb376ab" dependencies = [ "log", - "regex", ] [[package]] name = "env_logger" -version = "0.11.3" +version = "0.11.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38b35839ba51819680ba087cd351788c9a3c476841207e0b8cee0b04722343b9" +checksum = "e13fa619b91fb2381732789fc5de83b45675e882f66623b7d8cb4f643017018d" dependencies = [ "env_filter", "log", @@ -580,7 +551,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" dependencies = [ "libc", - "windows-sys", + "windows-sys 0.52.0", ] [[package]] @@ -604,22 +575,22 @@ dependencies = [ "libc", ] +[[package]] +name = "fastrand" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6" + [[package]] name = "flate2" -version = "1.0.30" +version = "1.0.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f54427cfd1c7829e2a139fcefea601bf088ebca651d2bf53ebc600eac295dae" +checksum = "a1b589b4dc103969ad3cf85c950899926ec64300a1a46d76c03a6072957036f0" dependencies = [ "crc32fast", "miniz_oxide", ] -[[package]] -name = "fuchsia-cprng" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" - [[package]] name = "generic-array" version = "0.14.7" @@ -652,15 +623,25 @@ dependencies = [ [[package]] name = "gimli" -version = "0.28.1" +version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" +checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" [[package]] name = "hashbrown" version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +dependencies = [ + "ahash", + "allocator-api2", +] + +[[package]] +name = "hashbrown" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e087f84d4f86bf4b218b927129862374b72199ae7d8657835f1e89000eea4fb" [[package]] name = "heck" @@ -674,15 +655,6 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" -[[package]] -name = "hmac" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" -dependencies = [ - "digest", -] - [[package]] name = "hole-punch" version = "0.0.4-alpha.0" @@ -702,7 +674,7 @@ version = "0.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" dependencies = [ - "windows-sys", + "windows-sys 0.52.0", ] [[package]] @@ -716,9 +688,9 @@ dependencies = [ [[package]] name = "iana-time-zone" -version = "0.1.60" +version = "0.1.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" +checksum = "235e081f3925a06703c2d0117ea8b91f042756fd6e7a6e5d901e8ca1a996b220" dependencies = [ "android_system_properties", "core-foundation-sys", @@ -739,71 +711,51 @@ dependencies = [ [[package]] name = "include-flate" -version = "0.2.0" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2e11569346406931d20276cc460215ee2826e7cad43aa986999cb244dd7adb0" +checksum = "df49c16750695486c1f34de05da5b7438096156466e7f76c38fcdf285cf0113e" dependencies = [ - "include-flate-codegen-exports", + "include-flate-codegen", "lazy_static", "libflate", ] [[package]] name = "include-flate-codegen" -version = "0.1.4" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a7d6e1419fa3129eb0802b4c99603c0d425c79fb5d76191d5a20d0ab0d664e8" +checksum = "8c5b246c6261be723b85c61ecf87804e8ea4a35cb68be0ff282ed84b95ffe7d7" dependencies = [ "libflate", - "proc-macro-hack", "proc-macro2", "quote", - "syn 1.0.109", -] - -[[package]] -name = "include-flate-codegen-exports" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75657043ffe3d8280f1cb8aef0f505532b392ed7758e0baeac22edadcee31a03" -dependencies = [ - "include-flate-codegen", - "proc-macro-hack", + "syn", ] [[package]] name = "indexmap" -version = "2.2.6" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" +checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da" dependencies = [ "equivalent", - "hashbrown", -] - -[[package]] -name = "inout" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" -dependencies = [ - "generic-array", + "hashbrown 0.15.0", ] [[package]] name = "is_executable" -version = "1.0.1" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa9acdc6d67b75e626ad644734e8bc6df893d9cd2a834129065d3dd6158ea9c8" +checksum = "d4a1b5bad6f9072935961dfbf1cced2f3d129963d091b6f69f007fe04e758ae2" dependencies = [ "winapi", ] [[package]] name = "is_terminal_polyfill" -version = "1.70.0" +version = "1.70.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8478577c03552c21db0e2724ffb8986a5ce7af88107e6be5d2ee6e158c12800" +checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" [[package]] name = "itoa" @@ -814,28 +766,18 @@ checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" [[package]] name = "java-properties" version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37bf6f484471c451f2b51eabd9e66b3fa7274550c5ec4b6c3d6070840945117f" +source = "git+https://github.com/Kernel-SU/java-properties.git?branch=master#42a4aa941b70ded2dd3be9e9f892471023e70229" dependencies = [ "encoding_rs", "lazy_static", - "regex", -] - -[[package]] -name = "jobserver" -version = "0.1.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2b099aaa34a9751c5bf0878add70444e1ed2dd73f347be99003d4577277de6e" -dependencies = [ - "libc", + "regex-lite", ] [[package]] name = "js-sys" -version = "0.3.69" +version = "0.3.70" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" +checksum = "1868808506b929d7b0cfa8f75951347aa71bb21144b7791bae35d9bccfcfe37a" dependencies = [ "wasm-bindgen", ] @@ -862,7 +804,7 @@ dependencies = [ "const_format", "derive-new", "encoding_rs", - "env_logger 0.11.3", + "env_logger", "extattr", "getopts", "hole-punch", @@ -875,57 +817,61 @@ dependencies = [ "loopdev", "nom", "procfs", - "regex", + "regex-lite", "retry", "rust-embed", - "rustix 0.38.30", + "rustix 0.38.34", "serde", "serde_json", "sha1", "sha256", - "tempdir", + "tempfile", "which", - "zip 2.1.0", + "zip", "zip-extensions", ] [[package]] name = "lazy_static" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" -version = "0.2.155" +version = "0.2.161" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" +checksum = "8e9489c2807c139ffd9c1794f4af0ebe86a828db53ecdc7fea2111d0fed085d1" [[package]] name = "libflate" -version = "1.4.0" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ff4ae71b685bbad2f2f391fe74f6b7659a34871c08b210fdc039e43bee07d18" +checksum = "45d9dfdc14ea4ef0900c1cddbc8dcd553fbaacd8a4a282cf4018ae9dd04fb21e" dependencies = [ "adler32", + "core2", "crc32fast", + "dary_heap", "libflate_lz77", ] [[package]] name = "libflate_lz77" -version = "1.2.0" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a52d3a8bfc85f250440e4424db7d857e241a3aebbbe301f3eb606ab15c39acbf" +checksum = "e6e0d73b369f386f1c44abd9c570d5318f55ccde816ff4b562fa452e5182863d" dependencies = [ + "core2", + "hashbrown 0.14.5", "rle-decode-fast", ] [[package]] name = "libm" -version = "0.2.8" +version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" +checksum = "a00419de735aac21d53b0de5ce2c03bd3627277cf471300f27ebc89f7d828047" [[package]] name = "linux-raw-sys" @@ -941,9 +887,9 @@ checksum = "9374ef4228402d4b7e403e5838cb880d9ee663314b0a900d5a6aabf0c213552e" [[package]] name = "log" -version = "0.4.21" +version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" [[package]] name = "loopdev" @@ -954,11 +900,21 @@ dependencies = [ "libc", ] +[[package]] +name = "lzma-rs" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "297e814c836ae64db86b36cf2a557ba54368d03f6afcd7d947c266692f71115e" +dependencies = [ + "byteorder", + "crc", +] + [[package]] name = "memchr" -version = "2.7.2" +version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "memmap" @@ -978,11 +934,11 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" -version = "0.7.3" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87dfd01fe195c66b572b37921ad8803d010623c0aca821bea2302239d155cdae" +checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1" dependencies = [ - "adler", + "adler2", ] [[package]] @@ -1012,53 +968,24 @@ dependencies = [ [[package]] name = "object" -version = "0.32.2" +version = "0.36.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" +checksum = "aedf0a2d09c573ed1d8d85b30c119153926a2b36dce0ab28322c09a117a4683e" dependencies = [ "memchr", ] [[package]] name = "once_cell" -version = "1.19.0" +version = "1.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" - -[[package]] -name = "password-hash" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7676374caaee8a325c9e7a2ae557f216c5563a171d6997b0ef8a65af35147700" -dependencies = [ - "base64ct", - "rand_core 0.6.4", - "subtle", -] - -[[package]] -name = "pbkdf2" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83a0692ec44e4cf1ef28ca317f14f8f07da2d95ec3fa01f86e4467b725e60917" -dependencies = [ - "digest", - "hmac", - "password-hash", - "sha2", -] +checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" [[package]] name = "pin-project-lite" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" - -[[package]] -name = "pkg-config" -version = "0.3.30" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" +checksum = "915a1e146535de9163f3987b8944ed8cf49a18bb0056bcebcdcece385cece4ff" [[package]] name = "powerfmt" @@ -1068,73 +995,56 @@ checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" [[package]] name = "ppv-lite86" -version = "0.2.17" +version = "0.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" - -[[package]] -name = "proc-macro-hack" -version = "0.5.20+deprecated" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068" +checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" +dependencies = [ + "zerocopy", +] [[package]] name = "proc-macro2" -version = "1.0.83" +version = "1.0.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b33eb56c327dec362a9e55b3ad14f9d2f0904fb5a5b03b513ab5465399e9f43" +checksum = "f139b0662de085916d1fb67d2b4169d1addddda1919e696f3252b740b629986e" dependencies = [ "unicode-ident", ] [[package]] name = "procfs" -version = "0.16.0" +version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "731e0d9356b0c25f16f33b5be79b1c57b562f141ebfcdb0ad8ac2c13a24293b4" +checksum = "cc5b72d8145275d844d4b5f6d4e1eef00c8cd889edb6035c21675d1bb1f45c9f" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", "chrono", "flate2", "hex", - "lazy_static", "procfs-core", - "rustix 0.38.34", + "rustix 0.38.38", ] [[package]] name = "procfs-core" -version = "0.16.0" +version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d3554923a69f4ce04c4a754260c338f505ce22642d3830e049a399fc2059a29" +checksum = "239df02d8349b06fc07398a3a1697b06418223b1c7725085e801e7c0fc6a12ec" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", "chrono", "hex", ] [[package]] name = "quote" -version = "1.0.36" +version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" dependencies = [ "proc-macro2", ] -[[package]] -name = "rand" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "552840b97013b1a26992c11eac34bdd778e464601a4c2054b5f0bff7c6761293" -dependencies = [ - "fuchsia-cprng", - "libc", - "rand_core 0.3.1", - "rdrand", - "winapi", -] - [[package]] name = "rand" version = "0.8.5" @@ -1143,7 +1053,7 @@ checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", "rand_chacha", - "rand_core 0.6.4", + "rand_core", ] [[package]] @@ -1153,24 +1063,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ "ppv-lite86", - "rand_core 0.6.4", + "rand_core", ] -[[package]] -name = "rand_core" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" -dependencies = [ - "rand_core 0.4.2", -] - -[[package]] -name = "rand_core" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc" - [[package]] name = "rand_core" version = "0.6.4" @@ -1201,51 +1096,10 @@ dependencies = [ ] [[package]] -name = "rdrand" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" -dependencies = [ - "rand_core 0.3.1", -] - -[[package]] -name = "regex" -version = "1.10.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c" -dependencies = [ - "aho-corasick", - "memchr", - "regex-automata", - "regex-syntax", -] - -[[package]] -name = "regex-automata" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" -dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax", -] - -[[package]] -name = "regex-syntax" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" - -[[package]] -name = "remove_dir_all" -version = "0.5.3" +name = "regex-lite" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" -dependencies = [ - "winapi", -] +checksum = "53a49587ad06b26609c52e423de037e7f57f20d53535d66e08c695f347df952a" [[package]] name = "retry" @@ -1253,7 +1107,7 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9166d72162de3575f950507683fac47e30f6f2c3836b71b7fbc61aa517c9c5f4" dependencies = [ - "rand 0.8.5", + "rand", ] [[package]] @@ -1264,9 +1118,9 @@ checksum = "3582f63211428f83597b51b2ddb88e2a91a9d52d12831f9d08f5e624e8977422" [[package]] name = "rust-embed" -version = "8.4.0" +version = "8.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19549741604902eb99a7ed0ee177a0663ee1eda51a29f71401f166e47e77806a" +checksum = "fa66af4a4fdd5e7ebc276f115e895611a34739a9c1c01028383d612d550953c0" dependencies = [ "include-flate", "rust-embed-impl", @@ -1276,22 +1130,22 @@ dependencies = [ [[package]] name = "rust-embed-impl" -version = "8.4.0" +version = "8.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb9f96e283ec64401f30d3df8ee2aaeb2561f34c824381efa24a35f79bf40ee4" +checksum = "6125dbc8867951125eec87294137f4e9c2c96566e61bf72c45095a7c77761478" dependencies = [ "proc-macro2", "quote", "rust-embed-utils", - "syn 2.0.65", + "syn", "walkdir", ] [[package]] name = "rust-embed-utils" -version = "8.4.0" +version = "8.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38c74a686185620830701348de757fd36bef4aa9680fd23c49fc539ddcc1af32" +checksum = "2e5347777e9aacb56039b0e1f28785929a8a3b709e87482e7442c72e7c12529d" dependencies = [ "sha2", "walkdir", @@ -1305,29 +1159,29 @@ checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" [[package]] name = "rustix" -version = "0.38.30" -source = "git+https://github.com/Kernel-SU/rustix.git?branch=main#0e270bce2d97466be6b987bb5f7ea5b1e8d84969" +version = "0.38.34" +source = "git+https://github.com/Kernel-SU/rustix.git?branch=main#4a53fbc7cb7a07cabe87125cc21dbc27db316259" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", "errno 0.3.9", "itoa", "libc", "linux-raw-sys", "once_cell", - "windows-sys", + "windows-sys 0.52.0", ] [[package]] name = "rustix" -version = "0.38.34" +version = "0.38.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" +checksum = "aa260229e6538e52293eeb577aabd09945a09d6d9cc0fc550ed7529056c2e32a" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", "errno 0.3.9", "libc", "linux-raw-sys", - "windows-sys", + "windows-sys 0.52.0", ] [[package]] @@ -1347,31 +1201,32 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.202" +version = "1.0.213" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "226b61a0d411b2ba5ff6d7f73a476ac4f8bb900373459cd00fab8512828ba395" +checksum = "3ea7893ff5e2466df8d720bb615088341b295f849602c6956047f8f80f0e9bc1" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.202" +version = "1.0.213" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6048858004bcff69094cd972ed40a32500f153bd3be9f716b2eed2e8217c4838" +checksum = "7e85ad2009c50b58e87caa8cd6dac16bdf511bbfb7af6c33df902396aa480fa5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.65", + "syn", ] [[package]] name = "serde_json" -version = "1.0.117" +version = "1.0.132" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3" +checksum = "d726bfaff4b320266d395898905d0eba0345aae23b54aee3a737e260fd46db03" dependencies = [ "itoa", + "memchr", "ryu", "serde", ] @@ -1411,6 +1266,12 @@ dependencies = [ "tokio", ] +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + [[package]] name = "simd-adler32" version = "0.3.7" @@ -1423,17 +1284,11 @@ version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" -[[package]] -name = "subtle" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" - [[package]] name = "syn" -version = "1.0.109" +version = "2.0.85" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +checksum = "5023162dfcd14ef8f32034d8bcd4cc5ddc61ef7a247c024a33e24e1f24d21b56" dependencies = [ "proc-macro2", "quote", @@ -1441,44 +1296,36 @@ dependencies = [ ] [[package]] -name = "syn" -version = "2.0.65" +name = "tempfile" +version = "3.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2863d96a84c6439701d7a38f9de935ec562c8832cc55d1dde0f513b52fad106" +checksum = "f0f2c9fc62d0beef6951ccffd757e241266a2c833136efbe35af6cd2567dca5b" dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "tempdir" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15f2b5fb00ccdf689e0149d1b1b3c03fead81c2b37735d812fa8bddbbf41b6d8" -dependencies = [ - "rand 0.4.6", - "remove_dir_all", + "cfg-if 1.0.0", + "fastrand", + "once_cell", + "rustix 0.38.38", + "windows-sys 0.59.0", ] [[package]] name = "thiserror" -version = "1.0.61" +version = "1.0.65" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709" +checksum = "5d11abd9594d9b38965ef50805c5e469ca9cc6f197f883f717e0269a3057b3d5" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.61" +version = "1.0.65" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533" +checksum = "ae71770322cbd277e69d762a16c444af02aa0575ac0d174f0b9562d3b37f8602" dependencies = [ "proc-macro2", "quote", - "syn 2.0.65", + "syn", ] [[package]] @@ -1502,9 +1349,9 @@ checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" [[package]] name = "tokio" -version = "1.37.0" +version = "1.41.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1adbebffeca75fcfd058afa480fb6c0b81e165a0323f9c9d39c9697e37c46787" +checksum = "145f3413504347a2be84393cc8a7d2fb4d863b375909ea59f2158261aa258bbb" dependencies = [ "backtrace", "bytes", @@ -1519,33 +1366,33 @@ checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" [[package]] name = "unicode-ident" -version = "1.0.12" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" [[package]] name = "unicode-width" -version = "0.1.12" +version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68f5e5f3158ecfd4b8ff6fe086db7c8467a2dfdac97fe420f2b7c4aa97af66d6" +checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" [[package]] name = "unicode-xid" -version = "0.2.4" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" [[package]] name = "utf8parse" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "version_check" -version = "0.9.4" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" [[package]] name = "walkdir" @@ -1565,34 +1412,35 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.92" +version = "0.2.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" +checksum = "128d1e363af62632b8eb57219c8fd7877144af57558fb2ef0368d0087bddeb2e" dependencies = [ "cfg-if 1.0.0", + "once_cell", "wasm-bindgen-macro", ] [[package]] name = "wasm-bindgen-backend" -version = "0.2.92" +version = "0.2.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" +checksum = "cb6dd4d3ca0ddffd1dd1c9c04f94b868c37ff5fac97c30b97cff2d74fce3a358" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", - "syn 2.0.65", + "syn", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-macro" -version = "0.2.92" +version = "0.2.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" +checksum = "e79384be7f8f5a9dd5d7167216f022090cf1f9ec128e6e6a482a2cb5c5422c56" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -1600,32 +1448,32 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.92" +version = "0.2.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" +checksum = "26c6ab57572f7a24a4985830b120de1594465e5d500f24afe89e16b4e833ef68" dependencies = [ "proc-macro2", "quote", - "syn 2.0.65", + "syn", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.92" +version = "0.2.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" +checksum = "65fc09f10666a9f147042251e0dda9c18f166ff7de300607007e96bdebc1068d" [[package]] name = "which" -version = "6.0.1" +version = "6.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8211e4f58a2b2805adfbefbc07bab82958fc91e3836339b1ab7ae32465dce0d7" +checksum = "b4ee928febd44d98f2f459a4a79bd4d928591333a494a10a868418ac1b39cf1f" dependencies = [ "either", "home", - "rustix 0.38.34", + "rustix 0.38.38", "winsafe", ] @@ -1647,11 +1495,11 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" -version = "0.1.8" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d4cc384e1e73b93bafa6fb4f1df8c41695c8a91cf9c4c64358067d15a7b6c6b" +checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" dependencies = [ - "windows-sys", + "windows-sys 0.59.0", ] [[package]] @@ -1678,11 +1526,20 @@ dependencies = [ "windows-targets", ] +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets", +] + [[package]] name = "windows-targets" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ "windows_aarch64_gnullvm", "windows_aarch64_msvc", @@ -1696,51 +1553,51 @@ dependencies = [ [[package]] name = "windows_aarch64_gnullvm" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" [[package]] name = "windows_aarch64_msvc" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" [[package]] name = "windows_i686_gnu" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" [[package]] name = "windows_i686_gnullvm" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" [[package]] name = "windows_i686_msvc" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" [[package]] name = "windows_x86_64_gnu" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" [[package]] name = "windows_x86_64_gnullvm" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" [[package]] name = "windows_x86_64_msvc" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winsafe" @@ -1749,53 +1606,53 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d135d17ab770252ad95e9a872d365cf3090e3be864a34ab46f48555993efc904" [[package]] -name = "zip" -version = "0.6.6" +name = "zerocopy" +version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "760394e246e4c28189f19d488c058bf16f564016aefac5d32bb1f3b51d5e9261" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" dependencies = [ - "aes", "byteorder", - "bzip2", - "constant_time_eq", - "crc32fast", - "crossbeam-utils", - "flate2", - "hmac", - "pbkdf2", - "sha1", - "time", - "zstd 0.11.2+zstd.1.5.2", + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" +dependencies = [ + "proc-macro2", + "quote", + "syn", ] [[package]] name = "zip" -version = "2.1.0" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2568cd0f20e86cd9a7349fe05178f7bd22f22724678448ae5a9bac266df2689" +checksum = "dc5e4288ea4057ae23afc69a4472434a87a2495cafce6632fd1c4ec9f5cf3494" dependencies = [ "arbitrary", - "bzip2", "crc32fast", "crossbeam-utils", "deflate64", "displaydoc", "flate2", "indexmap", + "lzma-rs", "memchr", "thiserror", "time", "zopfli", - "zstd 0.13.1", ] [[package]] name = "zip-extensions" -version = "0.6.2" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cecf62554c4ff96bce01a7ef123d160c3ffe9180638820f8b4d545c65b221b8c" +checksum = "386508a00aae1d8218b9252a41f59bba739ccee3f8e420bb90bcb1c30d960d4a" dependencies = [ - "zip 0.6.6", + "zip", ] [[package]] @@ -1811,50 +1668,3 @@ dependencies = [ "once_cell", "simd-adler32", ] - -[[package]] -name = "zstd" -version = "0.11.2+zstd.1.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20cc960326ece64f010d2d2107537f26dc589a6573a316bd5b1dba685fa5fde4" -dependencies = [ - "zstd-safe 5.0.2+zstd.1.5.2", -] - -[[package]] -name = "zstd" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d789b1514203a1120ad2429eae43a7bd32b90976a7bb8a05f7ec02fa88cc23a" -dependencies = [ - "zstd-safe 7.1.0", -] - -[[package]] -name = "zstd-safe" -version = "5.0.2+zstd.1.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d2a5585e04f9eea4b2a3d1eca508c4dee9592a89ef6f450c11719da0726f4db" -dependencies = [ - "libc", - "zstd-sys", -] - -[[package]] -name = "zstd-safe" -version = "7.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cd99b45c6bc03a018c8b8a86025678c87e55526064e38f9df301989dce7ec0a" -dependencies = [ - "zstd-sys", -] - -[[package]] -name = "zstd-sys" -version = "2.0.10+zstd.1.5.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c253a4914af5bafc8fa8c86ee400827e83cf6ec01195ec1f1ed8441bf00d65aa" -dependencies = [ - "cc", - "pkg-config", -] diff --git a/userspace/ksud/Cargo.toml b/userspace/ksud/Cargo.toml index d031660307dc..b0b00a7e1e46 100644 --- a/userspace/ksud/Cargo.toml +++ b/userspace/ksud/Cargo.toml @@ -2,48 +2,47 @@ name = "ksud" version = "0.1.0" edition = "2021" -rust-version = "1.77.2" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -anyhow = "1" -clap = { version = "4", features = ["derive"] } +anyhow = "1.0" +clap = { version = "4.5", features = ["derive"] } const_format = "0.2" -zip = { version = "2", features = [ +zip = { version = "2.2", default-features = false } +zip-extensions = { version = "0.8", features = [ "deflate", "deflate64", - "bzip2", "time", - "zstd", + "lzma", + "xz", ], default-features = false } -zip-extensions = "0.6" -java-properties = "2" +java-properties = { git = "https://github.com/Kernel-SU/java-properties.git", branch = "master", default-features = false } log = "0.4" env_logger = { version = "0.11", default-features = false } -serde = { version = "1" } -serde_json = "1" -regex = "1" +serde = { version = "1.0" } +serde_json = "1.0" encoding_rs = "0.8" -retry = "2" -humansize = "2" +retry = "2.0" +humansize = "2.1" libc = "0.2" -extattr = "1" +extattr = "1.0" jwalk = "0.8" -is_executable = "1" -nom = "7" -derive-new = "0.6" -rust-embed = { version = "8", features = [ +is_executable = "1.0" +nom = "7.1" +derive-new = "0.7" +rust-embed = { version = "8.5", features = [ "debug-embed", "compression", # must clean build after updating binaries ] } -which = "6" +which = "6.0" getopts = "0.2" sha256 = "1" sha1 = "0.10" -tempdir = "0.3" +tempfile = "3.13" chrono = "0.4" hole-punch = { git = "https://github.com/tiann/hole-punch" } +regex-lite = "0.1" [target.'cfg(any(target_os = "android", target_os = "linux"))'.dependencies] rustix = { git = "https://github.com/Kernel-SU/rustix.git", branch = "main", features = [ @@ -51,13 +50,14 @@ rustix = { git = "https://github.com/Kernel-SU/rustix.git", branch = "main", fea ] } # some android specific dependencies which compiles under unix are also listed here for convenience of coding android-properties = { version = "0.2", features = ["bionic-deprecated"] } -procfs = "0.16" +procfs = "0.17" loopdev = { git = "https://github.com/Kernel-SU/loopdev" } [target.'cfg(target_os = "android")'.dependencies] -android_logger = "0.13" +android_logger = { version = "0.14", default-features = false } [profile.release] strip = true opt-level = "z" -lto = true \ No newline at end of file +lto = true +codegen-units = 1 diff --git a/userspace/ksud/bin/aarch64/resetprop b/userspace/ksud/bin/aarch64/resetprop index 1155e5f49a3e..305032c90dc9 100644 Binary files a/userspace/ksud/bin/aarch64/resetprop and b/userspace/ksud/bin/aarch64/resetprop differ diff --git a/userspace/ksud/bin/x86_64/resetprop b/userspace/ksud/bin/x86_64/resetprop index dc4d9910e9ed..80030612649b 100644 Binary files a/userspace/ksud/bin/x86_64/resetprop and b/userspace/ksud/bin/x86_64/resetprop differ diff --git a/userspace/ksud/src/boot_patch.rs b/userspace/ksud/src/boot_patch.rs index c2661c68fc76..8ce6ee73a186 100644 --- a/userspace/ksud/src/boot_patch.rs +++ b/userspace/ksud/src/boot_patch.rs @@ -10,6 +10,7 @@ use anyhow::bail; use anyhow::ensure; use anyhow::Context; use anyhow::Result; +use regex_lite::Regex; use which::which; use crate::defs; @@ -27,7 +28,6 @@ fn ensure_gki_kernel() -> Result<()> { #[cfg(target_os = "android")] pub fn get_kernel_version() -> Result<(i32, i32, i32)> { - use regex::Regex; let uname = rustix::system::uname(); let version = uname.release().to_string_lossy(); let re = Regex::new(r"(\d+)\.(\d+)\.(\d+)")?; @@ -52,7 +52,6 @@ pub fn get_kernel_version() -> Result<(i32, i32, i32)> { #[cfg(target_os = "android")] fn parse_kmi(version: &str) -> Result { - use regex::Regex; let re = Regex::new(r"(.* )?(\d+\.\d+)(\S+)?(android\d+)(.*)")?; let cap = re .captures(version) @@ -97,6 +96,63 @@ pub fn get_current_kmi() -> Result { bail!("Unsupported platform") } +fn parse_kmi_from_kernel(kernel: &PathBuf, workdir: &Path) -> Result { + use std::fs::{copy, File}; + use std::io::{BufReader, Read}; + let kernel_path = workdir.join("kernel"); + copy(kernel, &kernel_path).context("Failed to copy kernel")?; + + let file = File::open(&kernel_path).context("Failed to open kernel file")?; + let mut reader = BufReader::new(file); + let mut buffer = Vec::new(); + reader + .read_to_end(&mut buffer) + .context("Failed to read kernel file")?; + + let printable_strings: Vec<&str> = buffer + .split(|&b| b == 0) + .filter_map(|slice| std::str::from_utf8(slice).ok()) + .filter(|s| s.chars().all(|c| c.is_ascii_graphic() || c == ' ')) + .collect(); + + let re = + Regex::new(r"(?:.* )?(\d+\.\d+)(?:\S+)?(android\d+)").context("Failed to compile regex")?; + for s in printable_strings { + if let Some(caps) = re.captures(s) { + if let (Some(kernel_version), Some(android_version)) = (caps.get(1), caps.get(2)) { + let kmi = format!("{}-{}", android_version.as_str(), kernel_version.as_str()); + return Ok(kmi); + } + } + } + println!("- Failed to get KMI version"); + bail!("Try to choose LKM manually") +} + +fn parse_kmi_from_boot(magiskboot: &Path, image: &PathBuf, workdir: &Path) -> Result { + let image_path = workdir.join("image"); + + std::fs::copy(image, &image_path).context("Failed to copy image")?; + + let status = Command::new(magiskboot) + .current_dir(workdir) + .stdout(Stdio::null()) + .stderr(Stdio::null()) + .arg("unpack") + .arg(&image_path) + .status() + .context("Failed to execute magiskboot command")?; + + if !status.success() { + bail!( + "magiskboot unpack failed with status: {:?}", + status.code().unwrap() + ); + } + + parse_kmi_from_kernel(&image_path, workdir) +} + fn do_cpio_cmd(magiskboot: &Path, workdir: &Path, cmd: &str) -> Result<()> { let status = Command::new(magiskboot) .current_dir(workdir) @@ -155,7 +211,10 @@ pub fn restore( magiskboot_path: Option, flash: bool, ) -> Result<()> { - let tmpdir = tempdir::TempDir::new("KernelSU").context("create temp dir failed")?; + let tmpdir = tempfile::Builder::new() + .prefix("KernelSU") + .tempdir() + .context("create temp dir failed")?; let workdir = tmpdir.path(); let magiskboot = find_magiskboot(magiskboot_path, workdir)?; @@ -310,13 +369,39 @@ fn do_patch( ); } - let tmpdir = tempdir::TempDir::new("KernelSU").context("create temp dir failed")?; + let tmpdir = tempfile::Builder::new() + .prefix("KernelSU") + .tempdir() + .context("create temp dir failed")?; let workdir = tmpdir.path(); + // extract magiskboot + let magiskboot = find_magiskboot(magiskboot_path, workdir)?; + let kmi = if let Some(kmi) = kmi { kmi } else { - get_current_kmi().context("Unknown KMI, please choose LKM manually")? + match get_current_kmi() { + Ok(value) => value, + Err(e) => { + println!("- {}", e); + if let Some(image_path) = &image { + println!( + "- Trying to auto detect KMI version for {}", + image_path.to_str().unwrap() + ); + parse_kmi_from_boot(&magiskboot, image_path, tmpdir.path())? + } else if let Some(kernel_path) = &kernel { + println!( + "- Trying to auto detect KMI version for {}", + kernel_path.to_str().unwrap() + ); + parse_kmi_from_kernel(kernel_path, tmpdir.path())? + } else { + "".to_string() + } + } + } }; let skip_init = kmi.starts_with("android12-"); @@ -329,9 +414,6 @@ fn do_patch( // try extract magiskboot/bootctl let _ = assets::ensure_binaries(false); - // extract magiskboot - let magiskboot = find_magiskboot(magiskboot_path, workdir)?; - if let Some(kernel) = kernel { std::fs::copy(kernel, workdir.join("kernel")).context("copy kernel from failed")?; } @@ -558,7 +640,7 @@ fn find_boot_image( } else { if cfg!(not(target_os = "android")) { println!("- Current OS is not android, refusing auto bootimage/bootdevice detection"); - bail!("please specify a boot image"); + bail!("Please specify a boot image"); } let mut slot_suffix = utils::getprop("ro.boot.slot_suffix").unwrap_or_else(|| String::from("")); diff --git a/userspace/ksud/src/cli.rs b/userspace/ksud/src/cli.rs index cd96a360f346..14717e62df78 100644 --- a/userspace/ksud/src/cli.rs +++ b/userspace/ksud/src/cli.rs @@ -223,6 +223,12 @@ enum Module { id: String, }, + /// run action for module + Action { + // module id + id: String, + }, + /// list all modules List, @@ -306,6 +312,7 @@ pub fn run() -> Result<()> { Module::Uninstall { id } => module::uninstall_module(&id), Module::Enable { id } => module::enable_module(&id), Module::Disable { id } => module::disable_module(&id), + Module::Action { id } => module::run_action(&id), Module::List => module::list_modules(), Module::Shrink => module::shrink_ksu_images(), } diff --git a/userspace/ksud/src/defs.rs b/userspace/ksud/src/defs.rs index c4b9fc3f989b..d5514501d140 100644 --- a/userspace/ksud/src/defs.rs +++ b/userspace/ksud/src/defs.rs @@ -29,9 +29,8 @@ pub const MODULE_UPDATE_TMP_DIR: &str = concatcp!(ADB_DIR, "modules_update/"); pub const SYSTEM_RW_DIR: &str = concatcp!(MODULE_DIR, ".rw/"); pub const TEMP_DIR: &str = "/debug_ramdisk"; -pub const TEMP_DIR_LEGACY: &str = "/sbin"; - pub const MODULE_WEB_DIR: &str = "webroot"; +pub const MODULE_ACTION_SH: &str = "action.sh"; pub const DISABLE_FILE_NAME: &str = "disable"; pub const UPDATE_FILE_NAME: &str = "update"; pub const REMOVE_FILE_NAME: &str = "remove"; diff --git a/userspace/ksud/src/init_event.rs b/userspace/ksud/src/init_event.rs index 7107ff74225d..5839c1957fa4 100644 --- a/userspace/ksud/src/init_event.rs +++ b/userspace/ksud/src/init_event.rs @@ -191,7 +191,7 @@ pub fn on_post_data_fs() -> Result<()> { } // mount temp dir - if let Err(e) = mount::mount_tmpfs(utils::get_tmp_path()) { + if let Err(e) = mount::mount_tmpfs(defs::TEMP_DIR) { warn!("do temp dir mount failed: {}", e); } diff --git a/userspace/ksud/src/module.rs b/userspace/ksud/src/module.rs index ac3ff213b62f..28c5df405795 100644 --- a/userspace/ksud/src/module.rs +++ b/userspace/ksud/src/module.rs @@ -570,6 +570,11 @@ pub fn uninstall_module(id: &str) -> Result<()> { }) } +pub fn run_action(id: &str) -> Result<()> { + let action_script_path = format!("/data/adb/modules/{}/action.sh", id); + exec_script(&action_script_path, true) +} + fn _enable_module(module_dir: &str, mid: &str, enable: bool) -> Result<()> { let src_module_path = format!("{module_dir}/{mid}"); let src_module = Path::new(&src_module_path); @@ -668,11 +673,13 @@ fn _list_modules(path: &str) -> Vec> { let update = path.join(defs::UPDATE_FILE_NAME).exists(); let remove = path.join(defs::REMOVE_FILE_NAME).exists(); let web = path.join(defs::MODULE_WEB_DIR).exists(); + let action = path.join(defs::MODULE_ACTION_SH).exists(); module_prop_map.insert("enabled".to_owned(), enabled.to_string()); module_prop_map.insert("update".to_owned(), update.to_string()); module_prop_map.insert("remove".to_owned(), remove.to_string()); module_prop_map.insert("web".to_owned(), web.to_string()); + module_prop_map.insert("action".to_owned(), action.to_string()); if result.is_err() { warn!("Failed to parse module.prop: {}", module_prop.display()); diff --git a/userspace/ksud/src/mount.rs b/userspace/ksud/src/mount.rs index 11be898bbeb7..a15deb976092 100644 --- a/userspace/ksud/src/mount.rs +++ b/userspace/ksud/src/mount.rs @@ -63,21 +63,18 @@ pub fn mount_ext4(source: impl AsRef, target: impl AsRef) -> Result< .attach(source) .with_context(|| "Failed to attach loop")?; let lo = new_loopback.path().ok_or(anyhow!("no loop"))?; - if let Result::Ok(fs) = fsopen("ext4", FsOpenFlags::FSOPEN_CLOEXEC) { - let fs = fs.as_fd(); - fsconfig_set_string(fs, "source", lo)?; - fsconfig_create(fs)?; - let mount = fsmount(fs, FsMountFlags::FSMOUNT_CLOEXEC, MountAttrFlags::empty())?; - move_mount( - mount.as_fd(), - "", - CWD, - target.as_ref(), - MoveMountFlags::MOVE_MOUNT_F_EMPTY_PATH, - )?; - } else { - mount(lo, target.as_ref(), "ext4", MountFlags::empty(), "")?; - } + let fs = fsopen("ext4", FsOpenFlags::FSOPEN_CLOEXEC)?; + let fs = fs.as_fd(); + fsconfig_set_string(fs, "source", lo)?; + fsconfig_create(fs)?; + let mount = fsmount(fs, FsMountFlags::FSMOUNT_CLOEXEC, MountAttrFlags::empty())?; + move_mount( + mount.as_fd(), + "", + CWD, + target.as_ref(), + MoveMountFlags::MOVE_MOUNT_F_EMPTY_PATH, + )?; Ok(()) } @@ -157,27 +154,18 @@ pub fn mount_overlayfs( #[cfg(any(target_os = "linux", target_os = "android"))] pub fn mount_tmpfs(dest: impl AsRef) -> Result<()> { info!("mount tmpfs on {}", dest.as_ref().display()); - if let Result::Ok(fs) = fsopen("tmpfs", FsOpenFlags::FSOPEN_CLOEXEC) { - let fs = fs.as_fd(); - fsconfig_set_string(fs, "source", KSU_OVERLAY_SOURCE)?; - fsconfig_create(fs)?; - let mount = fsmount(fs, FsMountFlags::FSMOUNT_CLOEXEC, MountAttrFlags::empty())?; - move_mount( - mount.as_fd(), - "", - CWD, - dest.as_ref(), - MoveMountFlags::MOVE_MOUNT_F_EMPTY_PATH, - )?; - } else { - mount( - KSU_OVERLAY_SOURCE, - dest.as_ref(), - "tmpfs", - MountFlags::empty(), - "", - )?; - } + let fs = fsopen("tmpfs", FsOpenFlags::FSOPEN_CLOEXEC)?; + let fs = fs.as_fd(); + fsconfig_set_string(fs, "source", KSU_OVERLAY_SOURCE)?; + fsconfig_create(fs)?; + let mount = fsmount(fs, FsMountFlags::FSMOUNT_CLOEXEC, MountAttrFlags::empty())?; + move_mount( + mount.as_fd(), + "", + CWD, + dest.as_ref(), + MoveMountFlags::MOVE_MOUNT_F_EMPTY_PATH, + )?; Ok(()) } @@ -188,29 +176,20 @@ pub fn bind_mount(from: impl AsRef, to: impl AsRef) -> Result<()> { from.as_ref().display(), to.as_ref().display() ); - if let Result::Ok(tree) = open_tree( + let tree = open_tree( CWD, from.as_ref(), OpenTreeFlags::OPEN_TREE_CLOEXEC | OpenTreeFlags::OPEN_TREE_CLONE | OpenTreeFlags::AT_RECURSIVE, - ) { - move_mount( - tree.as_fd(), - "", - CWD, - to.as_ref(), - MoveMountFlags::MOVE_MOUNT_F_EMPTY_PATH, - )?; - } else { - mount( - from.as_ref(), - to.as_ref(), - "", - MountFlags::BIND | MountFlags::REC, - "", - )?; - } + )?; + move_mount( + tree.as_fd(), + "", + CWD, + to.as_ref(), + MoveMountFlags::MOVE_MOUNT_F_EMPTY_PATH, + )?; Ok(()) } diff --git a/userspace/ksud/src/sepolicy.rs b/userspace/ksud/src/sepolicy.rs index 581c416a8185..b7286a06a769 100644 --- a/userspace/ksud/src/sepolicy.rs +++ b/userspace/ksud/src/sepolicy.rs @@ -11,7 +11,7 @@ use nom::{ sequence::Tuple, IResult, Parser, }; -use std::{path::Path, vec}; +use std::{ffi, path::Path, vec}; type SeObject<'a> = Vec<&'a str>; @@ -352,10 +352,11 @@ where let mut statements = vec![]; for line in input.split(['\n', ';']) { - if line.trim().is_empty() { + let trimmed_line = line.trim(); + if trimmed_line.is_empty() || trimmed_line.starts_with('#') { continue; } - if let Ok((_, statement)) = PolicyStatement::parse(line.trim()) { + if let Ok((_, statement)) = PolicyStatement::parse(trimmed_line) { statements.push(statement); } else if strict { bail!("Failed to parse policy statement: {}", line) @@ -659,19 +660,19 @@ impl<'a> TryFrom<&'a PolicyStatement<'a>> for Vec { struct FfiPolicy { cmd: u32, subcmd: u32, - sepol1: *const libc::c_char, - sepol2: *const libc::c_char, - sepol3: *const libc::c_char, - sepol4: *const libc::c_char, - sepol5: *const libc::c_char, - sepol6: *const libc::c_char, - sepol7: *const libc::c_char, + sepol1: *const ffi::c_char, + sepol2: *const ffi::c_char, + sepol3: *const ffi::c_char, + sepol4: *const ffi::c_char, + sepol5: *const ffi::c_char, + sepol6: *const ffi::c_char, + sepol7: *const ffi::c_char, } -fn to_c_ptr(pol: &PolicyObject) -> *const libc::c_char { +fn to_c_ptr(pol: &PolicyObject) -> *const ffi::c_char { match pol { PolicyObject::None | PolicyObject::All => std::ptr::null(), - PolicyObject::One(s) => s.as_ptr().cast::(), + PolicyObject::One(s) => s.as_ptr().cast::(), } } diff --git a/userspace/ksud/src/su.rs b/userspace/ksud/src/su.rs index b19bf7a21ca1..7884137e7ef2 100644 --- a/userspace/ksud/src/su.rs +++ b/userspace/ksud/src/su.rs @@ -19,30 +19,16 @@ use rustix::{ #[cfg(any(target_os = "linux", target_os = "android"))] pub fn grant_root(global_mnt: bool) -> Result<()> { - const KERNEL_SU_OPTION: u32 = 0xDEAD_BEEF; - const CMD_GRANT_ROOT: u64 = 0; - - let mut result: u32 = 0; - unsafe { - #[allow(clippy::cast_possible_wrap)] - libc::prctl( - KERNEL_SU_OPTION as i32, // supposed to overflow - CMD_GRANT_ROOT, - 0, - 0, - std::ptr::addr_of_mut!(result).cast::(), - ); - } + rustix::process::ksu_grant_root()?; - anyhow::ensure!(result == KERNEL_SU_OPTION, "grant root failed"); - let mut command = std::process::Command::new("sh"); + let mut command = Command::new("sh"); let command = unsafe { command.pre_exec(move || { if global_mnt { let _ = utils::switch_mnt_ns(1); let _ = utils::unshare_mnt_ns(); } - std::result::Result::Ok(()) + Result::Ok(()) }) }; // add /data/adb/ksu/bin to PATH @@ -64,7 +50,7 @@ fn print_usage(program: &str, opts: Options) { fn set_identity(uid: u32, gid: u32, groups: &[u32]) { #[cfg(any(target_os = "linux", target_os = "android"))] { - rustix::process::set_groups( + rustix::thread::set_thread_groups( groups .iter() .map(|g| unsafe { Gid::from_raw(*g) }) @@ -89,7 +75,7 @@ pub fn root_shell() -> Result<()> { // we are root now, this was set in kernel! use anyhow::anyhow; - let env_args: Vec = std::env::args().collect(); + let env_args: Vec = env::args().collect(); let program = env_args[0].clone(); let args = env_args .iter() @@ -154,7 +140,7 @@ pub fn root_shell() -> Result<()> { .collect::>(); let matches = match opts.parse(&args[1..]) { - std::result::Result::Ok(m) => m, + Result::Ok(m) => m, Err(f) => { println!("{f}"); print_usage(&program, opts); @@ -282,7 +268,7 @@ pub fn root_shell() -> Result<()> { set_identity(uid, gid, &groups); - std::result::Result::Ok(()) + Result::Ok(()) }) }; diff --git a/userspace/ksud/src/utils.rs b/userspace/ksud/src/utils.rs index 9bacfa366496..c08fdeee26c4 100644 --- a/userspace/ksud/src/utils.rs +++ b/userspace/ksud/src/utils.rs @@ -1,17 +1,15 @@ use anyhow::{bail, Context, Error, Ok, Result}; use std::{ - fs::{self, create_dir_all, remove_file, write, File, OpenOptions}, + fs::{create_dir_all, remove_file, write, File, OpenOptions}, io::{ ErrorKind::{AlreadyExists, NotFound}, Write, }, path::Path, process::Command, - sync::OnceLock, }; use crate::{assets, boot_patch, defs, ksucalls, module, restorecon}; -use std::fs::metadata; #[allow(unused_imports)] use std::fs::{set_permissions, Permissions}; #[cfg(unix)] @@ -189,68 +187,6 @@ pub fn has_magisk() -> bool { which::which("magisk").is_ok() } -fn is_ok_empty(dir: &str) -> bool { - use std::result::Result::Ok; - - match fs::read_dir(dir) { - Ok(mut entries) => entries.next().is_none(), - Err(_) => false, - } -} - -fn find_temp_path() -> String { - use std::result::Result::Ok; - - if is_ok_empty(defs::TEMP_DIR) { - return defs::TEMP_DIR.to_string(); - } - - // Try to create a random directory in /dev/ - let r = tempdir::TempDir::new_in("/dev/", ""); - match r { - Ok(tmp_dir) => { - if let Some(path) = tmp_dir.into_path().to_str() { - return path.to_string(); - } - } - Err(_e) => {} - } - - let dirs = [ - defs::TEMP_DIR, - "/patch_hw", - "/oem", - "/root", - defs::TEMP_DIR_LEGACY, - ]; - - // find empty directory - for dir in dirs { - if is_ok_empty(dir) { - return dir.to_string(); - } - } - - // Fallback to non-empty directory - for dir in dirs { - if metadata(dir).is_ok() { - return dir.to_string(); - } - } - - "".to_string() -} - -pub fn get_tmp_path() -> &'static str { - static CHOSEN_TMP_PATH: OnceLock = OnceLock::new(); - - CHOSEN_TMP_PATH.get_or_init(|| { - let r = find_temp_path(); - log::info!("Chosen temp_path: {}", r); - r - }) -} - #[cfg(target_os = "android")] fn link_ksud_to_bin() -> Result<()> { let ksu_bin = PathBuf::from(defs::DAEMON_PATH); diff --git a/website/docs/.vitepress/locales/zh_TW.ts b/website/docs/.vitepress/locales/zh_TW.ts index 3a92d45b2cf7..5d6fd9e8f14e 100644 --- a/website/docs/.vitepress/locales/zh_TW.ts +++ b/website/docs/.vitepress/locales/zh_TW.ts @@ -6,7 +6,7 @@ const pkg = require('vitepress/package.json') export default defineConfig({ lang: 'zh-TW', - description: '一個以核心為基礎,適用於 Android GKI 的 Root 解決方案。', + description: '一個基於核心,適用於 Android GKI 的 Root 解決方案。', themeConfig: { nav: nav(), diff --git a/website/docs/guide/how-to-integrate-for-non-gki.md b/website/docs/guide/how-to-integrate-for-non-gki.md index 242246a5c360..b4e6c80253f6 100644 --- a/website/docs/guide/how-to-integrate-for-non-gki.md +++ b/website/docs/guide/how-to-integrate-for-non-gki.md @@ -22,7 +22,7 @@ curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh ``` :::info -[KernelSU 1.0 no longer supports non-GKI kernels](https://github.com/tiann/KernelSU/issues/1705). The last supported version is `v0.9.5`, please make sure to use the correct branch. +[KernelSU 1.0 and later versions no longer support non-GKI kernels](https://github.com/tiann/KernelSU/issues/1705). The last supported version is `v0.9.5`, please make sure to use the correct version. ::: Then, you should check if *kprobe* is enabled in your kernel config, if it is not, please add these configs to it: @@ -55,22 +55,10 @@ If kprobe does not work in your kernel (may be an upstream or kernel bug below 4 First, add KernelSU to your kernel source tree: -::: code-group - -```sh[Latest tag(stable)] -curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash - -``` - -```sh[ main branch(dev)] -curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s main -``` - -```sh[Select tag(Such as v0.5.2)] -curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s v0.5.2 +```sh +curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s v0.9.5 ``` -::: - Keep in mind that on some devices, your defconfig may be in `arch/arm64/configs` or in other cases `arch/arm64/configs/vendor/your_defconfig`. For whichever defconfig you're using, make sure to enable `CONFIG_KSU` with `y` to enable or `n` to disable it. For example, in case you chose to enable it, you defconfig should contain the following string: ```txt diff --git a/website/docs/id_ID/guide/how-to-integrate-for-non-gki.md b/website/docs/id_ID/guide/how-to-integrate-for-non-gki.md index 11bd7905e9ba..d26d3c62ceea 100644 --- a/website/docs/id_ID/guide/how-to-integrate-for-non-gki.md +++ b/website/docs/id_ID/guide/how-to-integrate-for-non-gki.md @@ -17,23 +17,13 @@ KernelSU menggunakan kprobe untuk melakukan hook kernel, jika *kprobe* berjalan Pertama, tambahkan KernelSU ke dalam berkas kernel source tree: -- Latest tag(stable) - -```sh -curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash - -``` - -- main branch(dev) - ```sh -curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s main +curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s v0.9.5 ``` -- Select tag(Such as v0.5.2) - -```sh -curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s v0.5.2 -``` +:::info +[KernelSU 1.0 dan versi yang lebih baru tidak lagi mendukung kernel non-GKI](https://github.com/tiann/KernelSU/issues/1705). Versi terakhir yang didukung adalah `v0.9.5`, pastikan untuk menggunakan versi yang benar. +::: Kemudian, Anda harus memeriksa apakah *kprobe* diaktifkan dalam konfigurasi kernel Anda, jika tidak, tambahkan konfigurasi ini ke dalamnya: @@ -56,7 +46,7 @@ Jika kprobe tidak dapat bekerja pada kernel Anda (mungkin karena bug di upstream Pertama, tambahkan KernelSU ke dalam direktori kernel source tree: ```sh -curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash - +curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s v0.9.5 ``` Kemudian, tambahkan panggilan KernelSU ke source kernel, berikut ini adalah patch yang dapat dirujuk: diff --git a/website/docs/pt_BR/guide/how-to-integrate-for-non-gki.md b/website/docs/pt_BR/guide/how-to-integrate-for-non-gki.md index e880f99b1ea2..1652a00d1a72 100644 --- a/website/docs/pt_BR/guide/how-to-integrate-for-non-gki.md +++ b/website/docs/pt_BR/guide/how-to-integrate-for-non-gki.md @@ -18,9 +18,13 @@ O KernelSU usa kprobe para fazer ganchos do kernel, se o kprobe funcionar bem em Primeiro, adicione o KernelSU à árvore de origem do kernel: ```sh -curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash - +curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s v0.9.5 ``` +:::info INFORMAÇÕES +[KernelSU 1.0 e versões posteriores não suportam mais kernels não GKI](https://github.com/tiann/KernelSU/issues/1705). A última versão suportada é a `v0.9.5`, por favor, certifique-se de usar a versão correta. +::: + Então, você deve verificar se o kprobe está ativado na configuração do seu kernel, se não estiver, adicione estas configurações a ele: ```txt @@ -51,22 +55,10 @@ Se o kprobe não funcionar no seu kernel (pode ser um bug do upstream ou do kern Primeiro, adicione o KernelSU à árvore de origem do kernel: -::: code-group - -```sh[Tag mais recente (estável)] -curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash - -``` - -```sh[Branch principal (dev)] -curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s main -``` - -```sh[Selecionar tag (como v0.5.2)] -curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s v0.5.2 +```sh +curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s v0.9.5 ``` -::: - Tenha em mente que em alguns dispositivos, seu defconfig pode estar em `arch/arm64/configs` ou em outros casos `arch/arm64/configs/vendor/your_defconfig`. Para qualquer defconfig que você estiver usando, certifique-se de ativar `CONFIG_KSU` com `y` para ativa-lo ou `n` para desativa-lo. Por exemplo, caso você opte por ativa-lo, seu defconfig deverá conter a seguinte string: ```txt diff --git a/website/docs/public/templates/kernelmanager.root b/website/docs/public/templates/kernelmanager.root index 899e0d64e05d..3973dd1d5972 100644 --- a/website/docs/public/templates/kernelmanager.root +++ b/website/docs/public/templates/kernelmanager.root @@ -10,13 +10,10 @@ "READPROC" ], "capabilities":[ - "CAP_SYS_MODULE", - "CAP_SYS_NICE", - "CAP_SYS_RESOURCE", "CAP_KILL", "CAP_SYSLOG", - "CAP_PERFMON", - "CAP_SYS_BOOT" + "CAP_SYS_BOOT", + "CAP_DAC_OVERRIDE" ], "context":"u:r:su:s0", "namespace":"INHERITED", diff --git a/website/docs/repos.json b/website/docs/repos.json index 6ae95b901627..aabacea7420e 100644 --- a/website/docs/repos.json +++ b/website/docs/repos.json @@ -54,7 +54,7 @@ "kernel_name": "android_kernel_xiaomi_onc", "kernel_link": "https://github.com/hadadarjt/android_kernel_xiaomi_onc/tree/los21-KSU/local-non-gerrit-review", "devices": "Redmi 7: onclite | Redmi Y3: onc" - }, + }, { "maintainer": "HMTheBoy154", "maintainer_link": "https://github.com/hmtheboy154", @@ -691,5 +691,40 @@ "kernel_name": "kernel_xiaomi_chopin", "kernel_link": "https://github.com/ChopinKernels/kernel_xiaomi_chopin_android_S", "devices": "Redmi Note 10 Pro 5G (chopin) / Poco X3 GT (choping)" + }, + { + "maintainer": "Fede2782", + "maintainer_link": "https://github.com/Fede2782", + "kernel_name": "android_kernel_motorola_sdm632", + "kernel_link": "https://github.com/Fede2782/android_kernel_motorola_sdm632", + "devices": "Motorola Moto G7 (river)" + }, + { + "maintainer": "EmanuelCN", + "maintainer_link": "https://github.com/EmanuelCN", + "kernel_name": "N0Kernel", + "kernel_link": "https://github.com/EmanuelCN/kernel_xiaomi_sm8250", + "devices": "Poco F3 (alioth) | Mi 10T/Pro (apollo) | Poco F4 (munch)" + }, + { + "maintainer": "kvsnr113", + "maintainer_link": "https://github.com/kvsnr113", + "kernel_name": "NOVA Kernel", + "kernel_link": "https://github.com/kvsnr113/xiaomi_sm8250_kernel", + "devices": "Poco F3 (alioth) | Mi 10T/Pro (apollo) | Poco F4 (munch)" + }, + { + "maintainer": "anVzdGFtb25", + "maintainer_link": "https://github.com/anVzdGFtb25", + "kernel_name": "android_kernel_xiaomi_mt6768", + "kernel_link": "https://github.com/anVzdGFtb25/android_kernel_xiaomi_mt6768", + "devices": "Redmi 9 ( lancelot )" + }, + { + "maintainer": "rifsxd", + "maintainer_link": "https://github.com/rifsxd", + "kernel_name": "android_kernel_realme_RMX3511", + "kernel_link": "https://github.com/rifsxd/android_kernel_realme_RMX3511", + "devices": "Realme C35 (RMX3511)" } ] diff --git a/website/docs/zh_CN/guide/how-to-integrate-for-non-gki.md b/website/docs/zh_CN/guide/how-to-integrate-for-non-gki.md index 60668221bef1..5f20068259fd 100644 --- a/website/docs/zh_CN/guide/how-to-integrate-for-non-gki.md +++ b/website/docs/zh_CN/guide/how-to-integrate-for-non-gki.md @@ -22,7 +22,7 @@ curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh ``` :::info -[KernelSU 1.0 已经不再支持非 GKI 内核](https://github.com/tiann/KernelSU/issues/1705),最后的支持版本为 `v0.9.5`,请注意使用正确的分支。 +[KernelSU 1.0 及更高版本已经不再支持非 GKI 内核](https://github.com/tiann/KernelSU/issues/1705),最后的支持版本为 `v0.9.5`,请注意使用正确的版本。 ::: 然后,你需要检查你的内核是否开启了 *kprobe* 相关的配置,如果没有开启,需要添加以下配置: @@ -51,7 +51,7 @@ CONFIG_KPROBE_EVENTS=y 首先,把 KernelSU 添加到你的内核源码树,在内核的根目录执行以下命令: ```sh -curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash - +curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s v0.9.5 ``` 请注意,某些设备的defconfig文件可能在`arch/arm64/configs/设备代号_defconfig`或位于`arch/arm64/configs/vendor/设备代号_defconfig`。在您的defconfig文件中,将 `CONFIG_KSU`设置为`y`以启用KernelSU,或设置为`n`以禁用。比如在某个defconfig中: diff --git a/website/docs/zh_CN/guide/module.md b/website/docs/zh_CN/guide/module.md index 3690ed02915b..89dcc8e5988a 100644 --- a/website/docs/zh_CN/guide/module.md +++ b/website/docs/zh_CN/guide/module.md @@ -72,9 +72,9 @@ KernelSU 模块就是一个放置在 `/data/adb/modules` 内且满足如下结 │ │ │ │ *** 自动生成的目录,不要手动创建或者修改! *** │ │ -│ ├── vendor <--- A symlink to $MODID/system/vendor -│ ├── product <--- A symlink to $MODID/system/product -│ ├── system_ext <--- A symlink to $MODID/system/system_ext +│ ├── vendor <--- 如果 /system/vendor 是符号链接且存在,从 $MODID/system/vendor 移动到模块根目录 +│ ├── product <--- 如果 /system/product 是符号链接且存在,从 $MODID/system/product 移动到模块根目录 +│ ├── system_ext <--- 如果 /system/system_ext 是符号链接且存在,从 $MODID/system/system_ext 移动到模块根目录 │ │ │ │ *** Any additional files / folders are allowed *** │ │ diff --git a/website/docs/zh_TW/guide/app-profile.md b/website/docs/zh_TW/guide/app-profile.md index 659c5660164b..993857702927 100644 --- a/website/docs/zh_TW/guide/app-profile.md +++ b/website/docs/zh_TW/guide/app-profile.md @@ -2,7 +2,8 @@ App Profile 是 KernelSU 提供的一種針對各種應用程式自訂其使用配置的機制。 -對於授予了root 權限(也即可以使用 `su`)的應用程式來說,App Profile 也可以稱為Root Profile,它可以自訂 `su` 的 `uid`, `gid` , `groups` , ` capabilities` 以及 `SELinux context` 規則,從而限制 root 使用者的權限;例如可以針對防火牆應用程式僅授予網路權限,而不授予檔案存取權限,針對凍結類別應用程式僅授予 shell 權限而不是直接給 root ;透過最小化權限原則**把權力關進籠子裡**。 +對於授予了 root 權限(即可以使用 `su`)的應用程式來說,App Profile 也可以稱為 Root Profile,它可以自訂 `su` 的 `uid`、`gid`、`groups`、` capabilities` 以及 `SELinux context` 規則,從而限制 root 使用者的權限。 +例如可以針對防火牆應用程式僅授予網路權限,而不授予檔案存取權限,針對凍結類別應用程式僅授予 shell 權限而不是直接給 root ;透過最小化權限原則**把權力關進籠子裡**。 對於沒有被授予 root 權限的普通應用,App Profile 可以控制核心以及模組系統對此應用的行為;例如是否需要針對此應用程式卸載模組造成的修改等。核心和模組系統可以透過此配置決定是否要做一些類似「隱藏痕跡」類別的操作。 @@ -16,8 +17,8 @@ UID 為 0 的使用者稱為 root 使用者,GID 為 0 的群組稱為 root 群 對於 Android 系統來說,每個應用程式都是一個單獨的使用者(不考慮 share uid 的情況),擁有一個唯一的 UID。例如 `0` 是 root 使用者,`1000` 是 `system`,`2000` 是 ADB shell,10000-19999 的是一般使用者。 -:::info -此處的 UID 跟 Android 系統的多使用者,或者說工作資料(Work Profile),不是概念。工作資料實際上是對 UID 進行分片實現的,例如 10000-19999 是主使用者,110000-119999 是工作資料;他們中的任何一個普通應用都擁有自己獨有的 UID。 +:::info 補充 +此處的 UID 跟 Android 系統的多使用者,或者說工作資料(Work Profile),是不同概念。工作資料實際上是對 UID 進行分片實現的,例如 10000-19999 是主使用者,110000-119999 是工作資料;他們中的任何一個普通應用都擁有自己獨有的 UID。 ::: 每一個應用程式可以有若干個群組,GID 使其主要的群組,通常與 UID 一致;其他的群組稱為補充群組(groups)。某些權限是透過群組控制的,例如網路訪問,藍牙等。 @@ -29,7 +30,7 @@ oriole:/ $ id uid=2000(shell) gid=2000(shell) groups=2000(shell),1004(input),1007(log),1011(adb),1015(sdcard_rw),1028(sdcard_r),1078(ext_data_ww) (ext_obb_rw),3001(net_bt_admin),3002(net_bt),3003(inet),3006(net_bw_stats),3009(readproc),3011(uhid),3012(readreadtracefs:s05: ``` -其中,UID 為`2000`,GID 也即主要組ID 也為`2000`;除此之外它還在許多補充組裡面,例如`inet` 組代表可以創建`AF_INET` 和`AF_INET6` 的socket(存取網路),`sdcard_rw` 代表可以讀寫sdcard 等。 +其中,UID 為`2000`,GID 也即主要組 ID 也為 `2000`;除此之外它還在許多補充組裡面,例如 `inet` 組代表可以創建 `AF_INET` 和 `AF_INET6` 的 socket(存取網路),`sdcard_rw` 代表可以讀寫 sdcard 等。 KernelSU 的 Root Profile 可以自訂執行 `su` 後 root 程式的 UID, GID 和 groups。例如,你可以設定某個 root 應用程式的Root Profile 其UID 為`2000`,這表示此應用程式在使用`su` 的時候,它的實際權限是ADB Shell 等級;你可以去掉groups 中的`inet` ,這樣這個`su` 就無法存取網路。 @@ -43,7 +44,7 @@ App Profile 只是控制 root 應用程式使用 `su` 後的權限,它並非 Capabilities 是 Linux 的一種分權機制。 -傳統的 UNIX 系統為了執行權限檢查,將流程分為兩類:特權程式(其有效使用者 ID 為 0,稱為超級使用者或 root)和非特權程式(其有效 UID 為非零)。特權程式會繞過所有核心權限檢查,而非特權程式則根據其憑證(通常是有效UID、有效GID和補充群組清單)進行完整的權限檢查。 +傳統的 UNIX 系統為了執行權限檢查,將流程分為兩類:特權程式(其等效使用者 ID 為 0,稱為超級使用者或 root)和非特權程式(其等效 UID 為非零)。特權程式會繞過所有核心權限檢查,而非特權程式則根據其憑證(通常是等校 UID、等效 GID 和補充群組清單)進行完整的權限檢查。 從 Linux 2.2開始,Linux 將傳統上與超級使用者關聯的特權分解為獨立的單元,稱為 Capabilities(有的也翻譯為「權能」),它們可以獨立啟用和停用。 @@ -52,7 +53,7 @@ Capabilities 是 Linux 的一種分權機制。 KernelSU 的 Root Profile 可以自訂執行 `su` 後 root 程式的 Capabilities,從而實現只授予「部分 root 權限」。與上面介紹的UID, GID 不同,某些 root 應用就是需要 `su` 後 UID 是 `0`,此時我們可以透過限制這個 UID 為 `0` 的 root 使用者的 Capabilities,就可以限制它能夠執行的操作。 :::tip 強烈建議 -Linux 系統關於 Capability 的[官方文件](https://man7.org/linux/man-pages/man7/capabilities.7.html),解釋了每一項Capability 所代表的能力,寫的非常詳細,如果你想要自訂Capabilities,請務必先閱讀此文件。 +Linux 的 Capability [官方文件](https://man7.org/linux/man-pages/man7/capabilities.7.html)詳細解釋了每一項 Capability 所代表的能力,如果你想要自訂Capabilities,請務必先閱讀此文件。 ::: ### SELinux {#selinux} @@ -76,7 +77,7 @@ SELinux 的完整概念比較複雜,我們這裡不打算講解它的具體運 KernelSU 的 Root Profile 可以自訂執行 `su` 後 root 程式的 SELinux context,並且可以針對這個 context 設定特定的存取控制規則,從而更精細地控制 root 權限。 -通常情況下,應用程式執行 `su` 後,會將進程切換到一個**不受任何限制** 的SELinux 域,例如`u:r:su:s0`,透過 Root Profile,我們可以將它切換到一個自訂的網域,例如 `u:r:app1:s0`,然後為這個網域制定一系列規則: +通常情況下,應用程式執行 `su` 後,會將進程切換到一個**不受任何限制** 的 SELinux 域,例如`u:r:su:s0`,透過 Root Profile,我們可以將它切換到一個自訂的作用域,例如 `u:r:app1:s0`,然後為這個作用域制定一系列規則: ```sh type app1 @@ -85,34 +86,34 @@ typeattribute app1 mlstrustedsubject allow app1 * * * ``` -注意:此處的 `allow app1 * * *` 僅僅作為演示方便而使用,實際過程中不應使用這個規則,因為它跟 permissive 區別不大。 +注意:此處的 `allow app1 * * *` 僅僅作為示範方便而使用,實際過程中不應使用這個規則,因為它跟寬容模式區別不大。 ### 逃逸 {#escalation} 如果 Root Profile 的配置不合理,那麼可能會發生逃逸的情況:Root Profile 的限制會意外失效。 -例如,如果你為ADB shell 使用者設定允許root 權限(這是相當常見的情況);然後你給某個普通應用程式允許root 權限,但是配置它的root profile 中的UID 為2000(ADB shell 使用者的UID);那麼此時,這個App 可以透過執行兩次 `su` 來獲得完整的root 權限: +例如,如果你為ADB shell 使用者設定允許root 權限(這是相當常見的情況);然後你給某個普通應用程式允許 root 權限,但是配置它的 root profile 中的 UID 為 2000(ADB shell 使用者的UID);那麼此時,這個 App 可以透過執行兩次 `su` 來獲得完整的root 權限: -1. 第一次執行 `su`,由於 App Profile 強制生效,會正常切換到 UID 為 `2000(adb shell)` 而非 `0(root)`。 -2. 第二次執行 `su`,由於此時它 UID 是 `2000`,而你給 `2000(adb shell)` 配置了允許 root,它會獲得完整的 root 權限! +1. 第一次執行 `su`,由於 App Profile 強制生效,會正常切換到 UID 為 `2000` (adb shell) 而非 `0` (root)。 +2. 第二次執行 `su`,由於此時它 UID 是 `2000`,而你給 `2000` (adb shell) 配置了允許 root,它會獲得完整的 root 權限! :::warning 注意 這是完全符合預期的行為,並非 BUG!因此我們建議: -如果你的確需要給 adb 授予 root 權限(例如你是開發者),那麼不建議你在配置 Root Profile 的時候將 UID 改成 `2000`,用 `1000(system)` 會更好。 +如果你的確需要給 adb 授予 root 權限(例如你是開發者),那麼不建議你在配置 Root Profile 的時候將 UID 改成 `2000`,用 `1000` (system) 會更好。 ::: ## Non Root Profile {#non-root-profile} ### 卸載模組 {#umount-modules} -KernelSU 提供了一種 systemless 的方式來修改系統分區,這是透過掛載 overlayfs 來實現的。但有些情況下,App 可能會對這種行為比較敏感;因此,我們可以透過設定「卸載模組」來卸載掛載在這些應用程式上的模組。 +KernelSU 提供了一種無須直接修改系統分區的方式 (systemless) 來修改系統分區,這是透過掛載 overlayfs 來實現的。但有些情況下,App 可能會對這種行為比較敏感;因此,我們可以透過設定「卸載模組」來卸載掛載在這些應用程式上的模組。 另外,KernelSU 管理器的設定介面還提供了一個「預設卸載模組」的開關,這個開關預設是**開啟**的,這表示**如果不對應用程式做額外的設定**,預設情況下 KernelSU 或某些模組會對此應用程式執行卸載操作。當然,如果你不喜歡這個設定或這個設定會影響某些 App,你可以有以下選擇: 1. 保持「預設卸載模組」的開關,然後針對不需要「卸載模組」的應用程式進行單獨的設置,在 App Profile 中關閉「卸載模組」;(相當於「白名單」)。 2. 關閉「預設卸載模組」的開關,然後針對需要「卸載模組」的應用程式進行單獨的設置,在 App Profile 中開啟「卸載模組」;(相當於「黑名單」)。 -:::info -KernelSU 在 5.10 及以上內核上,內核無須任何修改就可以卸載模組;但在 5.10 以下的設備上,這個開關僅僅是一個“設定”,KernelSU 本身不會做任何動作,如果你希望在 5.10 以前的內核可以卸載模組,你需要將 `path_unmount` 函數向後移植到 `fs/namespace.c`,您可以在[如何為非 GKI 核心整合 KernelSU](/zh_TW/guide/how-to-integrate-for-non-gki.html)的末尾獲取更多資訊。一些模組(如 ZygiskNext)也會透過這個設定決定是否需要卸載。 +:::info 提示 +KernelSU 在 5.10 及以上內核上,內核無須任何修改就可以卸載模組;但在 5.10 以下的設備上,這個開關僅僅是一個"設定",KernelSU 本身不會做任何動作,如果你希望在 5.10 以前的內核可以卸載模組,你需要將 `path_unmount` 函數向後移植到 `fs/namespace.c`,您可以在[如何為非 GKI 核心整合 KernelSU](how-to-integrate-for-non-gki.md#how-to-backport-path_unpount)獲取更多資訊。一些模組(如 ZygiskNext)也會透過這個設定決定是否需要卸載。 ::: \ No newline at end of file diff --git a/website/docs/zh_TW/guide/difference-with-magisk.md b/website/docs/zh_TW/guide/difference-with-magisk.md index 6cd291e171de..fdaa5fc9d940 100644 --- a/website/docs/zh_TW/guide/difference-with-magisk.md +++ b/website/docs/zh_TW/guide/difference-with-magisk.md @@ -1,4 +1,4 @@ -# KernelSU 與 Magisk 的差異 {#title} +# KernelSU 與 Magisk 的差異 {#difference-with-magisk} 儘管 KernelSU 模組和 Magisk 模組之間有許多相似之處,但由於它們完全不同的實作機制,不可避免地存在一些差異;如果您想讓您的模組同時在 Magisk 和 KernelSU 上運作,那麼您必須瞭解這些差異。 @@ -6,7 +6,7 @@ - 模組檔案格式:都以 Zip 的格式組織模組,並且模組的格式幾乎相同 - 模組安裝目錄:都位於 `/data/adb/modules` -- Systemless:都支援通過模組以無系統修改的方式來更改 `/system` +- 無系統修改:都支援透過模組以無系統修改的方式來更改 `/system` - `post-fs-data.sh`:執行階段和語義完全相同 - `service.sh`:執行階段和語義完全相同 - `system.prop`:完全相同 @@ -19,10 +19,10 @@ 以下是一些不同之處: -1. KernelSU 的模組不支援在 Recovery 中安裝。 +1. KernelSU 的模組無法在 Recovery 中安裝。 2. KernelSU 的模組沒有內建的 Zygisk 支援 (但您可以透過 [ZygiskNext](https://github.com/Dr-TSNG/ZygiskNext) 來使用 Zygisk 模組)。 3. KernelSU 模組取代或刪除檔案與 Magisk 完全不同。KernelSU 不支援 `.replace` 方法,相反,您需要透過 `mknod filename c 0 0` 建立相同名稱的資料夾以刪除對應檔案。 -4. BusyBox 的目錄不同;KernelSU 內建的 BusyBox 在 `/data/adb/ksu/bin/busybox` 而 Magisk 在 `/data/adb/magisk/busybox`;**注意此為 KernelSU 內部行為,未來可能會變更!** -5. KernelSU 不支援 `.replace` 檔案;但 KernelSU 支援 `REPLACE` 和 `REMOVE` 變數以移除或取代檔案 (資料夾)。 +4. BusyBox 的目錄不同。KernelSU 內建的 BusyBox 在 `/data/adb/ksu/bin/busybox`,而 Magisk 在 `/data/adb/magisk/busybox`。**注意此為 KernelSU 內部行為,未來可能會變更!** +5. KernelSU 不支援 `.replace` 檔案;但 KernelSU 支援 `REPLACE` 和 `REMOVE` 變數以移除或取代檔案與資料夾。 6. KernelSU 新增了 `boot-completed` 階段以在啟動完成時執行一些腳本。 -7. KernelSU 新增了 `post-mount` 階段,以便在掛載 overlayfs 後執行一些腳本 +7. KernelSU 新增了 `post-mount` 階段,以便在掛載 overlayfs 後執行一些腳本。 diff --git a/website/docs/zh_TW/guide/faq.md b/website/docs/zh_TW/guide/faq.md index 8f1c13400c26..57166b68c4ac 100644 --- a/website/docs/zh_TW/guide/faq.md +++ b/website/docs/zh_TW/guide/faq.md @@ -26,7 +26,7 @@ KernelSU 沒有內建 Zygisk 支援,但是您可以用 [ZygiskNext](https://gi KernelSU 的模組系統與 Magisk 的 magic mount 存在衝突,如果在 KernelSU 中啟用了任何模組,那麼整個 Magisk 將無法正常運作。 -但是如果您只使用 KernelSU 的 `su`,那么它會和 Magisk 一同運作:KernelSU 修改 `kernel` 、 Magisk 修改 `ramdisk`,它們可以搭配使用。 +但是如果您只使用 KernelSU 的 `su`,那么它會和 Magisk 一同運作:KernelSU 修改 `kernel`、Magisk 修改 `ramdisk`,它們可以搭配使用。 ## KernelSU 会取代 Magisk 嗎? @@ -49,11 +49,11 @@ KernelSU 的模組系統與 Magisk 的 magic mount 存在衝突,如果在 Kern ## 如何為舊版核心整合 KernelSU? -請參閱[指南](how-to-integrate-for-non-gki) +請參閱[指南](how-to-integrate-for-non-gki.md) ## 為何我的 Android 版本為 13,但核心版本卻是 "android12-5.10"? -核心版本與 Android 版本無關,如果您要刷新 KernelSU,請一律使用**核心版本**而非 Android 版本,如果你為 "android12-5.10" 的裝置刷新 Android 13 的核心,等候您的將會是開機迴圈。 +核心版本與 Android 版本無關,如果您要使用 KernelSU,請一律使用**核心版本**而非 Android 版本,如果你為 "android12-5.10" 的裝置寫入 Android 13 的核心,等候您的將會是開機迴圈。 ## 我是 GKI1.0,能用 KernelSU 嗎? diff --git a/website/docs/zh_TW/guide/how-to-build.md b/website/docs/zh_TW/guide/how-to-build.md index 034044945df1..89517c84b0d2 100644 --- a/website/docs/zh_TW/guide/how-to-build.md +++ b/website/docs/zh_TW/guide/how-to-build.md @@ -5,7 +5,7 @@ 1. [建置核心](https://source.android.com/docs/setup/build/building-kernels) 2. [標準核心映像 (GKI) 發行組建](https://source.android.com/docs/core/architecture/kernel/gki-release-builds) -::: warning +::: warning 警告 此文件適用於 GKI 裝置,如果您是舊版核心,請參閱[如何為非 GKI 裝置整合 KernelSU](how-to-integrate-for-non-gki) ::: @@ -20,7 +20,7 @@ repo init -m manifest.xml repo sync ``` -`` 是一個可以唯一確定組建的資訊清單檔案,您可以使用這個資訊清單進行可重新預測的組建。您需要從[標準核心映像 (GKI) 發行組建](https://source.android.com/docs/core/architecture/kernel/gki-release-builds) 下載資訊清單檔案 +`` 是一個可以唯一確定組建的資訊清單,您可以使用這個資訊清單進行可重新預測的組建。您需要從[標準核心映像 (GKI) 發行組建](https://source.android.com/docs/core/architecture/kernel/gki-release-builds)下載資訊清單。 ### 建置 {#build} @@ -34,14 +34,14 @@ LTO=thin BUILD_CONFIG=common/build.config.gki.aarch64 build/build.sh 不要忘記新增 `LTO=thin`,否則,如果您的電腦記憶體小於 24GB,建置可能會失敗。 -從 Android 13 開始,核心由 `bazel` 建置: +從 Android 13 開始,核心使用 `bazel` 建置: ```sh tools/bazel build --config=fast //common:kernel_aarch64_dist ``` -:::info -對於某些 Android 14 內核,要使 Wi-Fi/藍牙正常工作,可能需要刪除所有受 GKI 保護的匯出: +:::info 你可能需要知道... +對於某些 Android 14 核心,要使 Wi-Fi/藍牙正常工作,可能需要刪除所有受 GKI 保護的匯出: ```sh rm common/android/abi_gki_protected_exports_* @@ -52,22 +52,20 @@ rm common/android/abi_gki_protected_exports_* 如果您可以成功建置核心,那麼建置 KernelSU 就會非常輕鬆,依自己的需求在核心原始碼根目錄中執行以下任一命令: -- 最新 tag (穩定版本) +::: code-group -```sh +```sh[最新 tag (穩定版本)] curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash - ``` -- main 分支 (開發版本) - -```sh +```sh[main 分支 (開發版本)] curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s main ``` -- 選取 tag (例如 v0.5.2) - -```sh +```sh[選取 tag (例如 v0.5.2)] curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s v0.5.2 ``` +::: + 然後重新建置核心,您將會得到一個帶有 KernelSU 的核心映像! diff --git a/website/docs/zh_TW/guide/how-to-integrate-for-non-gki.md b/website/docs/zh_TW/guide/how-to-integrate-for-non-gki.md index 43b4980e9a66..a6c28485f313 100644 --- a/website/docs/zh_TW/guide/how-to-integrate-for-non-gki.md +++ b/website/docs/zh_TW/guide/how-to-integrate-for-non-gki.md @@ -1,4 +1,4 @@ -# 如何為非 GKI 核心整合 KernelSU {#introduction} +# 如何為非 GKI 核心整合 KernelSU {#how-to-integrate-kernelsu-for-non-gki-kernels} KernelSU 可以被整合到非 GKI 核心中,現在它最低支援到核心 4.14 版本;理論上也可以支援更低的版本。 @@ -11,31 +11,21 @@ KernelSU 可以被整合到非 GKI 核心中,現在它最低支援到核心 4. 1. 藉助 `kprobe` 自動整合 2. 手動修改核心原始碼 -## 使用 kprobe 整合 {#using-kprobes} +## 使用 kprobe 整合 {#integrate-with-kprobe} KernelSU 使用 kprobe 機制來處理核心的相關 hook,如果 *kprobe* 可以在您建置的核心中正常運作,那麼建議使用這個方法進行整合。 首先,把 KernelSU 新增至您的核心來源樹狀結構,再核心的根目錄執行以下命令: -- 最新 tag (稳定版本) - -```sh -curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash - -``` - -- main 分支(開發版本) - ```sh -curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s main +curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s v0.9.5 ``` -- 選取 tag (例如 v0.5.2) - -```sh -curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s v0.5.2 -``` +:::info 公告 +[KernelSU 1.0 及更新版本不再支援非 GKI 核心](https://github.com/tiann/KernelSU/issues/1705)。最後一個支援的版本為 `v0.9.5`,請確保使用的版本正確。 +::: -然後,您需要檢查您的核心是否啟用 *kprobe* 相關組態,如果未啟用,則需要新增以下組態: +然後,您需要檢查您的核心是否啟用 *kprobe*,如果未啟用,則需要新增以下設定: ``` CONFIG_KPROBES=y @@ -45,33 +35,40 @@ CONFIG_KPROBE_EVENTS=y 最後,重新建置您的核心即可。 -如果您發現 KPROBES 仍未生效,很有可能是因為它的相依性 `CONFIG_MODULES` 並未被啟用 (如果還是未生效請輸入 `make menuconfig` 搜尋 KPROBES 的其他相依性並啟用) +如果您發現 KPROBES 仍未生效,很有可能是因為它依賴的 `CONFIG_MODULES` 並未被啟用,如果還是未生效請輸入 `make menuconfig` 搜尋 KPROBES 的其他相依性並啟用。 如果您在整合 KernelSU 之後手機無法啟動,那麼很可能您的核心中 **kprobe 無法正常運作**,您需要修正這個錯誤,或者使用第二種方法。 :::tip 如何檢查 kprobe 是否損毀? -將 `KernelSU/kernel/ksu.c` 中的 `ksu_enable_sucompat()` 和 `ksu_enable_ksud()` 取消註解,如果正常開機,即 kprobe 已損毀;或者您可以手動嘗試使用 kprobe 功能,如果不正常,手機會直接重新啟動。 +將 `KernelSU/kernel/ksu.c` 中的 `ksu_enable_sucompat()` 和 `ksu_enable_ksud()` 註解掉,如果正常開機,即 kprobe 已損毀;或者您可以手動嘗試使用 kprobe 功能,如果不正常,手機會直接重新啟動。 ::: :::info 如何為非 GKI 核心啟用卸載模組功能 -如果你的內核版本小於 5.10,你應該將 `path_umount` 向後移植至 `fs/namespace.c`。 卸載模組功能依賴於這個函數。 如果你沒有向後移植 `path_umount`,卸載模組功能將無法工作。 你可以在底下查看更多關於 `path_unmount` 的資料。 +如果你的內核版本小於 5.10,你應該將 `path_umount` 向後移植至 `fs/namespace.c`。卸載模組功能依賴於這個函數。如果你沒有向後移植 `path_umount`,卸載模組功能將無法工作。你可以在[這裡查看更多關於 `path_unmount` 的資料](#how-to-backport-path_unpount)。 ::: -## 手動修改核心原始碼 {#modify-kernel-source-code} +## 手動修改核心原始碼 {#manually-modify-the-kernel-source} -如果 kprobe 無法正常運作 (可能是上游的錯誤或核心版本過低),那您可以嘗試這種方法: +如果 kprobe 無法正常運作 (在4.8之前可能是上游或核心的錯誤),那您可以嘗試這種方法: -首先,將 KernelSU 新增至您的原始碼樹狀結構,再核心的根目錄執行以下命令: +首先,將 KernelSU 新增至您的原始碼樹狀結構,在核心的根目錄執行以下命令: ```sh -curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash - +curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -s v0.9.5 +``` +請記住,在某些裝置上,您的 `defconfig` 可能位於 `arch/arm64/configs` 中,或在其他情況下位於 `arch/arm64/configs/vendor/你的defconfig` 中。無論您使用哪個 `defconfig`,請確保使用 `CONFIG_KSU=y` 啟用KernelSU,或使用 `n` 停用它。例如,如果您選擇啟用它,則 `defconfig` 應包含以下字串: +```conf +# KernelSU +CONFIG_KSU=y ``` 然後,手動修改核心原始碼,您可以參閱下方的 patch: -```diff +::: code-group + +```diff[exec.c] diff --git a/fs/exec.c b/fs/exec.c index ac59664eaecf..bdd585e1d2cc 100644 --- a/fs/exec.c @@ -97,7 +94,7 @@ index ac59664eaecf..bdd585e1d2cc 100644 return __do_execve_file(fd, filename, argv, envp, flags, NULL); } ``` -```diff +```diff[open.c] diff --git a/fs/open.c b/fs/open.c index 05036d819197..965b84d486b8 100644 --- a/fs/open.c @@ -128,7 +125,7 @@ index 05036d819197..965b84d486b8 100644 if (mode & ~S_IRWXO) /* where's F_OK, X_OK, W_OK, R_OK? */ return -EINVAL; ``` -```diff +```diff[read_write.c] diff --git a/fs/read_write.c b/fs/read_write.c index 650fc7e0f3a6..55be193913b6 100644 --- a/fs/read_write.c @@ -151,7 +148,7 @@ index 650fc7e0f3a6..55be193913b6 100644 return -EBADF; if (!(file->f_mode & FMODE_CAN_READ)) ``` -```diff +```diff[stat.c] diff --git a/fs/stat.c b/fs/stat.c index 376543199b5a..82adcef03ecc 100644 --- a/fs/stat.c @@ -175,6 +172,8 @@ index 376543199b5a..82adcef03ecc 100644 return -EINVAL; ``` +::: + 主要修改四個項目: 1. do_faccessat,通常位於 `fs/open.c` @@ -236,9 +235,11 @@ index 2ff887661237..e758d7db7663 100644 return -EINVAL; ``` +### 安全模式 {#safe-mode} + 若要啟用 KernelSU 內建的安全模式,您還需要修改 `drivers/input/input.c` 中的 `input_handle_event` 方法: -:::tip +:::tip 小建議 強烈建議啟用此功能,如果遇到開機迴圈,這將會非常有用! ::: @@ -266,9 +267,45 @@ index 45306f9ef247..815091ebfca4 100755 add_input_randomness(type, code, value); ``` +:::info 不小心進入安全模式? +如果您使用手動整合且不停用 `CONFIG_KPROBES`,那麼您將可能會在啟動後透過按下音量來減少按鈕來觸發安全模式!因此,如果使用手動集成,您需要停用 `CONFIG_KPROBES` ! +::: + +### 無法在終端中執行 `pm` ? {#failed-to-execute-pm-in-terminal} + +你應該修改 `fs/devpts/inode.c`,參考: + +```diff +diff --git a/fs/devpts/inode.c b/fs/devpts/inode.c +index 32f6f1c68..d69d8eca2 100644 +--- a/fs/devpts/inode.c ++++ b/fs/devpts/inode.c +@@ -602,6 +602,8 @@ struct dentry *devpts_pty_new(struct pts_fs_info *fsi, int index, void *priv) + return dentry; + } + ++#ifdef CONFIG_KSU ++extern int ksu_handle_devpts(struct inode*); ++#endif ++ + /** + * devpts_get_priv -- get private data for a slave + * @pts_inode: inode of the slave +@@ -610,6 +612,7 @@ struct dentry *devpts_pty_new(struct pts_fs_info *fsi, int index, void *priv) + */ + void *devpts_get_priv(struct dentry *dentry) + { ++ #ifdef CONFIG_KSU ++ ksu_handle_devpts(dentry->d_inode); ++ #endif + if (dentry->d_sb->s_magic != DEVPTS_SUPER_MAGIC) + return NULL; + return dentry->d_fsdata; +``` + ### 如何向後移植 path_umount {#how-to-backport-path_unpount} -你可以透過向後移植 `path_umount` 來讓卸載模組功能在小於 5.10 的非 GKI 核心上運作. 你可以參考這個修改: +你可以透過向後移植 `path_umount` 來讓卸載模組功能在低於 5.10 的非 GKI 核心上運作。你可以參考這個修改: ```diff --- a/fs/namespace.c @@ -315,5 +352,4 @@ index 45306f9ef247..815091ebfca4 100755 * This is important for filesystems which use unnamed block devices. ``` - 最後,再次建置您的核心,KernelSU 將會如期運作。 diff --git a/website/docs/zh_TW/guide/installation.md b/website/docs/zh_TW/guide/installation.md index c0e0f32d3d6e..1d2e7a3d852f 100644 --- a/website/docs/zh_TW/guide/installation.md +++ b/website/docs/zh_TW/guide/installation.md @@ -1,25 +1,25 @@ # 安裝 {#title} -## 檢查您的裝置是否受支援 {#check-if-supported} +## 檢查您的裝置是否受支援 {#check-if-your-device-is-supported} -從 [GitHub Releases](https://github.com/tiann/KernelSU/releases) 下載 KernelSU 管理器應用程式,然後將應用程式安裝至裝置並開啟: +從 [GitHub Releases](https://github.com/tiann/KernelSU/releases) 下載 KernelSU 管理器,然後安裝至裝置並開啟: -- 如果應用程式顯示「不支援」,則表示您的裝置不支援 KernelSU,您需要自行編譯核心才能繼續使用,,KernelSU 官方也永遠不會為您提供一個可以刷新的 Boot 映像。 -- 如果應用程式顯示「未安裝」,那麼 KernelSU 支援您的裝置;可以進行下一步作業。 +- 如果顯示「不支援」,則表示您的裝置不支援 KernelSU,您需要自行編譯核心才能繼續使用,KernelSU 官方也永遠不會提供一個您可以寫入的 Boot 映像。 +- 如果顯示「未安裝」,那麼 KernelSU 支援您的裝置。 -:::info +::: info 提示 對於顯示「不支援」的裝置,這裡有一個[非官方支援裝置清單](unofficially-support-devices.md),您可以使用這個清單裡的核心自行編譯。 ::: -## 備份您的原廠 boot.img {#backup-boot-image} +## 備份您的原廠 boot.img {#backup-stock-boot-img} -在進行刷新作業前,您必須預先備份您的原廠 boot.img。如果您在後續刷新作業中出現了任何問題,您都可以透過使用 Fastboot 刷新回到原廠 Boot 以還原系統。 +在寫入核心映像前,您必須預先備份您的原廠 boot.img。如果您在後續寫入中出現了任何問題,您都可以透過使用 Fastboot 寫回原廠 Boot 以還原系統。 -::: warning -刷新作業可能會造成資料遺失,請確保做好這一步再繼續進行下一步作業!!必要時您還可以備份您手機的所有資料。 +::: warning 警告 +寫入核心映像可能會造成資料遺失,請確保做好這一步再繼續進行下一步作業!!必要時您還可以備份您手機的所有資料。 ::: -## 必要知識 {#acknowage} +## 必要知識 {#necessary-knowledge} ### ADB 和 Fastboot {#adb-and-fastboot} @@ -27,9 +27,9 @@ ### KMI -KMI 全稱 Kernel Module Interface,相同 KMI 的核心版本是**相容的** 這也是 GKI 中「標準」的涵義所在;反之,如果 KMI 不同,那麼這些核心之間無法彼此相容,刷新與您裝置 KMI 不同的核心映像可能會導致開機迴圈。 +KMI 全稱 Kernel Module Interface,相同 KMI 的核心版本是**相容的**,這也是 GKI 中「標準」的涵義所在。反之,如果 KMI 不同,那麼這些核心之間無法彼此相容,寫入與您裝置 KMI 不同的核心映像可能會導致無法開機。 -具體來講,對 GKI 的裝置,其核心版本格式應該如下: +具體來講,對於 GKI 的裝置,其核心版本格式應該如下: ```txt KernelRelease := @@ -37,21 +37,27 @@ Version.PatchLevel.SubLevel-AndroidRelease-KmiGeneration-suffix w .x .y -zzz -k -something ``` -其中,`w.x-zzz-k` 為 KMI 版本。例如,一部裝置核心版本為 `5.10.101-android12-9-g30979850fc20`,那麼它的 KMI 為 `5.10-android12-9`;理論上刷新其他這個 KMI 的核心也能正常開機。 +其中,`w.x-zzz-k` 為 KMI 版本。例如,一部裝置核心版本為 `5.10.101-android12-9-g30979850fc20`,那麼它的 KMI 為 `5.10-android12-9`,理論上寫入其他這個 KMI 的核心也能正常開機。 -::: tip +::: tip 補充 請注意,核心版本中的 SubLevel 並非 KMI 的一部分!也就是說 `5.10.101-android12-9-g30979850fc20` 與 `5.10.137-android12-9-g30979850fc20` 的 KMI 相同! ::: +### 安全性修補程式等級 {#security-patch-level} + +較新的 Android 裝置可能具有防回滾機制,不允許寫入具有較舊安全性修補程式等級的啟動映像。例如,如果您的裝置核心為 `5.10.101-android12-9-g30979850fc20`,則其安全修補程式等級為 `2023-11`;即使寫入了 KMI 對應的核心,如果安全修補程式等級早於 `2023-11`(例如 `2023-06`),也可能會導致無法開機。 + +因此,最好使用具有最新安全性修補程式等級的核心來維護與 KMI 的對應關係。 + ### 核心版本與 Android 版本 {#kernel-version-vs-android-version} 請注意:**核心版本與 Android 版本並不一定相同!** -如果您發現您的核心版本是 `android12-5.10.101`,然而您 Android 系統的版本為 Android 13 或者其他;請不要覺得奇怪,因為 Android 系統的版本與 Linux 核心的版本號碼並非一致;Linux 核心的版本號碼一般與**裝置出廠時隨附的 Android 系統的版本一致**,如果後續 Android 系統更新,核心版本一般不會發生變化。如果您需要刷新,**請以核心版本為準!!** +如果您發現您的核心版本是 `android12-5.10.101`,然而您 Android 系統的版本為 Android 13 或更高,請不要覺得奇怪,因為 Android 系統的版本與 Linux 核心的版本號碼並非一致。Linux 核心的版本號碼一般與**裝置出廠時隨附的 Android 系統的版本一致**,如果後續 Android 系統更新,核心版本一般不會發生變化。如果您需要寫入,**請以核心版本為準!!** ## 安裝簡介 {#introduction} -自 `0.9.0` 版本以後,在 GKI 裝置中,KernelSU 支援兩種運行模式: +自 `0.9.0` 版本以後,在 GKI 裝置上,KernelSU 支援兩種運作模式: 1. `GKI`:使用**通用核心鏡像**(GKI)取代掉裝置原有的核心。 2. `LKM`:使用**可載入核心模組**(LKM)的方式載入到裝置核心中,不會替換掉裝置原有的核心。 @@ -60,55 +66,56 @@ w .x .y -zzz -k -something ### GKI 模式 {#gki-mode} -GKI 模式會替換掉裝置原有的內核,使用 KernelSU 提供的通用內核鏡像。 GKI 模式的優點是: +GKI 模式會替換掉裝置原有的核心,使用 KernelSU 提供的通用核心鏡像。 GKI 模式的優點是: -1. 通用型強,適用於大多數裝置;例如三星開啟了 KNOX 的裝置,LKM 模式無法運作。還有一些冷門的魔改裝置,也只能使用 GKI 模式; -2. 不依賴官方韌體即可使用;不需要等待官方韌體更新,只要 KMI 一致,就可以使用; +1. 通用型高,適用於大多數裝置;例如開啟了 KNOX 的三星裝置、或是 LKM 模式無法運作的裝置。還有一些冷門的魔改裝置,也只能使用 GKI 模式。 +2. 不依賴官方韌體即可使用;不需要等待官方韌體更新,只要 KMI 一致,就可以使用。 ### LKM 模式 {#lkm-mode} -LKM 模式不會替換掉裝置原有的內核,而是使用可載入內核模組的方式載入到裝置內核中。 LKM 模式的優點是: +LKM 模式不會替換掉裝置原有的核心,而是使用可載入核心模組的方式載入到裝置核心中。 LKM 模式的優點是: -1. 不會取代裝置原有的核心;如果你對裝置原有的核心有特殊需求,或是你希望在使用第三方核心的同時使用 KernelSU,可以使用 LKM 模式; -2. 升級和 OTA 較為方便;升級 KernelSU 時,可以直接在管理器內部安裝,無需再手動刷寫;系統 OTA 後,可以直接安裝到第二個槽位,也無需再手動刷寫; -3. 適用於一些特殊場景;例如使用臨時 ROOT 權限也可以載入 LKM,由於不需要替換 boot 分區,因此不會觸發 avb,不會使裝置意外變磚; -4. LKM 可以被暫時卸載;如果你暫時想取消 root,可以卸載 LKM,這個過程不需要刷寫分區,甚至也不用重啟裝置;如果你想再次 root,只需要重啟裝置即可; +1. 不會取代裝置原有的核心:如果你對裝置原有的核心有特殊需求,或是你希望在使用第三方核心的同時使用 KernelSU,可以使用 LKM 模式。 +2. 升級和 OTA 較為方便:升級 KernelSU 時,可以直接在管理器內部安裝,無需再手動寫入;系統 OTA 後,可以直接安裝到第二個槽位,也無需再手動寫入。 +3. 適用於一些特殊場景:例如使用臨時 root 權限也可以載入 LKM,由於不需要替換 boot 分區,因此不會觸發 avb,不會使裝置意外變磚。 +4. LKM 可以被暫時卸載:如果你暫時想取消 root,可以卸載 LKM,這個過程不需要寫入分區,甚至也不用重啟裝置。如果你想重新取得 root,只需要重啟裝置即可。 :::tip 兩種模式共存 -打開管理器後,你可以在首頁看到裝置目前運行的模式;注意 GKI 模式的優先級高於 LKM ,如你你既使用 GKI 內核替換掉了原有的內核,又使用 LKM 的方式修補了 GKI 內核,那麼 LKM 會被忽略,裝置將永遠以 GKI 的模式運作。 +打開管理器後,你可以在首頁看到裝置目前運行的模式。注意 GKI 模式的優先級高於 LKM ,如你既使用 GKI 核心替換掉了原有的核心,又使用 LKM 的方式修補了 GKI 核心,那麼 LKM 會被忽略,裝置將永遠以 GKI 的模式運作。 ::: ### 選哪個? {#which-one} -如果你的裝置是手機,我們建議您優先考慮 LKM 模式;如果你的裝置是模擬器、WSA 或 Waydroid 等,我們建議您優先考慮 GKI 模式。 +如果你的裝置是手機,我們建議您優先考慮 LKM 模式。 +如果你的裝置是模擬器、WSA 或 Waydroid 等,我們建議您優先考慮 GKI 模式。 ## LKM 安裝 {#lkm-installation} ### 取得官方韌體 {#get-the-official-firmware} -使用 LKM 的模式,需要取得官方韌體,然後在官方韌體的基礎上修補;如果你使用的是第三方內核,可以把第三方內核的 boot.img 作為官方韌體。 +使用 LKM 的模式,需要取得官方韌體,然後在官方韌體的基礎上修補;如果你使用的是第三方核心,可以把第三方核心的 boot.img 作為官方韌體。 -取得官方韌體的方法有很多,如果你的裝置支援 `fastboot boot`,那麼我們最推薦以及最簡單的方法是使用 `fastboot boot` 臨時啟動 KernelSU 提供的 GKI 內核,然後安裝管理器,最後在管理器中直接安裝;這種方法不需要你手動下載官方韌體,也不需要你手動提取 boot。 +取得官方韌體的方法有很多,如果你的裝置支援 `fastboot boot`,那麼我們最推薦以及最簡單的方法是使用 `fastboot boot` 臨時啟動 KernelSU 提供的 GKI 核心,並參考[使用管理器](#use-the-manager)安裝。 如果你的裝置不支援 `fastboot boot`,那麼你可能需要手動去下載官方韌體包,然後從中提取 boot。 -與 GKI 模式不同,LKM 模式會修改 `ramdisk`,因此在出廠 Android 13 的裝置上,它需要修補的是 `init_boot` 分區而非 `boot` 分區;而 GKI 模式則永遠是操作 `boot` 分區。 +與 GKI 模式不同,LKM 模式會修改 `ramdisk`,因此在出廠 Android 13 的裝置上,通常它需要修補的是 `init_boot` 分區而非 `boot` 分區;而 GKI 模式則永遠是修改 `boot` 分區。 ### 使用管理器 {#use-the-manager} 開啟管理器,點選右上角的安裝圖標,會出現若干個選項: -1. 選擇並修補一個文件:如果你手機目前沒有 root 權限,你可以選擇這個選項,然後選擇你的官方韌體,管理器會自動修補它;你只需要刷入這個修補後的文件,即可永久取得 root 權限; -2. 直接安裝:如果你手機已經 root,你可以選擇這個選項,管理器會自動獲取你的裝置資訊,然後自動修補官方韌體,然後刷入;你可以考慮使用`fastboot boot` KernelSU 的 GKI 內核來取得臨時 root 安裝管理器,然後再使用這個選項;這種方式也是 KernelSU 升級最主要的方式; -3. 安裝到另一個分割區:如果你的裝置支援 A/B 分割區,你可以選擇這個選項,管理器會自動修補官方韌體,然後安裝到另一個分割區;這種方式適用於 OTA 後的裝置,你可以在 OTA 後直接安裝到另一個分割區,然後重新啟動裝置即可; +1. 選擇並修補一個文件:如果你手機目前沒有 root 權限,你可以選擇這個選項,然後選擇你的官方韌體,管理器會自動修補它。你只需要寫入這個修補後的文件,即可永久取得 root 權限。 +2. 直接安裝:如果你手機已經 root,你可以選擇這個選項,管理器會自動獲取你的裝置資訊,然後自動修補官方韌體,然後寫入。你可以考慮使用 `fastboot boot` KernelSU 的 GKI 核心來取得臨時 root 安裝管理器,然後再使用這個選項。**這種方式也是 KernelSU 升級最主要的方式**。 +3. 安裝到另一個分割區:如果你的裝置支援 A/B 分區,你可以選擇這個選項,管理器會自動修補官方韌體,然後安裝到另一個分區。這種方式適用於 OTA 後的裝置,你可以在 OTA 後直接安裝到另一個分割區,然後重新啟動裝置即可。 -### 使用命令列 +### 使用命令列{#use-the-command-line} -如果你不想使用管理器,你也可以使用命令列來安裝 LKM;KernelSU 提供的 `ksud` 工具可以幫助你快速修補官方韌體,然後刷入。 +如果你不想使用管理器,你也可以使用命令列來安裝 LKM。KernelSU 提供的 `ksud` 可以幫助你快速修補官方韌體,然後寫入。 這個工具支援 macOS、Linux 和 Windows,你可以在 [GitHub Release](https://github.com/tiann/KernelSU/releases) 下載對應的版本。 -使用方法:`ksud boot-patch` 具體的使用方法你可以查看命令列幫助。 +使用方法:`ksud boot-patch`。 你可以查看命令列的提示了解具體的使用方法。 ```sh husky:/ # ksud boot-patch -h @@ -129,126 +136,130 @@ Options: -h, --help Print help ``` 需要說明的幾個選項: -1. `--magiskboot` 選項可以指定 magiskboot 的路徑,如果不指定,ksud 會在環境變數中尋找;如果你不知道如何取得 magiskboot,可以參考[這裡](#patch-boot-image); -2. `--kmi` 選項可以指定 `KMI` 版本,如果你的裝置核心名字沒有遵循 KMI 規範,你可以透過這個選項來指定; +1. `--magiskboot` 選項可以指定 magiskboot 的路徑,如果不指定,ksud 會在環境變數中尋找。如果你不知道如何取得 magiskboot,可以參考[這裡](#patch-boot-image)。 +2. `--kmi` 選項可以指定 `KMI` 版本,如果你的裝置核心名字沒有遵循 KMI 規範,你可以透過這個選項來指定。 + 最常見的使用方法為: ```sh ksud boot-patch -b --kmi android13-5.10 ``` -## GKI 安裝 +## GKI 安裝{#gki-mode-installation} GKI 的安裝方式有以下幾種,各自適用於不同的場景,請依需求選擇: -1. 使用自訂 Recovery (如 TWRP) 安裝 -2. 使用核心刷新應用程式 (例如 Franco Kernel Manager) 安裝 -3. 使用 KernelSU 提供的 boot.img 透過 Fastboot 安裝 +1. 使用 KernelSU 提供的 boot.img 透過 Fastboot 安裝 +2. 使用核心寫入程式 (例如 KernelFlasher) 安裝 +3. 使用自訂 Recovery (例如 TWRP) 安裝 4. 手動修補 boot.img 並安裝 -## 使用自訂 Recovery 安裝 {#install-by-recovery} - -先決條件:您的裝置必須有自訂的 Recovery,例如 TWRP;如果沒有或者只有官方 Recovery,請使用其他方法。 - -步驟: - -1. 在 KernelSU 的 [Release 頁面](https://github.com/tiann/KernelSU/releases) 下載與您手機版本相符的以 AnyKernel3 開頭的 Zip 套件;例如,手機核心版本為 `android12-5.10.66`,那麼您應該下載 `AnyKernel3-android12-5.10.66_yyyy-MM.zip` 這個檔案 (其中 `yyyy` 為年份,`MM` 為月份)。 -2. 重新開機手機至 TWRP。 -3. 使用 Adb 將 AnyKernel3-*.zip 放置到手機 /sdcard 然後在 TWRP 圖形使用者介面選擇並安裝;或者您也可以直接 `adb sideload AnyKernel-*.zip` 安裝。 - -PS. 這種方法適用於任何狀況下的安裝 (不限於初次安裝或後續更新),只要您用 TWRP 就可以進行作業。 - -## 使用核心刷新應用程式安裝 {#install-by-kernel-flasher} - -先決條件:您的裝置必須已經 Root。例如您已經安裝了 Magisk 並取得 Root 存取權,或者您已經安裝了舊版本的 KernelSU 需升級到其他版本的 KernelSU;如果您的裝置並未 Root,請嘗試其他方法。 - -步驟: - -1. 下載 AnyKernel3 的 Zip 檔案;請參閱 *使用自訂 Recovery 安裝* 章節的内容。 -2. 開啟核心刷新應用程式提供的 AnyKernel3 Zip 檔案進行刷新。 - -如果您先前並未使用過核心刷新應用程式,可以嘗試下面幾個方法: - -1. [Kernel Flasher](https://github.com/capntrips/KernelFlasher/releases) -2. [Franco Kernel Manager](https://play.google.com/store/apps/details?id=com.franco.kernel) -3. [Ex Kernel Manager](https://play.google.com/store/apps/details?id=flar2.exkernelmanager) - -PS. 這種方法在更新 KernelSU 時比較方便,無需電腦即可完成 (注意備份!)。 - ## 使用 KernelSU 提供的 boot.img 安裝 {#install-with-boot-img-provided-by-kernelsu} -這種方法無需您有 TWRP,也無需您的手機有 Root 權限;適用於您初次安裝 KernelSU。 +如果你的裝置的 `boot.img` 使用常見的壓縮格式,你可以直接寫入 KernelSU 提供的 GKI 核心映像,這種方法無需 TWRP,也無需您的手機有 Root 權限;適用於您初次安裝 KernelSU。 ### 找到合適的 boot.img {#find-proper-boot-img} -KernelSU 為 GKI 裝置提供了標準 boot.img,您需要將 boot.img 刷新至裝置的 Boot 分割區。 +KernelSU 為 GKI 裝置提供了標準 boot.img,您需要將 boot.img 寫入至裝置的 Boot 分區。 -您可以從 [GitHub Release](https://github.com/tiann/KernelSU/releases) 下載 boot.img,請注意,您應該使用正確版本的 boot.img。例如,如果您的裝置顯示核心是 `android12-5.10.101`,需要下載 `android-5.10.101_yyyy-MM.boot-.img`. +您可以從 [GitHub Release](https://github.com/tiann/KernelSU/releases) 下載 boot.img,請注意,您應該使用正確版本的 boot.img。如果你不知道你該下載哪個檔案,請詳細閱讀文檔中的 [KMI](#kmi) 與[安全性修補程式等級](#security-patch-level)。 -其中 `` 指的是您的官方 boot.img 的核心壓縮格式,請檢查您原有 boot.img 的核心壓縮格式,您應該使用正確的格式,例如 `lz4`、`gz`;如果使用不正確的壓縮格式,刷新 Boot 後可能無法開機。 +通常,在相同的 KMI 和安全性修補程式等級下,會存在三種不同格式的啟動檔案。除了核心壓縮格式之外,它們都是相同的。請檢查您原來的 boot.img 的核心壓縮格式。您應該使用正確的格式,例如 `lz4` 、 `gz`,如果你使用了不正確的壓縮格式,你可能會在寫入後無法開機。 -::: info -1. 您可以透過 magiskboot 以取得您的原始 Boot 的壓縮格式;當然,您也可以詢問與您相同型號的其他更有經驗的使用者。另外,核心的壓縮格式通常部會出現變更,如果您使用的某個壓縮格式成功開機,後續可以優先嘗試這個格式。 +::: info 關於 boot.img 的壓縮格式 +1. 您可以透過 magiskboot 以取得您的原始 Boot 的壓縮格式。當然,您也可以詢問與您相同型號的其他更有經驗的使用者。另外,核心的壓縮格式通常不會出現變更,如果您使用的某個壓縮格式成功開機,後續可以優先嘗試這個格式。 2. 小米裝置通常 `gz` 或者 **不壓縮**。 3. Pixel 裝置有些特殊,請遵循下方的指示。 ::: -### 將 boot.img 刷新至裝置 {#flash-boot-img-to-device} +### 將 boot.img 寫入至裝置 {#flash-boot-img-to-device} -使用 `adb` 連接您的裝置,然後執行 `adb reboot bootloader` 進入 fastboot 模式,然後使用此命令刷新 KernelSU: +使用 `adb` 連接您的裝置,然後執行 `adb reboot bootloader` 進入 fastboot 模式,然後使用此命令寫入 KernelSU: ```sh fastboot flash boot boot.img ``` -::: info -如果您的裝置支援 `fastboot boot`,可以先使用 `fastboot boot boot.img` 來先嘗試使用 boot.img 開機進入系統,如果出現意外,重新啟動即可開機。 +::: info 提示 +如果您的裝置支援 `fastboot boot`,可以先使用 `fastboot boot boot.img` 來嘗試使用 boot.img 開機進入系統,如果出現意外,重新啟動即可開機。 ::: ### 重新開機 {#reboot} -刷新完成後,您應該重新啟動您的裝置: +寫入完成後,您應該重新啟動您的裝置: ```sh fastboot reboot ``` -## 手動修補 boot.img {#patch-boot-image} +## 使用核心寫入程式安裝 {#install-with-kernel-flasher} + +先決條件:您的裝置必須已經 Root。例如您已經安裝了 Magisk 並取得 Root 存取權,或者您已經安裝了舊版本的 KernelSU 需升級到其他版本的 KernelSU;如果您的裝置並未 Root,請嘗試其他方法。 + +步驟: + +1. 下載 AnyKernel3 的 Zip 檔。如果你不知道你該下載哪個檔案,請詳細閱讀文檔中的 [KMI](#kmi) 與[安全性修補程式等級](#security-patch-level)。 +2. 開啟核心寫入程式提供的 AnyKernel3 Zip 檔案並寫入核心。 -對於某些裝置來說,其 boot.img 格式並不是很常見,比如 `lz4`,`gz` 和未壓縮;最典型的就是 Pixel,它的 boot.img 格式是 `lz4_legacy` 壓縮,ramdisk 可能是 `gz` 也可能是 `lz4_legacy` 壓縮;此時如果您直接刷新 KernelSU 提供的 boot.img,手機可能無法開機;這時,您可以透過手動修補 boot.img 來完成。 +如果您先前並未使用過核心寫入應用程式,可以嘗試下面幾個: -一般有兩種修補方法: +1. [Kernel Flasher](https://github.com/capntrips/KernelFlasher/releases) +2. [Franco Kernel Manager](https://play.google.com/store/apps/details?id=com.franco.kernel) +3. [Ex Kernel Manager](https://play.google.com/store/apps/details?id=flar2.exkernelmanager) + +P.S. 這種方法在更新 KernelSU 時比較方便,無需電腦即可完成 (注意備份!)。 + +## 手動修補 boot.img {#patch-boot-image} -1. [Android-Image-Kitchen](https://forum.xda-developers.com/t/tool-android-image-kitchen-unpack-repack-kernel-ramdisk-win-android-linux-mac.2073775/) -2. [magiskboot](https://github.com/topjohnwu/Magisk/releases) +對於某些裝置來說,其 boot.img 格式並不是很常見,不屬於 `lz4`,`gz` 和未壓縮;最典型的就是 Pixel,它的 boot.img 格式是 `lz4_legacy` 壓縮,ramdisk 可能是 `gz` 也可能是 `lz4_legacy` 壓縮;此時如果您直接寫入 KernelSU 提供的 boot.img,手機可能無法開機。這時,您可以透過手動修補 boot.img 來完成。 -其中,Android-Image-Kitchen 適用於在電腦上作業,magiskboot 需要手機協作。 +永遠建議使用 `magiskboot` 來修補映像,一般有兩種修補方法: -### 準備 {#patch-preparation} +1. [magiskboot](https://github.com/topjohnwu/Magisk/releases) +2. [magiskboot_build](https://github.com/ookiineko/magiskboot_build/releases/tag/last-ci) -1. 取得您手機的原廠 boot.img;您可以聯絡您的裝置製造商,您也可能需要[payload-dumper-go](https://github.com/ssut/payload-dumper-go) -2. 下載 KernelSU 提供的與您的裝置 KMI 一致地 AnyKernel3 Zip 檔案 (可參閱 *使用自訂 Recovery 安裝*)。 -3. 解壓縮 AnyKernel3 Zip 檔案,取得其中的 `Image` 檔案,此檔案為 KernelSU 的核心檔案。 +其中,官方的 `magiskboot` 僅能在 Android 上使用,若您想在電腦上完成,可以嘗試第二個選項。 -### 使用 Android-Image-Kitchen {#using-android-image-kitchen} +### 準備 {#preparation} -1. 下載 Android-Image-Kitchen 至您的電腦。 -2. 將手機原廠 boot.img 放置於 Android-Image-Kitchen 根目錄。 -3. 在 Android-Image-Kitchen 根目錄執行 `./unpackimg.sh boot.img`;此命令會將 boot.img 解除封裝,您會得到一些檔案。 -4. 將 `split_img` 目錄中的 `boot.img-kernel` 取代為您從 AnyKernel3 解壓縮出來的 `Image` (注意名稱變更為 boot.img-kernel)。 -5. 在 Android-Image-Kitchecn 根目錄執行 `./repackimg.sh`;此時您會得到一個 `image-new.img` 檔案;使用此 boot.img 透過 fastboot 刷新即可 (刷新方法請參閱上一章節)。 +1. 取得您手機的原廠 boot.img,您可以從您的裝置製造商取得,您也可能需要 [payload-dumper-go](https://github.com/ssut/payload-dumper-go)。 +2. 下載 KernelSU 提供的與您的裝置 KMI 一致的 AnyKernel3 Zip 檔 (可參閱[使用自訂 Recovery 安裝](#install-with-custom-recovery))。 +3. 解壓縮 AnyKernel3 Zip 檔,取得其中的 `Image` 檔,此檔案為具有 KernelSU 的核心。 -### 使用 magiskboot {#using magiskboot} +### 在 Android 上使用 magiskboot {#using-magiskboot-on-Android-devices} -1. 在 Magisk 的 [Release 頁面](https://github.com/topjohnwu/Magisk/releases) 下載最新的 Magisk 安裝套件。 -2. 將 `Magisk-*(version).apk` 重新命名為 `Magisk-*.zip` 然後解壓縮。 -3. 將解壓縮後的 `Magisk-*/lib/arm64-v8a/libmagiskboot.so` 檔案,使用 Adb 推入至手機:`adb push Magisk-*/lib/arm64-v8a/libmagiskboot.so /data/local/tmp/magiskboot` +1. 在 Magisk 的 [Release 頁面](https://github.com/topjohnwu/Magisk/releases) 下載最新的 Magisk。 +2. 將 `Magisk-*(version).apk` 重新命名為 `Magisk-*.zip` 並解壓縮。 +3. 使用 Adb 將 magiskboot 推入至手機:`adb push Magisk-*/lib/arm64-v8a/libmagiskboot.so /data/local/tmp/magiskboot`。 4. 使用 Adb 將原廠 boot.img 和 AnyKernel3 中的 Image 推入至手機。 -5. adb shell 進入 /data/local/tmp/ 目錄,然後賦予先前推入檔案的可執行權限 `chmod +x magiskboot` +5. adb shell 進入 /data/local/tmp/ 目錄,然後賦予先前推入的檔案可執行權限 `chmod +x magiskboot`。 6. adb shell 進入 /data/local/tmp/ 目錄,執行 `./magiskboot unpack boot.img` 此時會將 `boot.img` 解除封裝,得到一個名為 `kernel` 的檔案,這個檔案是您的原廠核心。 7. 使用 `Image` 取代 `kernel`: `mv -f Image kernel` -8. 執行 `./magiskboot repack boot.img` 重新封裝 img,此時您會得到一個 `new-boot.img` 檔案,透過 Fastboot 將這個檔案刷新至裝置即可。 +8. 執行 `./magiskboot repack boot.img` 重新封裝映像,此時您會得到一個 `new-boot.img` 檔案,透過 Fastboot 將這個檔案寫入至裝置即可。 + +### 在 Windows/macOS/Linux PC 上使用 magiskboot {#using-magiskboot-on-PC} + +1. 在 [magiskboot_build](https://github.com/ookiineko/magiskboot_build/releases/tag/last-ci) 下載對應的 magiskboot。 +2. (僅linux)賦予檔案可執行權限 `chmod +x magiskboot`。 +3. 執行 `./magiskboot unpack boot.img` 此時會將 `boot.img` 解除封裝,得到一個名為 `kernel` 的檔案,這個檔案是您的原廠核心。 +4. 使用 `Image` 取代 `kernel`: `mv -f Image kernel` +5. 執行 `./magiskboot repack boot.img` 重新封裝映像,此時您會得到一個 `new-boot.img` 檔案,透過 Fastboot 將這個檔案寫入至裝置即可。 + +## 使用自訂 Recovery 安裝 {#install-with-custom-recovery} + +先決條件:您的裝置必須有自訂的 Recovery,例如 TWRP。如果沒有或者只有官方 Recovery,請使用其他方法。 + +步驟: + +1. 在 KernelSU 的 [Release 頁面](https://github.com/tiann/KernelSU/releases) 下載與您手機版本相符的以 AnyKernel3 開頭的 Zip 檔;例如,手機核心版本為 `android12-5.10.66`,那麼您應該下載 `AnyKernel3-android12-5.10.66_yyyy-MM.zip` 這個檔案 (其中 `yyyy` 為年份,`MM` 為月份)。 +2. 重新開機手機至 TWRP。 +3. 使用 Adb 將 AnyKernel3-*.zip 放置到手機 `/sdcard` 然後在 TWRP 圖形使用者介面選擇並安裝;或者您也可以直接 `adb sideload AnyKernel-*.zip` 安裝。 + +PS. 這種方法適用於任何狀況下的安裝 (不限於初次安裝或後續更新),只要您用 TWRP 就可以進行作業。 ## GKI的其他替代方法 {#other-methods} -其實所有這些安裝方法的主旨只有一個,那就是**將原廠核心取代為 KernelSU 提供的核心**;只要能實現這個目的,就可以安裝;比如以下是其他可行的方法: +其實所有這些安裝方法的主旨只有一個,那就是**將原廠核心取代為 KernelSU 提供的核心**。只要能實現這個目的,就可以安裝,比如以下是其他可行的方法: + +1. 首先安裝 Magisk,透過 Magisk 取得 Root 權限後使用核心寫入程式寫入 KernelSU 的 AnyKernel Zip。 +2. 使用某些 PC 上的寫入工具組寫入 KernelSU 提供的核心。 + +但是,如果不起作用,請嘗試 Magiskboot 方法。 -1. 首先安裝 Magisk,透過 Magisk 取得 Root 權限後使用核心刷新程式刷新 KernelSU 的 AnyKernel Zip。 -2. 使用某些 PC 上的刷新工具組刷新 KernelSU 提供的核心。 diff --git a/website/docs/zh_TW/guide/module-webui.md b/website/docs/zh_TW/guide/module-webui.md index 5dbdaf7826f2..671faad7a153 100644 --- a/website/docs/zh_TW/guide/module-webui.md +++ b/website/docs/zh_TW/guide/module-webui.md @@ -2,11 +2,11 @@ KernelSU 的模組除了執行啟動腳本和修改系統檔案之外,還支援顯示 UI 介面和與使用者互動。 -該模組可以透過任何 Web 技術編寫HTML + CSS + JavaScript頁面。 KernelSU的管理器將透過 WebView 顯示這些頁面。它還提供了一些用於與系統互動的JavaScript API,例如執行shell命令。 +該模組可以透過任何 Web 技術編寫 HTML + CSS + JavaScript 頁面。 KernelSU的管理器將透過 WebView 顯示這些頁面。它還提供了一些用於與系統互動的 JavaScript API,例如執行 shell 命令。 ## WebUI 根目錄 {#webroot-directory} -Web資源應放置在模組根目錄的`webroot`子目錄中,並且其中**必須**有一個名為`index.html`的文件,該檔案是模組頁面入口。 +Web資源應放置在模組根目錄的 `webroot` 子目錄中,並且其中**必須**有一個名為 `index.html` 的文件,該檔案是模組頁面入口。 包含Web介面的最簡單的模組結構如下: @@ -18,7 +18,7 @@ Web資源應放置在模組根目錄的`webroot`子目錄中,並且其中**必 `-- index.html ``` -:::warning +:::warning 提醒 安裝模組時,KernelSU 將自動設定`webroot`的權限和 SELinux context。如果您不知道自己在做什麼,請不要自行設定該目錄的權限! ::: @@ -28,7 +28,7 @@ Web資源應放置在模組根目錄的`webroot`子目錄中,並且其中**必 如果只是一個顯示頁面,那和一般網頁沒有什麼不同。更重要的是,KernelSU 提供了一系列的系統 API,讓您可以實現模組獨特的功能。 -KernelSU 提供了一個 JavaScript 庫並[在 npm 上發布](https://www.npmjs.com/package/kernelsu),您可以在網頁的 JavaScript 程式碼中使用它。 +KernelSU [在 npm 上發布](https://www.npmjs.com/package/kernelsu)了一個 JavaScript 庫,您可以在網頁的 JavaScript 程式碼中使用它。 例如,您可以執行 shell 命令來取得特定配置或修改屬性: @@ -42,8 +42,9 @@ const { errno, stdout } = exec("getprop ro.product.model"); [API 文檔](https://www.npmjs.com/package/kernelsu) -如果您發現現有的API無法滿足您的需求或使用不方便,歡迎您在[這裡](https://github.com/tiann/KernelSU/issues)給我們建議! -## 一些技巧 +如果您發現現有的 API 無法滿足您的需求或使用不方便,歡迎您在[這裡](https://github.com/tiann/KernelSU/issues)給我們建議! -1. 您可以正常使用`localStorage`來儲存一些數據,但卸載管理器後,這些數據將會遺失。如果需要持久保存,可以自行將資料寫入某個目錄。 -2. 對於簡單的頁面,我建議您使用[parceljs](https://parceljs.org/)進行打包。它無須設定,使用非常方便。不過,如果你是前端高手或有自己的喜好,那就選擇你喜歡的吧! +## 一些技巧 {#some-tips} + +1. 您可以正常使用 `localStorage` 來儲存一些數據,但解除安裝管理器後,這些數據將會遺失。如果需要持久保存,可以自行將資料寫入某個目錄。 +2. 對於簡單的頁面,我建議您使用 [parceljs](https://parceljs.org/) 進行打包。它無須設定,使用非常方便。不過,如果你是前端高手或有自己的喜好,那就選擇你喜歡的吧! diff --git a/website/docs/zh_TW/guide/module.md b/website/docs/zh_TW/guide/module.md index 717c862890ec..dac0fec6c3ee 100644 --- a/website/docs/zh_TW/guide/module.md +++ b/website/docs/zh_TW/guide/module.md @@ -1,8 +1,8 @@ -# 模組指南 {#introduction} +# 模組指南 {#module-guide} -KernelSU 提供了一個模組機制,它可以在保持系統分割區完整性的同時達到修改系統分割區的效果;這種機制一般被稱為 systemless。 +KernelSU 提供了一個模組機制,它可以在保持系統分割區完整性的同時達到修改系統分割區的效果;這種機制一般被稱為 systemless (無系統修改)。 -KernelSU 的模組運作機制與 Magisk 幾乎相同,如果您熟悉 Magisk 模組的開發,那麼開發 KernelSU 的模組大同小異,您可以跳過下列有關模組的介紹,只需要瞭解 [KernelSU 模組與 Magisk 模組的異同](difference-with-magisk.md)。 +KernelSU 的模組運作機制與 Magisk 幾乎相同,如果您熟悉 Magisk 模組的開發,那麼開發 KernelSU 的模組大同小異,您可以跳過下列有關模組的介紹,只需要瞭解 [KernelSU 模組與 Magisk 模組的差異](difference-with-magisk.md)。 ## WebUI @@ -11,20 +11,20 @@ KernelSU 的模組支援顯示互動介面,請參閱 [WebUI 文檔](module-web ## Busybox KernelSU 提供了一個完備的 BusyBox 二進位檔案 (包括完整的 SELinux 支援)。可執行檔位於 `/data/adb/ksu/bin/busybox`。 -KernelSU 的 BusyBox 支援同時執行時可切換的 "ASH Standalone Shell Mode"。 -這種讀了模式意味著在執行 BusyBox 的 ash shell 時,每個命令都會直接使用 BusyBox 中內建的應用程式,而不論 PATH 的設定為何。 -例如,`ls`、`rm`、`chmod` 等命令將不會使用 PATH 中設定的命令 (在 Android 的狀況下,預設狀況下分別為 `/system/bin/ls`、`/system/bin/rm` 和 `/system/bin/chmod`),而是直接呼叫 BusyBox 內建的應用程式。 +KernelSU 的 BusyBox 支援執行時可切換的 "ASH 獨立模式"。 +ASH 獨立模式在執行 BusyBox 時,每個命令都會直接使用 BusyBox 中內建的應用程式,而不論 `PATH` 的設定為何。 +例如,`ls`、`rm`、`chmod` 等命令將不會使用 `PATH` 中設定的命令 (在 Android 的狀況下,預設狀況下分別為 `/system/bin/ls`、`/system/bin/rm` 和 `/system/bin/chmod`),而是直接呼叫 BusyBox 內建的應用程式。 這確保了腳本始終在可預測的環境中執行,並始終具有完整的命令套件,不論它執行在哪個 Android 版本上。 要強制下一個命令不使用 BusyBox,您必須使用完整路徑呼叫可執行檔。 -在 KernelSU 上下文中執行的每個 shell 腳本都將在 BusyBox 的 ash shell 中以獨立模式執行。對於第三方開發人員相關的內容,包括所有開機腳本和模組安裝腳本。 +每個基於 KernelSU 上下文的腳本都將在 BusyBox 的獨立模式執行。對於第三方開發人員而言,這包括所有開機腳本和模組安裝腳本。 對於想要在 KernelSU 之外使用這個「獨立模式」功能的使用者,有兩種啟用方法: 1. 將環境變數 `ASH_STANDALONE` 設為 `1`。例如:`ASH_STANDALONE=1 /data/adb/ksu/bin/busybox sh