diff --git a/.github/ISSUE_TEMPLATE/add_device.yml b/.github/ISSUE_TEMPLATE/add_device.yml deleted file mode 100644 index 87953bc4..00000000 --- a/.github/ISSUE_TEMPLATE/add_device.yml +++ /dev/null @@ -1,33 +0,0 @@ -name: Contribute to Unofficially Supported Devices -description: Add your device kernel source to KernelSU Next's Unofficially Supported Device List -title: "[Add Device]: " -labels: ["add-device"] -body: - - type: markdown - attributes: - value: | - Thanks for supporting KernelSU Next! - - type: input - id: repo-url - attributes: - label: Repository URL - description: Your repository URL - placeholder: https://github.com/rifsxd/KernelSU-Next - validations: - required: true - - type: input - id: device - attributes: - label: Device - description: Please describe the device maintained by you. - placeholder: GKI 2.0 Device - validations: - required: true - - type: checkboxes - id: terms - attributes: - label: Code of Conduct - description: By submitting this issue, you should be the maintainer of the repository. - options: - - label: I'm the maintainer of this repository - required: true diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml deleted file mode 100644 index 18bc7745..00000000 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ /dev/null @@ -1,73 +0,0 @@ -name: Bug report -description: Create a report to help us improve KernelSU Next -labels: [Bug] - -body: - - type: checkboxes - attributes: - label: Please check before submitting an issue - options: - - label: I have searched the issues and haven't found anything relevant - required: true - - - label: I will upload bugreport file in KernelSU Manager - Settings - Report log - required: true - - - label: I know how to reproduce the issue which may not be specific to my device - required: false - - - - type: textarea - attributes: - label: Describe the bug - description: A clear and concise description of what the bug is - validations: - required: true - - - - type: textarea - attributes: - label: To Reproduce - description: Steps to reproduce the behaviour - placeholder: | - - 1. Go to '...' - - 2. Click on '....' - - 3. Scroll down to '....' - - 4. See error - - - - type: textarea - attributes: - label: Expected behavior - description: A clear and concise description of what you expected to happen. - - - - type: textarea - attributes: - label: Screenshots - description: If applicable, add screenshots to help explain your problem. - - - - type: textarea - attributes: - label: Logs - description: If applicable, add crash or any other logs to help us figure out the problem. - - - - type: textarea - attributes: - label: Device info - value: | - - Device: - - OS Version: - - Kernel Version: - - KSUN Driver Version: - - KSUN Manager Version: - validations: - required: true - - - - type: textarea - attributes: - label: Additional context - description: Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml deleted file mode 100644 index ec4bb386..00000000 --- a/.github/ISSUE_TEMPLATE/config.yml +++ /dev/null @@ -1 +0,0 @@ -blank_issues_enabled: false \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/custom.yml b/.github/ISSUE_TEMPLATE/custom.yml deleted file mode 100644 index 5b17fe90..00000000 --- a/.github/ISSUE_TEMPLATE/custom.yml +++ /dev/null @@ -1,11 +0,0 @@ -name: Custom issue template -description: WARNING! If you are reporting a bug but use this template, the issue will be closed directly. -title: '[Custom]' -body: - - type: textarea - id: description - attributes: - label: "Describe your problem." - validations: - required: true - diff --git a/.github/ISSUE_TEMPLATE/request.yml b/.github/ISSUE_TEMPLATE/request.yml deleted file mode 100644 index 296be3d7..00000000 --- a/.github/ISSUE_TEMPLATE/request.yml +++ /dev/null @@ -1,41 +0,0 @@ -name: Feature Request -description: Suggest a new feature or improvement for KernelSU Next. -title: "[Feature] " -labels: ["enhancement"] -body: - - type: markdown - attributes: - value: | - Thank you for taking the time to suggest a feature! Please fill out the following details. - - type: textarea - id: problem - attributes: - label: "Is your feature request related to a problem?" - description: "A clear and concise description of what the problem is. Ex: I'm always frustrated when..." - placeholder: "Describe the problem or need." - validations: - required: false - - type: textarea - id: solution - attributes: - label: "Describe the solution you'd like" - description: "A clear and concise description of what you want to happen." - placeholder: "Describe your proposed solution." - validations: - required: true - - type: textarea - id: alternatives - attributes: - label: "Describe alternatives you've considered" - description: "A clear and concise description of any alternative solutions or features you've considered." - placeholder: "List any alternatives." - validations: - required: false - - type: textarea - id: additional - attributes: - label: "Additional context" - description: "Add any other context or screenshots about the feature request here." - placeholder: "Additional context or screenshots." - validations: - required: false \ No newline at end of file diff --git a/.github/dependabot.yml b/.github/dependabot.yml deleted file mode 100644 index c46b22b6..00000000 --- a/.github/dependabot.yml +++ /dev/null @@ -1,38 +0,0 @@ -version: 2 -updates: - - package-ecosystem: github-actions - directory: / - schedule: - interval: daily - groups: - actions: - patterns: - - "*" - - package-ecosystem: cargo - directory: userspace/ksud_magic - schedule: - interval: daily - allow: - - dependency-type: "all" - groups: - crates: - patterns: - - "*" - - package-ecosystem: cargo - directory: userspace/ksud_overlayfs - schedule: - interval: daily - allow: - - dependency-type: "all" - groups: - crates: - patterns: - - "*" - - package-ecosystem: gradle - directory: manager - schedule: - interval: daily - groups: - maven: - patterns: - - "*" \ No newline at end of file diff --git a/.github/manifests/android-14-avd_x86_64.xml b/.github/manifests/android-14-avd_x86_64.xml deleted file mode 100644 index db2a6c06..00000000 --- a/.github/manifests/android-14-avd_x86_64.xml +++ /dev/null @@ -1,71 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/.github/manifests/android-15-avd_aarch64.xml b/.github/manifests/android-15-avd_aarch64.xml deleted file mode 100644 index e3a5e086..00000000 --- a/.github/manifests/android-15-avd_aarch64.xml +++ /dev/null @@ -1,89 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/.github/manifests/android-15-avd_x86_64.xml b/.github/manifests/android-15-avd_x86_64.xml deleted file mode 100644 index 0504d8a5..00000000 --- a/.github/manifests/android-15-avd_x86_64.xml +++ /dev/null @@ -1,89 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/.github/scripts/build_a12.sh b/.github/scripts/build_a12.sh deleted file mode 100644 index 4f0c0a65..00000000 --- a/.github/scripts/build_a12.sh +++ /dev/null @@ -1,64 +0,0 @@ -#!/bin/bash -set -euo pipefail - -build_from_image() { - export TITLE - TITLE=kernel-aarch64-${1//Image-/} - echo "[+] title: $TITLE" - - export PATCH_LEVEL - PATCH_LEVEL=$(echo "$1" | awk -F_ '{ print $2}') - echo "[+] patch level: $PATCH_LEVEL" - - echo '[+] Download prebuilt ramdisk' - GKI_URL=https://dl.google.com/android/gki/gki-certified-boot-android12-5.10-"${PATCH_LEVEL}"_r1.zip - FALLBACK_URL=https://dl.google.com/android/gki/gki-certified-boot-android12-5.10-2023-01_r1.zip - status=$(curl -sL -w "%{http_code}" "$GKI_URL" -o /dev/null) - if [ "$status" = "200" ]; then - curl -Lo gki-kernel.zip "$GKI_URL" - else - echo "[+] $GKI_URL not found, using $FALLBACK_URL" - curl -Lo gki-kernel.zip "$FALLBACK_URL" - fi - unzip gki-kernel.zip && rm gki-kernel.zip - - echo '[+] Unpack prebuilt boot.img' - BOOT_IMG=$(find . -maxdepth 1 -name "boot*.img") - $UNPACK_BOOTIMG --boot_img="$BOOT_IMG" - rm "$BOOT_IMG" - - echo '[+] Building Image.gz' - $GZIP -n -k -f -9 Image >Image.gz - - echo '[+] Building boot.img' - $MKBOOTIMG --header_version 4 --kernel Image --output boot.img --ramdisk out/ramdisk --os_version 12.0.0 --os_patch_level "${PATCH_LEVEL}" - $AVBTOOL add_hash_footer --partition_name boot --partition_size $((64 * 1024 * 1024)) --image boot.img --algorithm SHA256_RSA2048 --key ../kernel-build-tools/linux-x86/share/avb/testkey_rsa2048.pem - - echo '[+] Building boot-gz.img' - $MKBOOTIMG --header_version 4 --kernel Image.gz --output boot-gz.img --ramdisk out/ramdisk --os_version 12.0.0 --os_patch_level "${PATCH_LEVEL}" - $AVBTOOL add_hash_footer --partition_name boot --partition_size $((64 * 1024 * 1024)) --image boot-gz.img --algorithm SHA256_RSA2048 --key ../kernel-build-tools/linux-x86/share/avb/testkey_rsa2048.pem - - echo '[+] Building boot-lz4.img' - $MKBOOTIMG --header_version 4 --kernel Image.lz4 --output boot-lz4.img --ramdisk out/ramdisk --os_version 12.0.0 --os_patch_level "${PATCH_LEVEL}" - $AVBTOOL add_hash_footer --partition_name boot --partition_size $((64 * 1024 * 1024)) --image boot-lz4.img --algorithm SHA256_RSA2048 --key ../kernel-build-tools/linux-x86/share/avb/testkey_rsa2048.pem - - echo '[+] Compress images' - for image in boot*.img; do - $GZIP -n -f -9 "$image" - mv "$image".gz "${1//Image-/}"-"$image".gz - done - - echo "[+] Images to upload" - find . -type f -name "*.gz" - - # find . -type f -name "*.gz" -exec python3 "$GITHUB_WORKSPACE"/KernelSU-Next/scripts/ksunextbot.py {} + -} - -for dir in Image*; do - if [ -d "$dir" ]; then - echo "----- Building $dir -----" - cd "$dir" - build_from_image "$dir" - cd .. - fi -done diff --git a/.github/scripts/build_a13.sh b/.github/scripts/build_a13.sh deleted file mode 100644 index 32ce875a..00000000 --- a/.github/scripts/build_a13.sh +++ /dev/null @@ -1,43 +0,0 @@ -#!/bin/bash -set -euo pipefail - -build_from_image() { - export TITLE - TITLE=kernel-aarch64-${1//Image-/} - - echo "[+] title: $TITLE" - echo '[+] Building Image.gz' - $GZIP -n -k -f -9 Image >Image.gz - - echo '[+] Building boot.img' - $MKBOOTIMG --header_version 4 --kernel Image --output boot.img - $AVBTOOL add_hash_footer --partition_name boot --partition_size $((64 * 1024 * 1024)) --image boot.img --algorithm SHA256_RSA2048 --key ../kernel-build-tools/linux-x86/share/avb/testkey_rsa2048.pem - - echo '[+] Building boot-gz.img' - $MKBOOTIMG --header_version 4 --kernel Image.gz --output boot-gz.img - $AVBTOOL add_hash_footer --partition_name boot --partition_size $((64 * 1024 * 1024)) --image boot-gz.img --algorithm SHA256_RSA2048 --key ../kernel-build-tools/linux-x86/share/avb/testkey_rsa2048.pem - - echo '[+] Building boot-lz4.img' - $MKBOOTIMG --header_version 4 --kernel Image.lz4 --output boot-lz4.img - $AVBTOOL add_hash_footer --partition_name boot --partition_size $((64 * 1024 * 1024)) --image boot-lz4.img --algorithm SHA256_RSA2048 --key ../kernel-build-tools/linux-x86/share/avb/testkey_rsa2048.pem - - echo '[+] Compress images' - for image in boot*.img; do - $GZIP -n -f -9 "$image" - mv "$image".gz "${1//Image-/}"-"$image".gz - done - - echo '[+] Images to upload' - find . -type f -name "*.gz" - - # find . -type f -name "*.gz" -exec python3 "$GITHUB_WORKSPACE"/KernelSU-Next/scripts/ksunextbot.py {} + -} - -for dir in Image*; do - if [ -d "$dir" ]; then - echo "----- Building $dir -----" - cd "$dir" - build_from_image "$dir" - cd .. - fi -done diff --git a/.github/workflows/avd-kernel.yml b/.github/workflows/avd-kernel.yml deleted file mode 100644 index 96f85d68..00000000 --- a/.github/workflows/avd-kernel.yml +++ /dev/null @@ -1,137 +0,0 @@ -name: GKI Kernel Build - -on: - workflow_call: - inputs: - version_name: - required: true - type: string - description: > - With SUBLEVEL of kernel, - for example: android12-5.10.66 - arch: - required: true - type: string - description: > - Build arch: aarch64/x86_64 - debug: - required: false - type: boolean - default: true - manifest_name: - required: false - type: string - description: > - Local repo manifest xml path, - typically for AVD kernel build. - secrets: - BOOT_SIGN_KEY: - required: false - CHAT_ID: - required: false - BOT_TOKEN: - required: false - MESSAGE_THREAD_ID: - required: false - -jobs: - build: - name: Build ${{ inputs.version_name }} - runs-on: ubuntu-22.04 - steps: - - name: Maximize build space - uses: easimon/maximize-build-space@master - with: - root-reserve-mb: 8192 - temp-reserve-mb: 2048 - remove-dotnet: 'true' - remove-android: 'true' - remove-haskell: 'true' - remove-codeql: 'true' - - - uses: actions/checkout@v4 - with: - path: KernelSU-Next - fetch-depth: 0 - - - name: Setup need_upload - id: need_upload - run: | - if [ ! -z "${{ secrets.BOT_TOKEN }}" ]; then - echo "UPLOAD=true" >> $GITHUB_OUTPUT - else - echo "UPLOAD=false" >> $GITHUB_OUTPUT - fi - - - name: Setup kernel source - run: | - echo "Free space:" - df -h - cd $GITHUB_WORKSPACE - sudo apt-get install repo -y - mkdir android-kernel && cd android-kernel - repo init --depth=1 -u https://android.googlesource.com/kernel/manifest -m "$GITHUB_WORKSPACE/KernelSU-Next/.github/manifests/${{ inputs.manifest_name }}" --repo-rev=v2.16 - repo --version - repo --trace sync -c -j$(nproc --all) --no-tags - df -h - - - name: Setup KernelSU-Next - env: - PATCH_PATH: ${{ inputs.patch_path }} - IS_DEBUG_KERNEL: ${{ inputs.debug }} - run: | - cd $GITHUB_WORKSPACE/android-kernel - echo "[+] KernelSU-Next setup" - GKI_ROOT=$(pwd) - echo "[+] GKI_ROOT: $GKI_ROOT" - echo "[+] Copy KernelSU-Next driver to $GKI_ROOT/common/drivers" - ln -sf $GITHUB_WORKSPACE/KernelSU-Next/kernel $GKI_ROOT/common/drivers/kernelsu - echo "[+] Add KernelSU-Next driver to Makefile" - DRIVER_MAKEFILE=$GKI_ROOT/common/drivers/Makefile - DRIVER_KCONFIG=$GKI_ROOT/common/drivers/Kconfig - grep -q "kernelsu" "$DRIVER_MAKEFILE" || printf "\nobj-\$(CONFIG_KSU) += kernelsu/\n" >> "$DRIVER_MAKEFILE" - grep -q "kernelsu" "$DRIVER_KCONFIG" || sed -i "/endmenu/i\\source \"drivers/kernelsu/Kconfig\"" "$DRIVER_KCONFIG" - echo "[+] Apply KernelSU-Next patches" - cd $GKI_ROOT/common/ && git apply $GITHUB_WORKSPACE/KernelSU-Next/.github/patches/$PATCH_PATH/*.patch || echo "[-] No patch found" - - if [ "$IS_DEBUG_KERNEL" = "true" ]; then - echo "[+] Enable debug features for kernel" - printf "\nccflags-y += -DCONFIG_KSU_DEBUG\n" >> $GITHUB_WORKSPACE/KernelSU-Next/kernel/Makefile - fi - repo status - echo "[+] KernelSU-Next setup done." - cd $GITHUB_WORKSPACE/KernelSU-Next - VERSION=$(($(git rev-list --count HEAD) + 10200)) - echo "VERSION: $VERSION" - echo "kernelsu_version=$VERSION" >> $GITHUB_ENV - - - name: Make working directory clean to avoid dirty - working-directory: android-kernel - run: | - rm common/android/abi_gki_protected_exports_* || echo "No protected exports!" - git config --global user.email "bot@kernelsu.org" - git config --global user.name "KernelSU-NextBot" - cd common/ && git add -A && git commit -a -m "Add KernelSU-Next" - repo status - - - name: Build kernel - working-directory: android-kernel - run: | - if [ ! -z ${{ vars.EXPECTED_SIZE }} ] && [ ! -z ${{ vars.EXPECTED_HASH }} ]; then - export KSU_EXPECTED_SIZE=${{ vars.EXPECTED_SIZE }} - export KSU_EXPECTED_HASH=${{ vars.EXPECTED_HASH }} - fi - tools/bazel run --config=fast --config=stamp --lto=thin //common-modules/virtual-device:virtual_device_${{ inputs.arch }}_dist -- --dist_dir=dist - NAME=kernel-${{ inputs.arch }}-avd-${{ inputs.version_name }}-${{ env.kernelsu_version }} - TARGET_IMAGE=dist/bzImage - if [ ! -e $TARGET_IMAGE ]; then - TARGET_IMAGE=dist/Image - fi - mv $TARGET_IMAGE $NAME - echo "file_path=android-kernel/$NAME" >> $GITHUB_ENV - - - name: Upload Kernel - uses: actions/upload-artifact@v4 - with: - name: kernel-${{ inputs.arch }}-avd-${{ inputs.version_name }}-${{ env.kernelsu_version }} - path: "${{ env.file_path }}" diff --git a/.github/workflows/build-debug-kernel.yml b/.github/workflows/build-debug-kernel.yml deleted file mode 100644 index bf320a90..00000000 --- a/.github/workflows/build-debug-kernel.yml +++ /dev/null @@ -1,62 +0,0 @@ -name: Build debug kernel -on: - workflow_dispatch: - -jobs: - build-debug-kernel-a12: - uses: ./.github/workflows/gki-kernel.yml - with: - version: android12-5.10 - version_name: android12-5.10.236 - tag: android12-5.10-2025-05 - os_patch_level: 2025-05 - patch_path: "5.10" - debug: true - build-debug-kernel-a13: - strategy: - matrix: - include: - - version: "5.10" - sub_level: 236 - os_patch_level: 2025-05 - - version: "5.15" - sub_level: 180 - os_patch_level: 2025-05 - uses: ./.github/workflows/gki-kernel.yml - with: - version: android13-${{ matrix.version }} - version_name: android13-${{ matrix.version }}.${{ matrix.sub_level }} - tag: android13-${{ matrix.version }}-${{ matrix.os_patch_level }} - patch_path: ${{ matrix.version }} - debug: true - build-debug-kernel-a14: - strategy: - matrix: - include: - - version: "5.15" - sub_level: 180 - os_patch_level: 2025-05 - - version: "6.1" - sub_level: 138 - os_patch_level: 2025-06 - 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: 89 - os_patch_level: 2025-06 - 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 deleted file mode 100644 index 316e6dc4..00000000 --- a/.github/workflows/build-kernel-a12.yml +++ /dev/null @@ -1,120 +0,0 @@ -name: Build Kernel - Android 12 -on: - push: - branches: [ "next" ] - paths: - - ".github/workflows/build-kernel-a12.yml" - - ".github/workflows/gki-kernel.yml" - - ".github/scripts/build_a12.sh" - - "kernel/**" - pull_request: - branches: [ "next" ] - paths: - - ".github/workflows/build-kernel-a12.yml" - - ".github/workflows/gki-kernel.yml" - - ".github/scripts/build-a12.sh" - - "kernel/**" - workflow_call: -jobs: - build-kernel: - if: github.event_name != 'pull_request' && github.ref != 'refs/heads/next' - strategy: - matrix: - include: - - sub_level: 218 - os_patch_level: 2024-08 - - sub_level: 226 - os_patch_level: 2024-11 - - sub_level: 233 - os_patch_level: 2025-02 - - sub_level: 236 - os_patch_level: 2025-05 - uses: ./.github/workflows/gki-kernel.yml - secrets: inherit - with: - version: android12-5.10 - version_name: android12-5.10.${{ matrix.sub_level }} - tag: android12-5.10-${{ matrix.os_patch_level }} - os_patch_level: ${{ matrix.os_patch_level }} - patch_path: "5.10" - - upload-artifacts: - needs: build-kernel - runs-on: ubuntu-latest - if: ${{ ( github.event_name != 'pull_request' && github.ref == 'refs/heads/next' ) || github.ref_type == 'tag' || github.ref == 'refs/heads/next' }} - 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-Next - 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/ksunextbot.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-Next - export VERSION=$(($(git rev-list --count HEAD) + 10200)) - echo "VERSION: $VERSION" - cd - - bash $GITHUB_WORKSPACE/KernelSU-Next/.github/scripts/build_a12.sh - - - name: Display structure of boot files - run: ls -R - - - name: Upload images artifact - uses: actions/upload-artifact@v4 - with: - name: boot-images-android12 - path: Image-android12*/*.img.gz - - check-build-kernel: - if: (github.event_name == 'pull_request' && !github.event.pull_request.draft) || github.ref == 'refs/heads/next' - uses: ./.github/workflows/gki-kernel.yml - with: - version: android12-5.10 - version_name: android12-5.10.236 - tag: android12-5.10-2025-05 - os_patch_level: 2025-05 - 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 deleted file mode 100644 index 3dcb81a2..00000000 --- a/.github/workflows/build-kernel-a13.yml +++ /dev/null @@ -1,163 +0,0 @@ -name: Build Kernel - Android 13 -on: - push: - branches: [ "next" ] - paths: - - ".github/workflows/build-kernel-a13.yml" - - ".github/workflows/gki-kernel.yml" - - ".github/scripts/build_a13.sh" - - "kernel/**" - pull_request: - branches: [ "next" ] - paths: - - ".github/workflows/build-kernel-a13.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/next' - strategy: - matrix: - include: - - version: "5.10" - sub_level: 210 - os_patch_level: 2024-06 - - version: "5.10" - sub_level: 214 - os_patch_level: 2024-07 - - version: "5.10" - sub_level: 218 - os_patch_level: 2024-08 - - version: "5.10" - sub_level: 223 - os_patch_level: 2024-11 - - version: "5.10" - sub_level: 228 - os_patch_level: 2025-01 - - version: "5.10" - sub_level: 234 - os_patch_level: 2025-03 - - version: "5.10" - sub_level: 236 - os_patch_level: 2025-05 - - version: "5.15" - sub_level: 149 - os_patch_level: 2024-07 - - version: "5.15" - sub_level: 151 - os_patch_level: 2024-08 - - version: "5.15" - sub_level: 153 - os_patch_level: 2024-09 - - version: "5.15" - sub_level: 167 - os_patch_level: 2024-11 - - version: "5.15" - sub_level: 170 - os_patch_level: 2025-01 - - version: "5.15" - sub_level: 178 - os_patch_level: 2025-03 - - version: "5.15" - sub_level: 180 - os_patch_level: 2025-05 - uses: ./.github/workflows/gki-kernel.yml - secrets: inherit - with: - version: android13-${{ matrix.version }} - version_name: android13-${{ matrix.version }}.${{ matrix.sub_level }} - tag: android13-${{ 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/next' ) || github.ref_type == 'tag' || github.ref == 'refs/heads/next' }} - 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-Next - 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/ksunextbot.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-Next - export VERSION=$(($(git rev-list --count HEAD) + 10200)) - echo "VERSION: $VERSION" - cd - - bash $GITHUB_WORKSPACE/KernelSU-Next/.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-android13 - path: Image-android13*/*.img.gz - - check-build-kernel: - if: (github.event_name == 'pull_request' && !github.event.pull_request.draft) || github.ref == 'refs/heads/next' - strategy: - matrix: - include: - - version: "5.10" - sub_level: 236 - os_patch_level: 2025-05 - - version: "5.15" - sub_level: 180 - os_patch_level: 2025-05 - uses: ./.github/workflows/gki-kernel.yml - with: - version: android13-${{ matrix.version }} - version_name: android13-${{ matrix.version }}.${{ matrix.sub_level }} - tag: android13-${{ 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-a14.yml b/.github/workflows/build-kernel-a14.yml deleted file mode 100644 index 0f4b85e9..00000000 --- a/.github/workflows/build-kernel-a14.yml +++ /dev/null @@ -1,184 +0,0 @@ -name: Build Kernel - Android 14 -on: - push: - branches: [ "next" ] - paths: - - ".github/workflows/build-kernel-a14.yml" - - ".github/workflows/gki-kernel.yml" - - ".github/scripts/build_a13.sh" - - "kernel/**" - pull_request: - branches: [ "next" ] - paths: - - ".github/workflows/build-kernel-a14.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/next' - strategy: - matrix: - include: - - version: "5.15" - sub_level: 149 - os_patch_level: 2024-06 - - version: "5.15" - sub_level: 153 - os_patch_level: 2024-07 - - version: "5.15" - sub_level: 158 - os_patch_level: 2024-08 - - version: "5.15" - sub_level: 164 - os_patch_level: 2024-09 - - version: "5.15" - sub_level: 167 - os_patch_level: 2024-11 - - version: "5.15" - sub_level: 170 - os_patch_level: 2025-01 - - version: "5.15" - sub_level: 178 - os_patch_level: 2025-03 - - version: "5.15" - sub_level: 180 - os_patch_level: 2025-05 - - version: "6.1" - sub_level: 78 - os_patch_level: 2024-06 - - version: "6.1" - sub_level: 84 - os_patch_level: 2024-07 - - version: "6.1" - sub_level: 90 - os_patch_level: 2024-08 - - version: "6.1" - sub_level: 93 - os_patch_level: 2024-09 - - version: "6.1" - sub_level: 99 - os_patch_level: 2024-10 - - version: "6.1" - sub_level: 112 - os_patch_level: 2024-11 - - version: "6.1" - sub_level: 115 - os_patch_level: 2024-12 - - version: "6.1" - sub_level: 118 - os_patch_level: 2025-01 - - version: "6.1" - sub_level: 124 - os_patch_level: 2025-02 - - version: "6.1" - sub_level: 128 - os_patch_level: 2025-03 - - version: "6.1" - sub_level: 129 - os_patch_level: 2025-04 - - version: "6.1" - sub_level: 134 - os_patch_level: 2025-05 - - version: "6.1" - sub_level: 138 - os_patch_level: 2025-06 - uses: ./.github/workflows/gki-kernel.yml - secrets: inherit - with: - version: android14-${{ matrix.version }} - version_name: android14-${{ matrix.version }}.${{ matrix.sub_level }} - tag: android14-${{ 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/next' ) || github.ref_type == 'tag' || github.ref == 'refs/heads/next' }} - 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-Next - 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/ksunextbot.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-Next - export VERSION=$(($(git rev-list --count HEAD) + 10200)) - echo "VERSION: $VERSION" - cd - - bash $GITHUB_WORKSPACE/KernelSU-Next/.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-android14 - path: Image-android14*/*.img.gz - - check-build-kernel: - if: (github.event_name == 'pull_request' && !github.event.pull_request.draft) || github.ref == 'refs/heads/next' - strategy: - matrix: - include: - - version: "5.15" - sub_level: 180 - os_patch_level: 2025-05 - - version: "6.1" - sub_level: 138 - os_patch_level: 2025-06 - 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 }} - os_patch_level: ${{ matrix.os_patch_level }} - patch_path: ${{ matrix.version }} \ No newline at end of file diff --git a/.github/workflows/build-kernel-a15.yml b/.github/workflows/build-kernel-a15.yml deleted file mode 100644 index 91b4174c..00000000 --- a/.github/workflows/build-kernel-a15.yml +++ /dev/null @@ -1,151 +0,0 @@ -name: Build Kernel - Android 15 -on: - push: - branches: [ "next" ] - paths: - - ".github/workflows/build-kernel-a15.yml" - - ".github/workflows/gki-kernel.yml" - - ".github/scripts/build_a13.sh" - - "kernel/**" - pull_request: - branches: [ "next" ] - 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/next' - strategy: - matrix: - include: - - version: "6.6" - sub_level: 30 - os_patch_level: 2024-08 - - version: "6.6" - sub_level: 46 - os_patch_level: 2024-09 - - version: "6.6" - sub_level: 50 - os_patch_level: 2024-10 - - version: "6.6" - sub_level: 56 - os_patch_level: 2024-11 - - version: "6.6" - sub_level: 57 - os_patch_level: 2024-12 - - version: "6.6" - sub_level: 58 - os_patch_level: 2025-01 - - version: "6.6" - sub_level: 66 - os_patch_level: 2025-02 - - version: "6.6" - sub_level: 77 - os_patch_level: 2025-03 - - version: "6.6" - sub_level: 82 - os_patch_level: 2025-04 - - version: "6.6" - sub_level: 87 - os_patch_level: 2025-05 - - version: "6.6" - sub_level: 89 - os_patch_level: 2025-06 - 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/next' ) || github.ref_type == 'tag' || github.ref == 'refs/heads/next' }} - 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-Next - 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/ksunextbot.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-Next - export VERSION=$(($(git rev-list --count HEAD) + 10200)) - echo "VERSION: $VERSION" - cd - - bash $GITHUB_WORKSPACE/KernelSU-Next/.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/next' - strategy: - matrix: - include: - - version: "6.6" - sub_level: 89 - os_patch_level: 2025-06 - 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 deleted file mode 100644 index 54240370..00000000 --- a/.github/workflows/build-kernel-arcvm.yml +++ /dev/null @@ -1,137 +0,0 @@ -name: Build Kernel - ChromeOS ARCVM -on: - push: - branches: ["next"] - paths: - - ".github/workflows/build-kernel-arcvm.yml" - - "kernel/**" - pull_request: - branches: ["next"] - paths: - - ".github/workflows/build-kernel-arcvm.yml" - - "kernel/**" - workflow_call: - workflow_dispatch: - -env: - git_tag: chromeos-5.10-arcvm - -jobs: - build: - if: github.event_name != 'pull_request' || (github.event_name == 'pull_request' && !github.event.pull_request.draft) - strategy: - matrix: - include: - - arch: x86_64 - kernel_image_name: bzImage - build_config: build.config.gki.x86_64 - defconfig: x86_64_arcvm_defconfig - - arch: arm64 - kernel_image_name: Image - build_config: build.config.gki.aarch64 - defconfig: arm64_arcvm_defconfig - - name: Build ChromeOS ARCVM kernel - runs-on: ubuntu-22.04 - env: - LTO: thin - ROOT_DIR: / - KERNEL_DIR: ${{ github.workspace }}/kernel - - steps: - - name: Install Build Tools - run: | - sudo apt-get update - sudo apt-get install -y --no-install-recommends bc \ - bison build-essential ca-certificates flex git gnupg \ - libelf-dev libssl-dev lsb-release software-properties-common wget \ - libncurses-dev binutils-aarch64-linux-gnu gcc-aarch64-linux-gnu nuget gzip \ - rsync python3 device-tree-compiler - - sudo ln -s --force python3 /usr/bin/python - - export LLVM_VERSION=14 - wget https://apt.llvm.org/llvm.sh - chmod +x llvm.sh - sudo ./llvm.sh $LLVM_VERSION - rm ./llvm.sh - sudo ln -s --force /usr/bin/clang-$LLVM_VERSION /usr/bin/clang - sudo ln -s --force /usr/bin/ld.lld-$LLVM_VERSION /usr/bin/ld.lld - sudo ln -s --force /usr/bin/llvm-objdump-$LLVM_VERSION /usr/bin/llvm-objdump - sudo ln -s --force /usr/bin/llvm-ar-$LLVM_VERSION /usr/bin/llvm-ar - sudo ln -s --force /usr/bin/llvm-nm-$LLVM_VERSION /usr/bin/llvm-nm - sudo ln -s --force /usr/bin/llvm-strip-$LLVM_VERSION /usr/bin/llvm-strip - sudo ln -s --force /usr/bin/llvm-objcopy-$LLVM_VERSION /usr/bin/llvm-objcopy - sudo ln -s --force /usr/bin/llvm-readelf-$LLVM_VERSION /usr/bin/llvm-readelf - sudo ln -s --force /usr/bin/clang++-$LLVM_VERSION /usr/bin/clang++ - - - name: Checkout KernelSU-Next - uses: actions/checkout@v4 - with: - path: KernelSU-Next - fetch-depth: 0 - - - name: Setup kernel source - run: git clone https://chromium.googlesource.com/chromiumos/third_party/kernel.git -b ${{ env.git_tag }} --depth=1 - - - name: Extract version from Makefile - working-directory: kernel - run: | - VERSION=$(grep -E '^VERSION = ' Makefile | awk '{print $3}') - PATCHLEVEL=$(grep -E '^PATCHLEVEL = ' Makefile | awk '{print $3}') - SUBLEVEL=$(grep -E '^SUBLEVEL = ' Makefile | awk '{print $3}') - echo "ChromeOS ARCVM Linux kernel version: $VERSION.$PATCHLEVEL.$SUBLEVEL" - echo "version=$VERSION.$PATCHLEVEL.$SUBLEVEL" >> $GITHUB_ENV - - - name: Setup KernelSU-Next - working-directory: kernel - run: | - echo "[+] KernelSU-Next setup" - KERNEL_ROOT=$GITHUB_WORKSPACE/kernel - echo "[+] KERNEL_ROOT: $KERNEL_ROOT" - echo "[+] Copy KernelSU-Next driver to $KERNEL_ROOT/drivers" - ln -sf $GITHUB_WORKSPACE/KernelSU-Next/kernel $KERNEL_ROOT/drivers/kernelsu-next - - echo "[+] Add KernelSU-Next driver to Makefile" - DRIVER_MAKEFILE=$KERNEL_ROOT/drivers/Makefile - DRIVER_KCONFIG=$KERNEL_ROOT/drivers/Kconfig - grep -q "kernelsu-next" "$DRIVER_MAKEFILE" || printf "\nobj-\$(CONFIG_KSU) += kernelsu-next/\n" >> "$DRIVER_MAKEFILE" - grep -q "kernelsu-next" "$DRIVER_KCONFIG" || sed -i "/endmenu/i\\source \"drivers/kernelsu-next/Kconfig\"" "$DRIVER_KCONFIG" - - echo "[+] Apply KernelSU patches" - cd $KERNEL_ROOT && git apply $GITHUB_WORKSPACE/KernelSU-Next/.github/patches/5.10/*.patch || echo "[-] No patch found" - - echo "[+] Patch script/setlocalversion" - sed -i 's/-dirty//g' $KERNEL_ROOT/scripts/setlocalversion - - echo "[+] KernelSU-Next setup done." - cd $GITHUB_WORKSPACE/KernelSU-Next - KSU_VERSION=$(($(git rev-list --count HEAD) + 10200)) - echo "KernelSU-Next version: $KSU_VERSION" - echo "kernelsu-next_version=$KSU_VERSION" >> $GITHUB_ENV - - - name: Build Kernel - working-directory: kernel - env: - KERNEL_IMAGE_NAME: ${{ matrix.kernel_image_name }} - ARCH: ${{ matrix.arch }} - run: | - set -a && . ${{ matrix.build_config }}; set +a - export DEFCONFIG=${{ matrix.defconfig }} - if [ ! -z ${{ vars.EXPECTED_SIZE }} ] && [ ! -z ${{ vars.EXPECTED_HASH }} ]; then - export KSU_EXPECTED_SIZE=${{ vars.EXPECTED_SIZE }} - export KSU_EXPECTED_HASH=${{ vars.EXPECTED_HASH }} - fi - - make LLVM=1 LLVM_IAS=1 DEPMOD=depmod DTC=dtc O=${PWD} mrproper - make LLVM=1 LLVM_IAS=1 DEPMOD=depmod DTC=dtc O=${PWD} ${DEFCONFIG} < /dev/null - scripts/config --file .config -e LTO_CLANG -d LTO_NONE -e LTO_CLANG_THIN -d LTO_CLANG_FULL -e THINLTO - make LLVM=1 LLVM_IAS=1 DEPMOD=depmod DTC=dtc O=${PWD} -j$(nproc) ${KERNEL_IMAGE_NAME} modules prepare-objtool - ls -l -h ${PWD}/arch/${ARCH}/boot - echo "file_path=${PWD}/arch/${ARCH}/boot/${KERNEL_IMAGE_NAME}" >> $GITHUB_ENV - - - name: Upload kernel-ARCVM-${{ matrix.arch }}-${{ env.version }} - uses: actions/upload-artifact@v4 - with: - name: kernel-ARCVM-${{ matrix.arch }}-${{ env.version }} - path: "${{ env.file_path }}" \ No newline at end of file diff --git a/.github/workflows/build-kernel-avd.yml b/.github/workflows/build-kernel-avd.yml deleted file mode 100644 index e85a4127..00000000 --- a/.github/workflows/build-kernel-avd.yml +++ /dev/null @@ -1,39 +0,0 @@ -name: Build Kernel - AVD -on: - push: - branches: ["next"] - paths: - - ".github/workflows/build-kernel-avd.yml" - - ".github/workflows/avd-kernel.yml" - - "kernel/**" - pull_request: - branches: ["next"] - paths: - - ".github/workflows/build-kernel-avd.yml" - - ".github/workflows/avd-kernel.yml" - - "kernel/**" - workflow_call: - workflow_dispatch: - -jobs: - build-kernel: - if: github.event_name != 'pull_request' && github.ref != 'refs/heads/checkci' - uses: ./.github/workflows/avd-kernel.yml - secrets: inherit - strategy: - matrix: - include: - - version: "android-14-avd_x86_64" - manifest: "android-14-avd_x86_64.xml" - arch: "x86_64" - - version: "android-15-avd_aarch64" - manifest: "android-15-avd_aarch64.xml" - arch: "aarch64" - - version: "android-15-avd_x86_64" - manifest: "android-15-avd_x86_64.xml" - arch: "x86_64" - with: - version_name: ${{ matrix.version }} - manifest_name: ${{ matrix.manifest }} - arch: ${{ matrix.arch }} - debug: true \ No newline at end of file diff --git a/.github/workflows/build-kernel-wsa.yml b/.github/workflows/build-kernel-wsa.yml deleted file mode 100644 index 09133e8e..00000000 --- a/.github/workflows/build-kernel-wsa.yml +++ /dev/null @@ -1,38 +0,0 @@ -name: Build Kernel - WSA -on: - push: - branches: ["next"] - paths: - - ".github/workflows/build-kernel-wsa.yml" - - ".github/workflows/wsa-kernel.yml" - - "kernel/**" - pull_request: - branches: ["next"] - paths: - - ".github/workflows/build-kernel-wsa.yml" - - ".github/workflows/wsa-kernel.yml" - - "kernel/**" - workflow_call: - workflow_dispatch: - -jobs: - build: - if: github.event_name != 'pull_request' && github.ref != 'refs/heads/checkci' - strategy: - matrix: - arch: [x86_64, arm64] - version: ["5.15.94.2", "5.15.104.1", "5.15.104.2", "5.15.104.3", "5.15.104.4"] - uses: ./.github/workflows/wsa-kernel.yml - with: - arch: ${{ matrix.arch }} - version: ${{ matrix.version }} - - check_build: - if: (github.event_name == 'pull_request' && !github.event.pull_request.draft) || github.ref == 'refs/heads/checkci' - uses: ./.github/workflows/wsa-kernel.yml - strategy: - matrix: - arch: [x86_64, arm64] - with: - arch: ${{ matrix.arch }} - version: "5.15.104.4" \ No newline at end of file diff --git a/.github/workflows/build-lkm.yml b/.github/workflows/build-lkm.yml deleted file mode 100644 index dc3482ac..00000000 --- a/.github/workflows/build-lkm.yml +++ /dev/null @@ -1,41 +0,0 @@ -name: Build LKM for KernelSU-Next -on: - push: - branches: [ "next" ] - paths: - - ".github/workflows/build-lkm.yml" - pull_request: - branches: [ "next" ] - paths: - - ".github/workflows/build-lkm.yml" - workflow_call: -jobs: - build-lkm: - strategy: - matrix: - include: - - version: "android12-5.10" - sub_level: 236 - os_patch_level: 2025-05 - - version: "android13-5.10" - sub_level: 236 - os_patch_level: 2025-05 - - version: "android13-5.15" - sub_level: 180 - os_patch_level: 2025-05 - - version: "android14-5.15" - sub_level: 180 - os_patch_level: 2025-05 - - version: "android14-6.1" - sub_level: 138 - os_patch_level: 2025-06 - - version: "android15-6.6" - sub_level: 89 - os_patch_level: 2025-06 - uses: ./.github/workflows/gki-kernel.yml - with: - version: ${{ matrix.version }} - version_name: ${{ matrix.version }}.${{ matrix.sub_level }} - tag: ${{ matrix.version }}-${{ matrix.os_patch_level }} - os_patch_level: ${{ matrix.os_patch_level }} - build_lkm: true \ No newline at end of file diff --git a/.github/workflows/build-manager-ci.yml b/.github/workflows/build-manager-ci.yml deleted file mode 100644 index a36ead50..00000000 --- a/.github/workflows/build-manager-ci.yml +++ /dev/null @@ -1,227 +0,0 @@ -name: Build Manager CI - -on: - push: - branches: [ "next" ] - paths: - - '.github/workflows/build-manager-ci.yml' - - 'manager/**' - - 'kernel/**' - - 'userspace/ksud_overlayfs**' - - 'userspace/ksud_magic/**' - - 'userspace/susfsd/**' - pull_request: - branches: [ "next" ] - paths: - - '.github/workflows/build-manager-ci.yml' - - 'manager/**' - workflow_call: - workflow_dispatch: - -jobs: - build-lkm: - uses: ./.github/workflows/build-lkm.yml - secrets: inherit - - build-susfsd: - needs: build-lkm - strategy: - matrix: - include: - - os: ubuntu-latest - uses: ./.github/workflows/susfsd.yml - with: - os: ${{ matrix.os }} - - build-ksud: - needs: build-susfsd - strategy: - matrix: - include: - - target: aarch64-linux-android - os: ubuntu-latest - - target: armv7-linux-androideabi - os: ubuntu-latest - - target: x86_64-linux-android - os: ubuntu-latest - uses: ./.github/workflows/ksud.yml - with: - target: ${{ matrix.target }} - os: ${{ matrix.os }} - - build-manager: - needs: build-ksud - runs-on: ubuntu-latest - defaults: - run: - working-directory: ./manager - - steps: - - name: Checkout Repository - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - name: Setup need_upload - id: need_upload - run: | - if [ ! -z "${{ secrets.BOT_TOKEN }}" ]; then - echo "UPLOAD=true" >> $GITHUB_OUTPUT - else - echo "UPLOAD=false" >> $GITHUB_OUTPUT - fi - - - name: Write Key - run: | - if [ ! -z "${{ secrets.KEYSTORE }}" ]; then - { - echo KEYSTORE_PASSWORD='${{ secrets.KEYSTORE_PASSWORD }}' - echo KEY_ALIAS='${{ secrets.KEY_ALIAS }}' - echo KEY_PASSWORD='${{ secrets.KEY_PASSWORD }}' - echo KEYSTORE_FILE='key.jks' - } >> gradle.properties - echo ${{ secrets.KEYSTORE }} | base64 -d > key.jks - fi - - - name: Setup Java - uses: actions/setup-java@v4 - with: - distribution: temurin - java-version: 21 - - - name: Setup Gradle - uses: gradle/actions/setup-gradle@v4 - - - name: Download susfsd - uses: actions/download-artifact@v4 - with: - name: susfsd-linux-android - path: . - - - name: Copy susfsd to app jniLibs - run: | - mkdir -p app/src/main/jniLibs/arm64-v8a - cp -f ../arm64-v8a/susfsd ../manager/app/src/main/jniLibs/arm64-v8a/libsusfsd.so - - mkdir -p app/src/main/jniLibs/armeabi-v7a - cp -f ../armeabi-v7a/susfsd ../manager/app/src/main/jniLibs/armeabi-v7a/libsusfsd.so - - mkdir -p app/src/main/jniLibs/x86_64 - cp -f ../x86_64/susfsd ../manager/app/src/main/jniLibs/x86_64/libsusfsd.so - - - - name: Download arm64 ksud_overlayfs - uses: actions/download-artifact@v4 - with: - name: ksud_overlayfs-aarch64-linux-android - path: ksud_overlayfs - - - name: Download arm ksud_overlayfs - uses: actions/download-artifact@v4 - with: - name: ksud_overlayfs-armv7-linux-androideabi - path: ksud_overlayfs - - - name: Download x86_64 ksud_overlayfs - uses: actions/download-artifact@v4 - with: - name: ksud_overlayfs-x86_64-linux-android - path: ksud_overlayfs - - - name: Copy ksud_overlayfs to app jniLibs - run: | - mkdir -p app/src/main/jniLibs/arm64-v8a - - mkdir -p app/src/main/jniLibs/armeabi-v7a - - mkdir -p app/src/main/jniLibs/x86_64 - - cp -f ../ksud_overlayfs/aarch64-linux-android/release/ksud ../manager/app/src/main/jniLibs/arm64-v8a/libksud_overlayfs.so - - cp -f ../ksud_overlayfs/armv7-linux-androideabi/release/ksud ../manager/app/src/main/jniLibs/armeabi-v7a/libksud_overlayfs.so - - cp -f ../ksud_overlayfs/x86_64-linux-android/release/ksud ../manager/app/src/main/jniLibs/x86_64/libksud_overlayfs.so - - - name: Download arm64 ksud_magic - uses: actions/download-artifact@v4 - with: - name: ksud_magic-aarch64-linux-android - path: ksud_magic - - - name: Download arm ksud_magic - uses: actions/download-artifact@v4 - with: - name: ksud_magic-armv7-linux-androideabi - path: ksud_magic - - - name: Download x86_64 ksud_magic - uses: actions/download-artifact@v4 - with: - name: ksud_magic-x86_64-linux-android - path: ksud_magic - - - name: Copy ksud_magic to app jniLibs - run: | - mkdir -p app/src/main/jniLibs/arm64-v8a - - mkdir -p app/src/main/jniLibs/armeabi-v7a - - mkdir -p app/src/main/jniLibs/x86_64 - - cp -f ../ksud_magic/aarch64-linux-android/release/ksud ../manager/app/src/main/jniLibs/arm64-v8a/libksud_magic.so - - cp -f ../ksud_magic/armv7-linux-androideabi/release/ksud ../manager/app/src/main/jniLibs/armeabi-v7a/libksud_magic.so - - cp -f ../ksud_magic/x86_64-linux-android/release/ksud ../manager/app/src/main/jniLibs/x86_64/libksud_magic.so - - - name: Build with Gradle - run: | - { - echo 'org.gradle.parallel=true' - echo 'org.gradle.vfs.watch=true' - echo 'org.gradle.jvmargs=-Xmx2048m' - echo 'android.native.buildOutput=verbose' - } >> gradle.properties - sed -i 's/org.gradle.configuration-cache=true//g' gradle.properties - chmod +x gradlew - ./gradlew clean assembleRelease - - - name: Upload Build Artifact - uses: actions/upload-artifact@v4 - with: - name: Manager - path: manager/app/build/outputs/apk/release/*.apk - - - name: Upload Mappings - uses: actions/upload-artifact@v4 - with: - name: Mappings - path: manager/app/build/outputs/mapping/release/ - - - name: Bot Session Cache - if: steps.need_upload.outputs.UPLOAD == 'true' - id: bot_session_cache - uses: actions/cache@v4 - with: - path: scripts/ksunextbot.session - key: ${{ runner.os }}-bot-session - - - name: Upload to Telegram - if: steps.need_upload.outputs.UPLOAD == 'true' - env: - API_ID: ${{ secrets.API_ID }} - API_HASH: ${{ secrets.API_HASH }} - 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 }} - TITLE: CI Manager (TEST BUILD) - run: | - if [ ! -z "${{ secrets.BOT_TOKEN }}" ]; then - export VERSION=$(git rev-list --count HEAD) - APK=$(find ./app/build/outputs/apk/release -name "*.apk") - pip3 install telethon - python3 $GITHUB_WORKSPACE/scripts/ksunextbot.py $APK - fi diff --git a/.github/workflows/build-manager-spoofed.yml b/.github/workflows/build-manager-spoofed.yml deleted file mode 100644 index c166f3a5..00000000 --- a/.github/workflows/build-manager-spoofed.yml +++ /dev/null @@ -1,222 +0,0 @@ -name: Build Manager Spoofed - -on: - push: - branches: [ "next" ] - paths: - - '.github/workflows/build-manager-ci.yml' - - 'manager/**' - - 'kernel/**' - - 'userspace/ksud_overlayfs**' - - 'userspace/ksud_magic/**' - - 'userspace/susfsd/**' - pull_request: - branches: [ "next" ] - paths: - - '.github/workflows/build-manager-ci.yml' - - 'manager/**' - workflow_call: - workflow_dispatch: - schedule: - - cron: "0 12 * * *" # 6 PM UTC+6 | 12 PM UTC - -jobs: - build-lkm: - uses: ./.github/workflows/build-lkm.yml - secrets: inherit - - build-susfsd: - needs: build-lkm - strategy: - matrix: - include: - - os: ubuntu-latest - uses: ./.github/workflows/susfsd.yml - with: - os: ${{ matrix.os }} - - build-ksud: - needs: build-susfsd - strategy: - matrix: - include: - - target: aarch64-linux-android - os: ubuntu-latest - - target: armv7-linux-androideabi - os: ubuntu-latest - - target: x86_64-linux-android - os: ubuntu-latest - uses: ./.github/workflows/ksud.yml - with: - target: ${{ matrix.target }} - os: ${{ matrix.os }} - - build-manager: - needs: build-ksud - runs-on: ubuntu-latest - defaults: - run: - working-directory: ./manager - - steps: - - name: Checkout Repository - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - name: Spoof Package ID - run: | - chmod +x spoof - ./spoof - - - name: Setup need_upload - id: need_upload - run: | - if [ ! -z "${{ secrets.BOT_TOKEN }}" ]; then - echo "UPLOAD=true" >> $GITHUB_OUTPUT - else - echo "UPLOAD=false" >> $GITHUB_OUTPUT - fi - - - name: Write Key - run: | - if [ ! -z "${{ secrets.KEYSTORE }}" ]; then - { - echo KEYSTORE_PASSWORD='${{ secrets.KEYSTORE_PASSWORD }}' - echo KEY_ALIAS='${{ secrets.KEY_ALIAS }}' - echo KEY_PASSWORD='${{ secrets.KEY_PASSWORD }}' - echo KEYSTORE_FILE='key.jks' - } >> gradle.properties - echo ${{ secrets.KEYSTORE }} | base64 -d > key.jks - fi - - - name: Setup Java - uses: actions/setup-java@v4 - with: - distribution: temurin - java-version: 21 - - - name: Setup Gradle - uses: gradle/actions/setup-gradle@v4 - - - name: Download susfsd - uses: actions/download-artifact@v4 - with: - name: susfsd-linux-android - path: . - - - name: Copy susfsd to app jniLibs - run: | - mkdir -p app/src/main/jniLibs/arm64-v8a - cp -f ../arm64-v8a/susfsd ../manager/app/src/main/jniLibs/arm64-v8a/libsusfsd.so - - mkdir -p app/src/main/jniLibs/armeabi-v7a - cp -f ../armeabi-v7a/susfsd ../manager/app/src/main/jniLibs/armeabi-v7a/libsusfsd.so - - mkdir -p app/src/main/jniLibs/x86_64 - cp -f ../x86_64/susfsd ../manager/app/src/main/jniLibs/x86_64/libsusfsd.so - - - - name: Download arm64 ksud_overlayfs - uses: actions/download-artifact@v4 - with: - name: ksud_overlayfs-aarch64-linux-android - path: ksud_overlayfs - - - name: Download arm ksud_overlayfs - uses: actions/download-artifact@v4 - with: - name: ksud_overlayfs-armv7-linux-androideabi - path: ksud_overlayfs - - - name: Download x86_64 ksud_overlayfs - uses: actions/download-artifact@v4 - with: - name: ksud_overlayfs-x86_64-linux-android - path: ksud_overlayfs - - - name: Copy ksud_overlayfs to app jniLibs - run: | - cp -f ../ksud_overlayfs/aarch64-linux-android/release/ksud ../manager/app/src/main/jniLibs/arm64-v8a/libksud_overlayfs.so - - cp -f ../ksud_overlayfs/armv7-linux-androideabi/release/ksud ../manager/app/src/main/jniLibs/armeabi-v7a/libksud_overlayfs.so - - cp -f ../ksud_overlayfs/x86_64-linux-android/release/ksud ../manager/app/src/main/jniLibs/x86_64/libksud_overlayfs.so - - - name: Download arm64 ksud_magic - uses: actions/download-artifact@v4 - with: - name: ksud_magic-aarch64-linux-android - path: ksud_magic - - - name: Download arm ksud_magic - uses: actions/download-artifact@v4 - with: - name: ksud_magic-armv7-linux-androideabi - path: ksud_magic - - - name: Download x86_64 ksud_magic - uses: actions/download-artifact@v4 - with: - name: ksud_magic-x86_64-linux-android - path: ksud_magic - - - name: Copy ksud_magic to app jniLibs - run: | - cp -f ../ksud_magic/aarch64-linux-android/release/ksud ../manager/app/src/main/jniLibs/arm64-v8a/libksud_magic.so - - cp -f ../ksud_magic/armv7-linux-androideabi/release/ksud ../manager/app/src/main/jniLibs/armeabi-v7a/libksud_magic.so - - cp -f ../ksud_magic/x86_64-linux-android/release/ksud ../manager/app/src/main/jniLibs/x86_64/libksud_magic.so - - - name: Build with Gradle - run: | - { - echo 'org.gradle.parallel=true' - echo 'org.gradle.vfs.watch=true' - echo 'org.gradle.jvmargs=-Xmx2048m' - echo 'android.native.buildOutput=verbose' - } >> gradle.properties - sed -i 's/org.gradle.configuration-cache=true//g' gradle.properties - chmod +x gradlew - ./gradlew clean assembleRelease - - - name: Upload Build Artifact - uses: actions/upload-artifact@v4 - with: - name: Manager - path: manager/app/build/outputs/apk/release/*.apk - - - name: Upload Mappings - uses: actions/upload-artifact@v4 - with: - name: Mappings - path: manager/app/build/outputs/mapping/release/ - - - name: Bot Session Cache - if: steps.need_upload.outputs.UPLOAD == 'true' - id: bot_session_cache - uses: actions/cache@v4 - with: - path: scripts/ksunextbot.session - key: ${{ runner.os }}-bot-session - - - name: Upload to Telegram - if: steps.need_upload.outputs.UPLOAD == 'true' - env: - API_ID: ${{ secrets.API_ID }} - API_HASH: ${{ secrets.API_HASH }} - 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 }} - TITLE: CI Manager (SPOOFED BUILD) - run: | - if [ ! -z "${{ secrets.BOT_TOKEN }}" ]; then - export VERSION=$(git rev-list --count HEAD) - APK=$(find ./app/build/outputs/apk/release -name "*.apk") - pip3 install telethon - python3 $GITHUB_WORKSPACE/scripts/ksunextbot.py $APK - fi diff --git a/.github/workflows/build-manager.yml b/.github/workflows/build-manager.yml deleted file mode 100644 index c40a756f..00000000 --- a/.github/workflows/build-manager.yml +++ /dev/null @@ -1,215 +0,0 @@ -name: Build Manager - -on: - # push: - # branches: [ "next" ] - # paths: - # - '.github/workflows/build-manager.yml' - # - 'manager/**' - # - 'kernel/**' - # - 'userspace/ksud_overlayfs**' - # - 'userspace/ksud_magic/**' - # - 'userspace/susfsd/**' - # pull_request: - # branches: [ "next" ] - # paths: - # - '.github/workflows/build-manager-ci.yml' - # - 'manager/**' - workflow_call: - workflow_dispatch: - -jobs: - build-lkm: - uses: ./.github/workflows/build-lkm.yml - secrets: inherit - - build-susfsd: - needs: build-lkm - strategy: - matrix: - include: - - os: ubuntu-latest - uses: ./.github/workflows/susfsd.yml - with: - os: ${{ matrix.os }} - - build-ksud: - needs: build-susfsd - strategy: - matrix: - include: - - target: aarch64-linux-android - os: ubuntu-latest - - target: armv7-linux-androideabi - os: ubuntu-latest - - target: x86_64-linux-android - os: ubuntu-latest - uses: ./.github/workflows/ksud.yml - with: - target: ${{ matrix.target }} - os: ${{ matrix.os }} - - build-manager: - needs: build-ksud - runs-on: ubuntu-latest - defaults: - run: - working-directory: ./manager - - steps: - - name: Checkout Repository - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - name: Setup need_upload - id: need_upload - run: | - if [ ! -z "${{ secrets.BOT_TOKEN }}" ]; then - echo "UPLOAD=true" >> $GITHUB_OUTPUT - else - echo "UPLOAD=false" >> $GITHUB_OUTPUT - fi - - - name: Write Key - run: | - if [ ! -z "${{ secrets.KEYSTORE }}" ]; then - { - echo KEYSTORE_PASSWORD='${{ secrets.KEYSTORE_PASSWORD }}' - echo KEY_ALIAS='${{ secrets.KEY_ALIAS }}' - echo KEY_PASSWORD='${{ secrets.KEY_PASSWORD }}' - echo KEYSTORE_FILE='key.jks' - } >> gradle.properties - echo ${{ secrets.KEYSTORE }} | base64 -d > key.jks - fi - - - name: Setup Java - uses: actions/setup-java@v4 - with: - distribution: temurin - java-version: 21 - - - name: Setup Gradle - uses: gradle/actions/setup-gradle@v4 - - - name: Download susfsd - uses: actions/download-artifact@v4 - with: - name: susfsd-linux-android - path: . - - - name: Copy susfsd to app jniLibs - run: | - mkdir -p app/src/main/jniLibs/arm64-v8a - cp -f ../arm64-v8a/susfsd ../manager/app/src/main/jniLibs/arm64-v8a/libsusfsd.so - - mkdir -p app/src/main/jniLibs/armeabi-v7a - cp -f ../armeabi-v7a/susfsd ../manager/app/src/main/jniLibs/armeabi-v7a/libsusfsd.so - - mkdir -p app/src/main/jniLibs/x86_64 - cp -f ../x86_64/susfsd ../manager/app/src/main/jniLibs/x86_64/libsusfsd.so - - - - name: Download arm64 ksud_overlayfs - uses: actions/download-artifact@v4 - with: - name: ksud_overlayfs-aarch64-linux-android - path: ksud_overlayfs - - - name: Download arm ksud_overlayfs - uses: actions/download-artifact@v4 - with: - name: ksud_overlayfs-armv7-linux-androideabi - path: ksud_overlayfs - - - name: Download x86_64 ksud_overlayfs - uses: actions/download-artifact@v4 - with: - name: ksud_overlayfs-x86_64-linux-android - path: ksud_overlayfs - - - name: Copy ksud_overlayfs to app jniLibs - run: | - cp -f ../ksud_overlayfs/aarch64-linux-android/release/ksud ../manager/app/src/main/jniLibs/arm64-v8a/libksud_overlayfs.so - - cp -f ../ksud_overlayfs/armv7-linux-androideabi/release/ksud ../manager/app/src/main/jniLibs/armeabi-v7a/libksud_overlayfs.so - - cp -f ../ksud_overlayfs/x86_64-linux-android/release/ksud ../manager/app/src/main/jniLibs/x86_64/libksud_overlayfs.so - - - name: Download arm64 ksud_magic - uses: actions/download-artifact@v4 - with: - name: ksud_magic-aarch64-linux-android - path: ksud_magic - - - name: Download arm ksud_magic - uses: actions/download-artifact@v4 - with: - name: ksud_magic-armv7-linux-androideabi - path: ksud_magic - - - name: Download x86_64 ksud_magic - uses: actions/download-artifact@v4 - with: - name: ksud_magic-x86_64-linux-android - path: ksud_magic - - - name: Copy ksud_magic to app jniLibs - run: | - cp -f ../ksud_magic/aarch64-linux-android/release/ksud ../manager/app/src/main/jniLibs/arm64-v8a/libksud_magic.so - - cp -f ../ksud_magic/armv7-linux-androideabi/release/ksud ../manager/app/src/main/jniLibs/armeabi-v7a/libksud_magic.so - - cp -f ../ksud_magic/x86_64-linux-android/release/ksud ../manager/app/src/main/jniLibs/x86_64/libksud_magic.so - - - name: Build with Gradle - run: | - { - echo 'org.gradle.parallel=true' - echo 'org.gradle.vfs.watch=true' - echo 'org.gradle.jvmargs=-Xmx2048m' - echo 'android.native.buildOutput=verbose' - } >> gradle.properties - sed -i 's/org.gradle.configuration-cache=true//g' gradle.properties - chmod +x gradlew - ./gradlew clean assembleRelease - - - name: Upload Build Artifact - uses: actions/upload-artifact@v4 - with: - name: Manager - path: manager/app/build/outputs/apk/release/*.apk - - - name: Upload Mappings - uses: actions/upload-artifact@v4 - with: - name: Mappings - path: manager/app/build/outputs/mapping/release/ - - - name: Bot Session Cache - if: steps.need_upload.outputs.UPLOAD == 'true' - id: bot_session_cache - uses: actions/cache@v4 - with: - path: scripts/ksunextbot.session - key: ${{ runner.os }}-bot-session - - - name: Upload to Telegram - if: steps.need_upload.outputs.UPLOAD == 'true' - env: - API_ID: ${{ secrets.API_ID }} - API_HASH: ${{ secrets.API_HASH }} - 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 }} - TITLE: Release Manager (STABLE BUILD) - run: | - if [ ! -z "${{ secrets.BOT_TOKEN }}" ]; then - export VERSION=$(git rev-list --count HEAD) - APK=$(find ./app/build/outputs/apk/release -name "*.apk") - pip3 install telethon - python3 $GITHUB_WORKSPACE/scripts/ksunextbot.py $APK - fi diff --git a/.github/workflows/build-su.yml b/.github/workflows/build-su.yml deleted file mode 100644 index ee2b66c5..00000000 --- a/.github/workflows/build-su.yml +++ /dev/null @@ -1,36 +0,0 @@ -name: Build SU -on: - push: - branches: [ "next" ] - paths: - - '.github/workflows/build-su.yml' - - 'userspace/su/**' - - 'scripts/ksunextbot.py' - pull_request: - branches: [ "next" ] - paths: - - 'userspace/su/**' -jobs: - build-su: - name: Build userspace su - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - name: Setup need_upload - id: need_upload - run: | - if [ ! -z "${{ secrets.BOT_TOKEN }}" ]; then - echo "UPLOAD=true" >> $GITHUB_OUTPUT - else - echo "UPLOAD=false" >> $GITHUB_OUTPUT - fi - - name: Build su - working-directory: ./userspace/su - run: $ANDROID_NDK/ndk-build - - name: Upload a Build Artifact - uses: actions/upload-artifact@v4 - with: - name: su - path: ./userspace/su/libs diff --git a/.github/workflows/clippy.yml b/.github/workflows/clippy.yml deleted file mode 100644 index 234d4886..00000000 --- a/.github/workflows/clippy.yml +++ /dev/null @@ -1,54 +0,0 @@ -name: Clippy check - -on: - push: - branches: - - next - paths: - - '.github/workflows/clippy.yml' - - 'userspace/ksud_magic/**' - - 'userspace/ksud_overlayfs/**' - pull_request: - branches: - - next - paths: - - '.github/workflows/clippy.yml' - - 'userspace/ksud_magic/**' - - 'userspace/ksud_overlayfs/**' - -env: - RUSTFLAGS: '-Dwarnings' - -jobs: - clippy: - runs-on: ubuntu-latest - steps: - - name: Checkout Repository - uses: actions/checkout@v4 - - - name: Setup Rust - run: rustup update stable - - - name: Setup Cross - run: RUSTFLAGS="" cargo install cross - - - name: Cache ksud_overlayfs - uses: Swatinem/rust-cache@v2 - with: - workspaces: userspace/ksud_overlayfs - - - name: Cache ksud_magic - uses: Swatinem/rust-cache@v2 - with: - workspaces: userspace/ksud_magic - - - name: Run Clippy - run: | - cross clippy --manifest-path userspace/ksud_magic/Cargo.toml --target aarch64-linux-android --release - cross clippy --manifest-path userspace/ksud_overlayfs/Cargo.toml --target aarch64-linux-android --release - - cross clippy --manifest-path userspace/ksud_magic/Cargo.toml --target armv7-linux-androideabi --release - cross clippy --manifest-path userspace/ksud_overlayfs/Cargo.toml --target armv7-linux-androideabi --release - - cross clippy --manifest-path userspace/ksud_magic/Cargo.toml --target x86_64-linux-android --release - cross clippy --manifest-path userspace/ksud_overlayfs/Cargo.toml --target x86_64-linux-android --release \ No newline at end of file diff --git a/.github/workflows/gki-kernel.yml b/.github/workflows/gki-kernel.yml deleted file mode 100644 index 0baddffb..00000000 --- a/.github/workflows/gki-kernel.yml +++ /dev/null @@ -1,261 +0,0 @@ -name: GKI Kernel Build - -on: - workflow_call: - inputs: - version: - required: true - type: string - description: > - Output directory of gki, - for example: android12-5.10 - version_name: - required: true - type: string - description: > - With SUBLEVEL of kernel, - for example: android12-5.10.66 - tag: - required: true - type: string - description: > - Part of branch name of common kernel manifest, - for example: android12-5.10-2021-11 - os_patch_level: - required: false - type: string - description: > - Patch level of common kernel manifest, - for example: 2021-11 - default: 2022-05 - patch_path: - required: false - type: string - description: > - Directory name of .github/patches/ - for example: 5.10 - use_cache: - required: false - type: boolean - default: true - embed_ksud: - required: false - type: string - default: ksud-aarch64-linux-android - description: > - Artifact name of prebuilt ksud to be embedded - for example: ksud-aarch64-linux-android - debug: - required: false - type: boolean - default: false - build_lkm: - required: false - type: boolean - default: false - secrets: - BOOT_SIGN_KEY: - required: false - CHAT_ID: - required: false - BOT_TOKEN: - required: false - MESSAGE_THREAD_ID: - required: false - -jobs: - build: - name: Build ${{ inputs.version_name }} - runs-on: ubuntu-latest - env: - CCACHE_COMPILERCHECK: "%compiler% -dumpmachine; %compiler% -dumpversion" - CCACHE_NOHASHDIR: "true" - CCACHE_HARDLINK: "true" - steps: - - name: Maximize build space - uses: easimon/maximize-build-space@master - with: - root-reserve-mb: 8192 - temp-reserve-mb: 2048 - remove-dotnet: 'true' - remove-android: 'true' - remove-haskell: 'true' - remove-codeql: 'true' - - - uses: actions/checkout@v4 - with: - path: KernelSU-Next - fetch-depth: 0 - - - name: Setup need_upload - id: need_upload - run: | - if [ ! -z "${{ secrets.BOT_TOKEN }}" ]; then - echo "UPLOAD=true" >> $GITHUB_OUTPUT - else - echo "UPLOAD=false" >> $GITHUB_OUTPUT - fi - - - name: Setup kernel source - run: | - echo "Free space:" - df -h - cd $GITHUB_WORKSPACE - sudo apt-get install repo -y - mkdir android-kernel && cd android-kernel - repo init --depth=1 --u https://android.googlesource.com/kernel/manifest -b common-${{ inputs.tag }} --repo-rev=v2.16 - REMOTE_BRANCH=$(git ls-remote https://android.googlesource.com/kernel/common ${{ inputs.tag }}) - DEFAULT_MANIFEST_PATH=.repo/manifests/default.xml - if grep -q deprecated <<< $REMOTE_BRANCH; then - echo "Found deprecated branch: ${{ inputs.tag }}" - sed -i 's/"${{ inputs.tag }}"/"deprecated\/${{ inputs.tag }}"/g' $DEFAULT_MANIFEST_PATH - cat $DEFAULT_MANIFEST_PATH - fi - repo --version - repo --trace sync -c -j$(nproc --all) --no-tags - df -h - - - name: Setup KernelSU-Next - env: - PATCH_PATH: ${{ inputs.patch_path }} - IS_DEBUG_KERNEL: ${{ inputs.debug }} - run: | - cd $GITHUB_WORKSPACE/android-kernel - echo "[+] KernelSU-Next setup" - GKI_ROOT=$(pwd) - echo "[+] GKI_ROOT: $GKI_ROOT" - echo "[+] Copy KernelSU-Next driver to $GKI_ROOT/common/drivers" - ln -sf $GITHUB_WORKSPACE/KernelSU-Next/kernel $GKI_ROOT/common/drivers/kernelsu - echo "[+] Add KernelSU-Next driver to Makefile" - DRIVER_MAKEFILE=$GKI_ROOT/common/drivers/Makefile - DRIVER_KCONFIG=$GKI_ROOT/common/drivers/Kconfig - grep -q "kernelsu" "$DRIVER_MAKEFILE" || printf "\nobj-\$(CONFIG_KSU) += kernelsu/\n" >> "$DRIVER_MAKEFILE" - grep -q "kernelsu" "$DRIVER_KCONFIG" || sed -i "/endmenu/i\\source \"drivers/kernelsu/Kconfig\"" "$DRIVER_KCONFIG" - echo "[+] Apply Compilation Patches" - if [ ! -e build/build.sh ]; then - GLIBC_VERSION=$(ldd --version 2>/dev/null | head -n 1 | awk '{print $NF}') - echo "GLIBC_VERSION: $GLIBC_VERSION" - if [ "$(printf '%s\n' "2.38" "$GLIBC_VERSION" | sort -V | head -n1)" = "2.38" ]; then - echo "Patching resolve_btfids/Makefile" - cd $GKI_ROOT/common/ && sed -i '/\$(Q)\$(MAKE) -C \$(SUBCMD_SRC) OUTPUT=\$(abspath \$(dir \$@))\/ \$(abspath \$@)/s//$(Q)$(MAKE) -C $(SUBCMD_SRC) EXTRA_CFLAGS="$(CFLAGS)" OUTPUT=$(abspath $(dir $@))\/ $(abspath $@)/' tools/bpf/resolve_btfids/Makefile || echo "No patch needed." - fi - fi - - if [ "$IS_DEBUG_KERNEL" = "true" ]; then - echo "[+] Enable debug features for kernel" - printf "\nccflags-y += -DCONFIG_KSU_DEBUG\n" >> $GITHUB_WORKSPACE/KernelSU-Next/kernel/Makefile - fi - repo status - echo "[+] KernelSU-Next setup done." - - - name: Symbol magic - run: | - echo "[+] Export all symbol from abi_gki_aarch64.xml" - COMMON_ROOT=$GITHUB_WORKSPACE/android-kernel/common - KSU_ROOT=$GITHUB_WORKSPACE/KernelSU-Next - ABI_XML=$COMMON_ROOT/android/abi_gki_aarch64.xml - SYMBOL_LIST=$COMMON_ROOT/android/abi_gki_aarch64 - # python3 $KSU_ROOT/scripts/abi_gki_all.py $ABI_XML > $SYMBOL_LIST - echo "[+] Add KernelSU-Next symbols" - cat $KSU_ROOT/kernel/export_symbol.txt | awk '{sub("[ \t]+","");print " "$0}' >> $SYMBOL_LIST - - - name: Setup ccache - if: inputs.use_cache == true - uses: hendrikmuhs/ccache-action@v1 - with: - key: gki-kernel-aarch64-${{ inputs.version_name }} - max-size: 2G - save: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }} - - - name: Setup for LKM - if: ${{ inputs.build_lkm == true }} - working-directory: android-kernel - run: | - pip install ast-grep-cli - sudo apt-get install llvm-15 -y - ast-grep -U -p '$$$ check_exports($$$) {$$$}' -r '' common/scripts/mod/modpost.c - ast-grep -U -p 'check_exports($$$);' -r '' common/scripts/mod/modpost.c - sed -i '/config KSU/,/help/{s/default y/default m/}' common/drivers/kernelsu/Kconfig - echo "drivers/kernelsu/kernelsu.ko" >> common/android/gki_aarch64_modules - - # bazel build, android14-5.15, android14-6.1 use bazel - if [ ! -e build/build.sh ]; then - sed -i 's/needs unknown symbol/Dont abort when unknown symbol/g' build/kernel/*.sh || echo "No unknown symbol scripts found" - if [ -e common/modules.bzl ]; then - sed -i 's/_COMMON_GKI_MODULES_LIST = \[/_COMMON_GKI_MODULES_LIST = \[ "drivers\/kernelsu\/kernelsu.ko",/g' common/modules.bzl - fi - else - TARGET_FILE="build/kernel/build.sh" - if [ ! -e "$TARGET_FILE" ]; then - TARGET_FILE="build/build.sh" - fi - sed -i 's/needs unknown symbol/Dont abort when unknown symbol/g' $TARGET_FILE || echo "No unknown symbol in $TARGET_FILE" - sed -i 's/if ! diff -u "\${KERNEL_DIR}\/\${MODULES_ORDER}" "\${OUT_DIR}\/modules\.order"; then/if false; then/g' $TARGET_FILE - sed -i 's@${ROOT_DIR}/build/abi/compare_to_symbol_list@echo@g' $TARGET_FILE - sed -i 's/needs unknown symbol/Dont abort when unknown symbol/g' build/kernel/*.sh || echo "No unknown symbol scripts found" - fi - - - name: Make working directory clean to avoid dirty - working-directory: android-kernel - run: | - if [ -f ./common/BUILD.bazel ]; then - sed -i '/^[[:space:]]*"protected_exports_list"[[:space:]]*:[[:space:]]*"android\/abi_gki_protected_exports_aarch64",$/d' ./common/BUILD.bazel - fi - rm common/android/abi_gki_protected_exports_* || echo "No protected exports!" - git config --global user.email "bot@kernelsu.org" - git config --global user.name "KernelSU-NextBot" - cd common/ && git add -A && git commit -a -m "Add KernelSU-Next" - repo status - - - name: Build Kernel/LKM - working-directory: android-kernel - run: | - if [ ! -z ${{ vars.EXPECTED_SIZE }} ] && [ ! -z ${{ vars.EXPECTED_HASH }} ]; then - export KSU_EXPECTED_SIZE=${{ vars.EXPECTED_SIZE }} - export KSU_EXPECTED_HASH=${{ vars.EXPECTED_HASH }} - fi - if [ -e build/build.sh ]; then - LTO=thin BUILD_CONFIG=common/build.config.gki.aarch64 build/build.sh CC="/usr/bin/ccache clang" - else - tools/bazel run --disk_cache=/home/runner/.cache/bazel --config=fast --config=stamp --lto=thin //common:kernel_aarch64_dist -- --dist_dir=dist - fi - - - name: Prepare artifacts - id: prepareArtifacts - run: | - OUTDIR=android-kernel/out/${{ inputs.version }}/dist - if [ ! -e $OUTDIR ]; then - OUTDIR=android-kernel/dist - fi - mkdir output - if [ "${{ inputs.build_lkm}}" = "true" ]; then - llvm-strip-15 -d $OUTDIR/kernelsu.ko - mv $OUTDIR/kernelsu.ko ./output/${{ inputs.version }}_kernelsu.ko - else - cp $OUTDIR/Image ./output/ - cp $OUTDIR/Image.lz4 ./output/ - git clone https://github.com/Kernel-SU/AnyKernel3 - rm -rf ./AnyKernel3/.git - cp $OUTDIR/Image ./AnyKernel3/ - fi - - - name: Upload Image and Image.gz - uses: actions/upload-artifact@v4 - if: ${{ inputs.build_lkm == false }} - with: - name: Image-${{ inputs.version_name }}_${{ inputs.os_patch_level }} - path: ./output/* - - - name: Upload AnyKernel3 - if: ${{ inputs.build_lkm == false }} - uses: actions/upload-artifact@v4 - with: - name: AnyKernel3-${{ inputs.version_name }}_${{ inputs.os_patch_level }} - path: ./AnyKernel3/* - - - name: Upload LKM - uses: actions/upload-artifact@v4 - if: ${{ inputs.build_lkm == true }} - with: - name: ${{ inputs.version }}-lkm - path: ./output/*_kernelsu.ko diff --git a/.github/workflows/ksud.yml b/.github/workflows/ksud.yml deleted file mode 100644 index c4ab0c27..00000000 --- a/.github/workflows/ksud.yml +++ /dev/null @@ -1,84 +0,0 @@ -name: Build ksud -on: - workflow_call: - inputs: - target: - required: true - type: string - os: - required: false - type: string - default: ubuntu-latest - pack_lkm: - required: false - type: boolean - default: true - use_cache: - required: false - type: boolean - default: true -jobs: - build: - runs-on: ${{ inputs.os }} - steps: - - name: Checkout Repository - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - name: Download Artifacts - uses: actions/download-artifact@v4 - - - name: Prepare LKM Files - if: ${{ inputs.pack_lkm }} - run: | - cp android*-lkm/*_kernelsu.ko ./userspace/ksud_overlayfs/bin/aarch64/ - cp android*-lkm/*_kernelsu.ko ./userspace/ksud_magic/bin/aarch64/ - - - name: Import susfsd Libraries - run: | - cp susfsd-linux-android/arm64-v8a/susfsd ./userspace/ksud_overlayfs/bin/aarch64/ - cp susfsd-linux-android/arm64-v8a/susfsd ./userspace/ksud_magic/bin/aarch64/ - cp susfsd-linux-android/armeabi-v7a/susfsd ./userspace/ksud_overlayfs/bin/arm/ - cp susfsd-linux-android/armeabi-v7a/susfsd ./userspace/ksud_magic/bin/arm/ - cp susfsd-linux-android/x86_64/susfsd ./userspace/ksud_overlayfs/bin/x86_64/ - cp susfsd-linux-android/x86_64/susfsd ./userspace/ksud_magic/bin/x86_64/ - - - name: Setup Rust - run: | - rustup update stable - rustup target add x86_64-apple-darwin - rustup target add aarch64-apple-darwin - - - name: Cache ksud_overlayfs - uses: Swatinem/rust-cache@v2 - with: - workspaces: userspace/ksud_overlayfs - cache-targets: false - - - name: Cache ksud_magic - uses: Swatinem/rust-cache@v2 - with: - workspaces: userspace/ksud_magic - cache-targets: false - - - name: Setup Cross - run: | - RUSTFLAGS="" cargo install cross --git https://github.com/cross-rs/cross --rev 66845c1 --force - - - name: Build ksud - run: | - CROSS_NO_WARNINGS=0 cross build --target ${{ inputs.target }} --release --manifest-path ./userspace/ksud_overlayfs/Cargo.toml - CROSS_NO_WARNINGS=0 cross build --target ${{ inputs.target }} --release --manifest-path ./userspace/ksud_magic/Cargo.toml - - - name: Upload ksud_overlayfs artifact - uses: actions/upload-artifact@v4 - with: - name: ksud_overlayfs-${{ inputs.target }} - path: userspace/ksud_overlayfs/target/**/release/ksud* - - - name: Upload ksud_magic artifact - uses: actions/upload-artifact@v4 - with: - name: ksud_magic-${{ inputs.target }} - path: userspace/ksud_magic/target/**/release/ksud* diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml deleted file mode 100644 index 6a07085c..00000000 --- a/.github/workflows/release.yml +++ /dev/null @@ -1,85 +0,0 @@ -name: Release -on: - push: - tags: - - "v*" - workflow_dispatch: - -jobs: - build-manager: - uses: ./.github/workflows/build-manager.yml - secrets: inherit - build-a12-kernel: - uses: ./.github/workflows/build-kernel-a12.yml - secrets: inherit - build-a13-kernel: - uses: ./.github/workflows/build-kernel-a13.yml - secrets: inherit - 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 - build-arcvm-kernel: - uses: ./.github/workflows/build-kernel-arcvm.yml - secrets: inherit - release: - needs: - - build-manager - - build-a12-kernel - - build-a13-kernel - - build-a14-kernel - - build-a15-kernel - - build-wsa-kernel - - build-arcvm-kernel - runs-on: ubuntu-latest - steps: - - name: Download artifacts - uses: actions/download-artifact@v4 - - name: Zip AnyKernel3 - run: | - for dir in AnyKernel3-*; do - if [ -d "$dir" ]; then - echo "----- Zip $dir -----" - (cd $dir && zip -r9 "$dir".zip ./* -x .git .gitignore ./*.zip && mv *.zip ..) - fi - done - - - name: Zip WSA kernel - run: | - for dir in kernel-WSA-*; do - if [ -d "$dir" ]; then - echo "------ Zip $dir ----------" - (cd $dir && zip -r9 "$dir".zip ./* -x .git .gitignore ./*.zip && mv *.zip ..) - fi - done - - - name: Zip ChromeOS ARCVM kernel - run: | - for dir in kernel-ARCVM-*; do - if [ -d "$dir" ]; then - echo "------ Zip $dir ----------" - (cd $dir && zip -r9 "$dir".zip ./* -x .git .gitignore ./*.zip && mv *.zip ..) - fi - done - - - name: Display structure of downloaded files - run: ls -R - - - name: release - uses: softprops/action-gh-release@v2 - with: - files: | - Manager/*.apk - android*-lkm/*_kernelsu.ko - AnyKernel3-*.zip - boot-images-*/Image-*/*.img.gz - kernel-WSA*.zip - kernel-ARCVM*.zip - ksud_magic*.zip - ksud_overlayfs*.zip - susfsd*.zip \ No newline at end of file diff --git a/.github/workflows/rustfmt.yml b/.github/workflows/rustfmt.yml deleted file mode 100644 index c24a157c..00000000 --- a/.github/workflows/rustfmt.yml +++ /dev/null @@ -1,40 +0,0 @@ -name: Rustfmt check - -on: - push: - branches: - - 'next' - paths: - - '.github/workflows/rustfmt.yml' - - 'userspace/ksud_magic/**' - - 'userspace/ksud_overlayfs/**' - pull_request: - branches: - - 'next' - paths: - - '.github/workflows/rustfmt.yml' - - 'userspace/ksud_magic/**' - - 'userspace/ksud_overlayfs/**' - -permissions: - checks: write - -jobs: - format: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - - uses: dtolnay/rust-toolchain@nightly - with: - components: rustfmt - - - uses: LoliGothick/rustfmt-check@master - with: - token: ${{ github.token }} - working-directory: userspace/ksud_magic - - - uses: LoliGothick/rustfmt-check@master - with: - token: ${{ github.token }} - working-directory: userspace/ksud_overlayfs \ No newline at end of file diff --git a/.github/workflows/shellcheck.yml b/.github/workflows/shellcheck.yml deleted file mode 100644 index 3ba9c74f..00000000 --- a/.github/workflows/shellcheck.yml +++ /dev/null @@ -1,27 +0,0 @@ -name: ShellCheck - -on: - push: - branches: - - 'next' - paths: - - '.github/workflows/shellcheck.yml' - - '**/*.sh' - pull_request: - branches: - - 'next' - paths: - - '.github/workflows/shellcheck.yml' - - '**/*.sh' - -jobs: - shellcheck: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - - name: Run ShellCheck - uses: ludeeus/action-shellcheck@2.0.0 - with: - ignore_names: gradlew - ignore_paths: ./userspace/ksud_magic/src/installer.sh ./userspace/ksud_overlayfs/src/installer.sh \ No newline at end of file diff --git a/.github/workflows/susfsd.yml b/.github/workflows/susfsd.yml deleted file mode 100644 index 9a0003a2..00000000 --- a/.github/workflows/susfsd.yml +++ /dev/null @@ -1,31 +0,0 @@ -name: Build susfsd aarch64 -on: - push: - branches: [ "next" ] - paths: - - '.github/workflows/susfsd.yml' - - 'userspace/susfsd/**' - workflow_dispatch: - workflow_call: - inputs: - os: - required: false - type: string - default: ubuntu-latest -jobs: - build-susfs: - name: Build userspace susfsd - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - name: Build susfsd - working-directory: ./userspace/susfsd - run: $ANDROID_NDK/ndk-build - - name: Upload Build Artifacts - uses: actions/upload-artifact@v4 - with: - name: susfsd-linux-android - path: ./userspace/susfsd/libs - diff --git a/.github/workflows/wsa-kernel.yml b/.github/workflows/wsa-kernel.yml deleted file mode 100644 index b4bd4407..00000000 --- a/.github/workflows/wsa-kernel.yml +++ /dev/null @@ -1,106 +0,0 @@ -name: Build Kernel - WSA -on: - workflow_call: - inputs: - arch: - required: true - type: string - description: > - Build arch: x86_64 / arm64 - version: - required: true - type: string - description: > - Build version -jobs: - build: - name: Build WSA-Kernel-${{ inputs.version }}-${{ inputs.arch }} - runs-on: ubuntu-22.04 - env: - CCACHE_COMPILERCHECK: "%compiler% -dumpmachine; %compiler% -dumpversion" - CCACHE_NOHASHDIR: "true" - CCACHE_HARDLINK: "true" - - steps: - - name: Install Build Tools - uses: awalsh128/cache-apt-pkgs-action@v1 - with: - packages: bc bison build-essential flex libelf-dev binutils-aarch64-linux-gnu gcc-aarch64-linux-gnu gzip ccache - version: 1.0 - - - name: Cache LLVM - id: cache-llvm - uses: actions/cache@v4 - with: - path: ./llvm - key: llvm-12.0.1 - - - name: Setup LLVM - uses: KyleMayes/install-llvm-action@v1 - with: - version: "12.0.1" - force-version: true - ubuntu-version: "16.04" - cached: ${{ steps.cache-llvm.outputs.cache-hit }} - - - name: Checkout KernelSU-Next - uses: actions/checkout@v4 - with: - path: KernelSU-Next - fetch-depth: 0 - - - name: Setup kernel source - uses: actions/checkout@v4 - with: - repository: microsoft/WSA-Linux-Kernel - ref: android-lts/latte-2/${{ inputs.version }} - path: WSA-Linux-Kernel - - - name: Setup Ccache - uses: hendrikmuhs/ccache-action@v1 - with: - key: WSA-Kernel-${{ inputs.version }}-${{ inputs.arch }} - save: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }} - max-size: 2G - - - name: Setup KernelSU-Next - working-directory: WSA-Linux-Kernel - run: | - echo "[+] KernelSU-Next setup" - KERNEL_ROOT=$GITHUB_WORKSPACE/WSA-Linux-Kernel - echo "[+] KERNEL_ROOT: $KERNEL_ROOT" - echo "[+] Copy KernelSU-Next driver to $KERNEL_ROOT/drivers" - ln -sf $GITHUB_WORKSPACE/KernelSU-Next/kernel $KERNEL_ROOT/drivers/kernelsu - echo "[+] Add KernelSU-Next driver to Makefile" - DRIVER_MAKEFILE=$KERNEL_ROOT/drivers/Makefile - DRIVER_KCONFIG=$KERNEL_ROOT/drivers/Kconfig - grep -q "kernelsu" "$DRIVER_MAKEFILE" || printf "\nobj-\$(CONFIG_KSU) += kernelsu/\n" >> "$DRIVER_MAKEFILE" - grep -q "kernelsu" "$DRIVER_KCONFIG" || sed -i "/endmenu/i\\source \"drivers/kernelsu/Kconfig\"" "$DRIVER_KCONFIG" - echo "[+] Apply KernelSU-Next patches" - cd $KERNEL_ROOT && git apply $GITHUB_WORKSPACE/KernelSU-Next/.github/patches/5.15/*.patch || echo "[-] No patch found" - echo "[+] KernelSU-Next setup done." - cd $GITHUB_WORKSPACE/KernelSU-Next - VERSION=$(($(git rev-list --count HEAD) + 10200)) - echo "VERSION: $VERSION" - echo "kernelsu_version=$VERSION" >> $GITHUB_ENV - - - name: Build Kernel - working-directory: WSA-Linux-Kernel - run: | - if [ ! -z ${{ vars.EXPECTED_SIZE }} ] && [ ! -z ${{ vars.EXPECTED_HASH }} ]; then - export KSU_EXPECTED_SIZE=${{ vars.EXPECTED_SIZE }} - export KSU_EXPECTED_HASH=${{ vars.EXPECTED_HASH }} - fi - declare -A ARCH_MAP=(["x86_64"]="x64" ["arm64"]="arm64") - cp configs/wsa/config-wsa-${ARCH_MAP[${{ inputs.arch }}]} .config - make olddefconfig - declare -A FILE_NAME=(["x86_64"]="bzImage" ["arm64"]="Image") - make -j`nproc` LLVM=1 ARCH=${{ inputs.arch }} $(if [ "${{ inputs.arch }}" == "arm64" ]; then echo CROSS_COMPILE=aarch64-linux-gnu; fi) ${FILE_NAME[${{ inputs.arch }}]} CCACHE="/usr/bin/ccache" - declare -A ARCH_MAP_FILE=(["x86_64"]="x86" ["arm64"]="arm64") - echo "file_path=WSA-Linux-Kernel/arch/${ARCH_MAP_FILE[${{ inputs.arch }}]}/boot/${FILE_NAME[${{ inputs.arch }}]}" >> $GITHUB_ENV - - - name: Upload kernel-${{ inputs.arch }}-${{ inputs.version }} - uses: actions/upload-artifact@v4 - with: - name: kernel-WSA-${{ inputs.arch }}-${{ inputs.version }} - path: "${{ env.file_path }}" diff --git a/SECURITY.md b/SECURITY.md deleted file mode 100644 index 83040d98..00000000 --- a/SECURITY.md +++ /dev/null @@ -1,7 +0,0 @@ -# Reporting Security Issues - -The KernelSU team and community take security bugs in KernelSU seriously. We appreciate your efforts to responsibly disclose your findings, and will make every effort to acknowledge your contributions. - -To report a security issue, please use the GitHub Security Advisory ["Report a Vulnerability"](https://github.com/tiann/KernelSU/security/advisories/new) tab, or you can mailto [weishu](mailto:twsxtd@gmail.com) directly. - -The KernelSU team will send a response indicating the next steps in handling your report. After the initial reply to your report, the security team will keep you informed of the progress towards a fix and full announcement, and may ask for additional information or guidance. diff --git a/assets/kernelsu_next.png b/assets/kernelsu_next.png deleted file mode 100644 index 30efeeb6..00000000 Binary files a/assets/kernelsu_next.png and /dev/null differ diff --git a/crowdin.yml b/crowdin.yml deleted file mode 100644 index 7cdcf76f..00000000 --- a/crowdin.yml +++ /dev/null @@ -1,2 +0,0 @@ -bundles: - - 8 diff --git a/docs/README.md b/docs/README.md deleted file mode 100644 index eb832932..00000000 --- a/docs/README.md +++ /dev/null @@ -1,91 +0,0 @@ -**English** | [简体中文](README_CN.md) | [繁體中文](README_TW.md) | [Türkçe](README_TR.md) | [Português (Brasil)](README_PT-BR.md) | [한국어](README_KO.md) | [Français](README_FR.md) | [Bahasa Indonesia](README_ID.md) | [Русский](README_RU.md) | [Українська](README_UA.md) | [ภาษาไทย](README_TH.md) | [Tiếng Việt](README_VI.md) | [Italiano](README_IT.md) | [Polski](README_PL.md) | [Български](README_BG.md) | [日本語](README_JA.md) | [Español](README_ES.md) - ---- - -
- KernelSU Next Logo - -

KernelSU Next

-

A kernel-based root solution for Android devices.

- -

- - Latest Release - - - Nightly Build - - - License: GPL v2 - - - GitHub License - - -

-
- ---- - -## 🚀 Features - -- Kernel-based `su` and root access management. -- Module system based on [Magic Mount](https://topjohnwu.github.io/Magisk/details.html#magic-mount) and [OverlayFS](https://en.wikipedia.org/wiki/OverlayFS). -- [App Profile](https://kernelsu.org/guide/app-profile.html): Limit root privileges per app. - ---- - -## ✅ Compatibility - -KernelSU Next supports Android kernels from **4.4 up to 6.6**: - -| Kernel version | Support notes | -|----------------------|-------------------------------------------------------------------------| -| 5.10+ (GKI 2.0) | Supports pre-built images and LKM/KMI | -| 4.19 – 5.4 (GKI 1.0) | Requires KernelSU driver built-in | -| < 4.14 (EOL) | Requires KernelSU driver (3.18+ is experimental and may need backports) | - -**Supported architectures:** `arm64-v8a`, `armeabi-v7a` and `x86_64` - ---- - -## 📦 Installation - -Please refer to the [Installation](https://kernelsu-next.github.io/webpage/pages/installation.html) guide for setup instructions. - ---- - -## 🔐 Security - -To report security issues, please see [SECURITY.md](/SECURITY.md). - ---- - -## 📜 License - -- **`/kernel` directory:** [GPL-2.0-only](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html). -- **All other files:** [GPL-3.0-or-later](https://www.gnu.org/licenses/gpl-3.0.html). - ---- - -## 💸 Donations - -If you’d like to support the project: - -- **USDT (BEP20, ERC20)**: `0x12b5224b7aca0121c2f003240a901e1d064371c1` -- **USDT (TRC20)**: `TYUVMWGTcnR5svnDoX85DWHyqUAeyQcdjh` -- **ETH (ERC20)**: `0x12b5224b7aca0121c2f003240a901e1d064371c1` -- **LTC**: `Ld238uYBuRQdZB5YwdbkuU6ektBAAUByoL` -- **BTC**: `19QgifcjMjSr1wB2DJcea5cxitvWVcXMT6` - ---- - -## 🙏 Credits - -- [Kernel-Assisted Superuser](https://git.zx2c4.com/kernel-assisted-superuser/about/) – Concept inspiration -- [Magisk](https://github.com/topjohnwu/Magisk) – Core root implementation -- [Genuine](https://github.com/brevent/genuine/) – APK v2 signature validation -- [Diamorphine](https://github.com/m0nad/Diamorphine) – Rootkit techniques -- [KernelSU](https://github.com/tiann/KernelSU) – The original base that made KernelSU Next possible -- [Magic Mount Port](https://github.com/5ec1cff/KernelSU/blob/main/userspace/ksud/src/magic_mount.rs) – 💜 to 5ec1cff for keeping KernelSU alive -- [Crowdin Translators](https://crowdin.com/project/kernelsu-next/members) – 💬 Thanks to everyone for helping make KernelSU Next multi-lingual! diff --git a/docs/README_BG.md b/docs/README_BG.md deleted file mode 100644 index 331df7d6..00000000 --- a/docs/README_BG.md +++ /dev/null @@ -1,58 +0,0 @@ -[English](README.md) | [简体中文](README_CN.md) | [繁體中文](README_TW.md) | [Türkçe](README_TR.md) | [Português (Brasil)](README_PT-BR.md) | [한국어](README_KO.md) | [Français](README_FR.md) | [Bahasa Indonesia](README_ID.md) | [Русский](README_RU.md) | [Український](README_UA.md) | [ภาษาไทย](README_TH.md) | [Tiếng Việt](README_VI.md) | [Italiano](README_IT.md) | [Polski](README_PL.md) | **Български** | [日本語](README_JA.md) - -# KernelSU Next - -лого - -Ядрено решение за root достъп за Android устройства. - -[![Последна версия](https://img.shields.io/github/v/release/KernelSU-Next/KernelSU-Next?label=Версия&logo=github)](https://github.com/KernelSU-Next/KernelSU-Next/releases/latest) -[![Нощна версия](https://img.shields.io/badge/Нощна_версия-сива?logo=hackthebox&logoColor=fff)](https://nightly.link/KernelSU-Next/KernelSU-Next/workflows/build-manager-ci/next/Manager) -[![Лиценз: GPL v2](https://img.shields.io/badge/Лиценз-GPL%20v2-оранжев.svg?logo=gnu)](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html) -[![Лиценз в GitHub](https://img.shields.io/github/license/KernelSU-Next/KernelSU-Next?logo=gnu)](/LICENSE) - -## Възможности - -1. Управление на `su` и root достъп на ядрено ниво -2. Система за модули базирана на [Magic Mount](https://topjohnwu.github.io/Magisk/details.html#magic-mount) / [OverlayFS](https://en.wikipedia.org/wiki/OverlayFS) -3. [Профили за приложения](https://kernelsu.org/guide/app-profile.html): Ограничаване на root права за конкретни приложения - -## Съвместимост - -KernelSU Next официално поддържа повечето Android ядра от версия 4.4 до 6.6: -- Ядра GKI 2.0 (5.10+) могат да използват предварително компилирани изображения и LKM/KMI -- Ядра GKI 1.0 (4.19 - 5.4) изискват прекомпилиране с драйвера на KernelSU -- Остарели ядра (<4.14) също изискват прекомпилиране (3.18+ е експериментална поддръжка) - -В момента се поддържа само архитектурата `arm64-v8a`, `armeabi-v7a` & `x86_64`. - -## Инсталация - -- [Инструкции за инсталиране](https://ksunext.org/pages/installation.html) - -## Сигурност - -За докладване на уязвимости вижте [SECURITY.md](/SECURITY.md). - -## Лиценз - -- Файловете в директорията `kernel` са [GPL-2.0-only](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html) -- Всички останали файлове са [GPL-3.0-or-later](https://www.gnu.org/licenses/gpl-3.0.html) - -## Дарения - -- 0x12b5224b7aca0121c2f003240a901e1d064371c1 [ USDT BEP20 ] -- TYUVMWGTcnR5svnDoX85DWHyqUAeyQcdjh [ USDT TRC20 ] -- 0x12b5224b7aca0121c2f003240a901e1d064371c1 [ USDT ERC20 ] -- 0x12b5224b7aca0121c2f003240a901e1d064371c1 [ ETH ERC20 ] -- Ld238uYBuRQdZB5YwdbkuU6ektBAAUByoL [ LTC ] -- 19QgifcjMjSr1wB2DJcea5cxitvWVcXMT6 [ BTC ] - -## Благодарности - -- [Kernel-Assisted Superuser](https://git.zx2c4.com/kernel-assisted-superuser/about/): Идеята за KernelSU -- [Magisk](https://github.com/topjohnwu/Magisk): Мощният root инструмент -- [genuine](https://github.com/brevent/genuine/): Валидация на APK подписи v2 -- [Diamorphine](https://github.com/m0nad/Diamorphine): Rootkit техники -- [KernelSU](https://github.com/tiann/KernelSU): Благодарности към tiann за създаването на KernelSU -- [Magic Mount Port](https://github.com/5ec1cff/KernelSU/blob/main/userspace/ksud/src/magic_mount.rs): 💜 5ec1cff за спасяването на KernelSU diff --git a/docs/README_CN.md b/docs/README_CN.md deleted file mode 100644 index 43e3777e..00000000 --- a/docs/README_CN.md +++ /dev/null @@ -1,90 +0,0 @@ -[English](README.md) | **简体中文** | [繁體中文](README_TW.md) | [Türkçe](README_TR.md) | [Português (Brasil)](README_PT-BR.md) | [한국어](README_KO.md) | [Français](README_FR.md) | [Bahasa Indonesia](README_ID.md) | [Русский](README_RU.md) | [Український](README_UA.md) | [ภาษาไทย](README_TH.md) | [Tiếng Việt](README_VI.md) | [Italiano](README_IT.md) | [Polski](README_PL.md) | [Български](README_BG.md) | [日本語](README_JA.md) - ---- - -
- KernelSU Next Logo - -

KernelSU Next

-

安卓设备基于内核的 Root 方案

- -

- - Latest Release - - - Nightly Build - - - License: GPL v2 - - - GitHub License - -

-
- ---- - -## 🚀 特性 - -- 基于内核的 `su` 和超级用户权限管理 -- 动态挂载系统基于 **[Magic Mount](https://topjohnwu.github.io/Magisk/details.html#magic-mount)** 以及 **[OverlayFS](https://en.wikipedia.org/wiki/OverlayFS)** -- [App Profile](https://kernelsu.org/zh_CN/guide/app-profile.html):把 Root 权限关进笼子里 - ---- - -## ✅ 兼容性 - -KernelSU Next 支持从 **4.4 到 6.6** 的大多数安卓内核 - -| 内核版本 | 支持情况 | -|----------------|---------------| -| 5.10+ (GKI 2.0) | 可运行预置镜像和 LKM/KMI | -| 4.19 – 5.4 (GKI 1.0) | 需要使用 KernelSU 内核驱动重新编译 | -| <4.14 (EOL) | 需要使用 KernelSU 内核驱动重新编译(3.18+ 的版本处于试验阶段,可能需要进行回溯移植) | - -**支持的架构:** -`arm64-v8a`、`armeabi-v7a`、`x86_64` - ---- - -## 📦 安装 - -请遵循该[安装说明](https://kernelsu-next.github.io/webpage/zh_CN/pages/installation.html)进行操作。 - ---- - -## 🔐 安全性 - -有关报告 KernelSU Next 漏洞的信息,请参阅 [SECURITY.md](/SECURITY.md). - ---- - -## 📜 许可 - -- **目录 `/kernel` 下所有文件**为 [GPL-2.0-only](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html) -- **`/kernel` 目录以外的其他部分**均为 [GPL-3.0-or-later](https://www.gnu.org/licenses/gpl-3.0.html) - ---- - -## 💸 捐赠 - -如果你喜欢这个项目还请支持: - -- **USDT (BEP20, ERC20)**: `0x12b5224b7aca0121c2f003240a901e1d064371c1` -- **USDT (TRC20)**: `TYUVMWGTcnR5svnDoX85DWHyqUAeyQcdjh` -- **ETH (ERC20)**: `0x12b5224b7aca0121c2f003240a901e1d064371c1` -- **LTC**: `Ld238uYBuRQdZB5YwdbkuU6ektBAAUByoL` -- **BTC**: `19QgifcjMjSr1wB2DJcea5cxitvWVcXMT6` - ---- - -## 🙏 鸣谢 - -- [Kernel-Assisted Superuser](https://git.zx2c4.com/kernel-assisted-superuser/about/):KernelSU 的灵感. -- [Magisk](https://github.com/topjohnwu/Magisk):强大的 Root 工具. -- [genuine](https://github.com/brevent/genuine/):APK v2 签名验证。 -- [Diamorphine](https://github.com/m0nad/Diamorphine):一些 Rootkit 技巧。 -- [KernelSU](https://github.com/tiann/KernelSU):感谢 tiann,否则 KernelSU Next 根本不会存在。 -- [Magic Mount Port](https://github.com/5ec1cff/KernelSU/blob/main/userspace/ksud/src/magic_mount.rs):💜 5ec1cff 为了拯救 KernelSU! diff --git a/docs/README_ES.md b/docs/README_ES.md deleted file mode 100644 index 7d168d9b..00000000 --- a/docs/README_ES.md +++ /dev/null @@ -1,89 +0,0 @@ -**English** | [简体中文](README_CN.md) | [繁體中文](README_TW.md) | [Türkçe](README_TR.md) | [Português (Brasil)](README_PT-BR.md) | [한국어](README_KO.md) | [Français](README_FR.md) | [Bahasa Indonesia](README_ID.md) | [Русский](README_RU.md) | [Українська](README_UA.md) | [ภาษาไทย](README_TH.md) | [Tiếng Việt](README_VI.md) | [Italiano](README_IT.md) | [Polski](README_PL.md) | [Български](README_BG.md) | [日本語](README_JA.md) | [Español](README_ES.md) - ---- - -
- KernelSU Next Logo - -

KernelSU Next

-

Una solución de root basada en el kernel para tus dispositivos Android.

- -

- - Latest Release - - - Nightly Build - - - License: GPL v2 - - - GitHub License - -

-
- ---- - -## 🚀 Características - -- `su` y gestión de acceso root basados en el kernel. -- Sistema de módulos basado en [Magic Mount](https://topjohnwu.github.io/Magisk/details.html#magic-mount) y [OverlayFS](https://en.wikipedia.org/wiki/OverlayFS). -- [Perfil de Aplicación](https://kernelsu.org/guide/app-profile.html): Limita los privilegios de root por aplicación. - ---- - -## ✅ Compatibilidad - -KernelSU Next es compatible con kernels de Android desde la versión **4.4 hasta la 6.6**: - -| Kernel version | Support notes | -|----------------------|-----------------------------------------------------------------------------------| -| 5.10+ (GKI 2.0) | Admite imágenes precompiladas y LKM/KMI | -| 4.19 – 5.4 (GKI 1.0) | Requiere que el driver de KernelSU esté integrado | -| < 4.14 (EOL) | Requiere el driver de KernelSU (3.18+ es experimental y puede necesitar backports | - -**Arquitecturas compatibles: ** `arm64-v8a`, `armeabi-v7a` y `x86_64` - ---- - -## 📦 Instalación - -Por favor, consulta la guía de [Instalación](https://kernelsu-next.github.io/webpage/pages/installation.html) para ver las instrucciones de configuración. - ---- - -## 🔐 Seguridad - -Para informar sobre problemas de seguridad, por favor, consulta [SECURITY.md](/SECURITY.md). - ---- - -## 📜 Licencia - -- **Directorio `/kernel`:** [Solo-GPL-2.0](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html). -- **Todos los demás archivos:** [GPL-3.0-o-superior](https://www.gnu.org/licenses/gpl-3.0.html). - ---- - -## 💸 Donaciones - -Si te gustaría apoyar el proyecto: - -- **USDT (BEP20, ERC20)**: `0x12b5224b7aca0121c2f003240a901e1d064371c1` -- **USDT (TRC20)**: `TYUVMWGTcnR5svnDoX85DWHyqUAeyQcdjh` -- **ETH (ERC20)**: `0x12b5224b7aca0121c2f003240a901e1d064371c1` -- **LTC**: `Ld238uYBuRQdZB5YwdbkuU6ektBAAUByoL` -- **BTC**: `19QgifcjMjSr1wB2DJcea5cxitvWVcXMT6` - ---- - -## 🙏 Créditos - -- [Kernel-Assisted Superuser](https://git.zx2c4.com/kernel-assisted-superuser/about/) – Inspiración para el concepto -- [Magisk](https://github.com/topjohnwu/Magisk) – Implementación principal del root -- [Genuine](https://github.com/brevent/genuine/) – Validación de la firma v2 de los APK -- [Diamorphine](https://github.com/m0nad/Diamorphine) – Técnicas de rootkit -- [KernelSU](https://github.com/tiann/KernelSU) – La base original que hizo posible KernelSU Next -- [Magic Mount Port](https://github.com/5ec1cff/KernelSU/blob/main/userspace/ksud/src/magic_mount.rs) – 💜 a 5ec1cff por mantener vivo KernelSU \ No newline at end of file diff --git a/docs/README_FR.md b/docs/README_FR.md deleted file mode 100644 index 8e069d1c..00000000 --- a/docs/README_FR.md +++ /dev/null @@ -1,49 +0,0 @@ -[English](README.md) | [简体中文](README_CN.md) | [繁體中文](README_TW.md) | [Türkçe](README_TR.md) | [Português (Brasil)](README_PT-BR.md) | [한국어](README_KO.md) | **Français** | [Bahasa Indonesia](README_ID.md) | [Русский](README_RU.md) | [Український](README_UA.md) | [ภาษาไทย](README_TH.md) | [Tiếng Việt](README_VI.md) | [Italiano](README_IT.md) | [Polski](README_PL.md) | [Български](README_BG.md) | [日本語](README_JA.md) - -# KernelSU Next - -logo - -Une solution root basée sur le noyau pour les appareils Android. - -[![Latest Release](https://img.shields.io/github/v/release/KernelSU-Next/KernelSU-Next?label=Release&logo=github)](https://github.com/KernelSU-Next/KernelSU-Next/releases/latest) -[![Nightly Release](https://img.shields.io/badge/Nightly%20Release-gray?logo=hackthebox&logoColor=fff)](https://nightly.link/KernelSU-Next/KernelSU-Next/workflows/build-manager-ci/next/Manager) -[![License: 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) -[![GitHub License](https://img.shields.io/github/license/KernelSU-Next/KernelSU-Next?logo=gnu)](/LICENSE) - -## Fonctionnalités - -1. Gestion des accès root et de la commande `su` basée sur le noyau. -2. Système de modules basé sur le système de montage dynamique [Magic Mount](https://topjohnwu.github.io/Magisk/details.html#magic-mount) / [OverlayFS](https://en.wikipedia.org/wiki/OverlayFS). -3. [Profil d'application](https://kernelsu.org/guide/app-profile.html) : Enfermez la puissance du root dans une cage. - -## État de compatibilité - -KernelSU Next prend officiellement en charge la plupart des noyaux Android de la version 4.4 à la version 6.6. - - Les noyaux GKI 2.0 (5.10+) peuvent exécuter des images pré-construites et des modules LKM/KMI. - - Les noyaux GKI 1.0 (4.19 - 5.4) doivent être reconstruits avec le pilote KernelSU. - - Les noyaux EOL (<4.14) doivent également être reconstruits avec le pilote KernelSU (3.18+ est expérimental et peut nécessiter des rétroportages fonctionnels). - -Actuellement, seul `arm64-v8a`, `armeabi-v7a` & `x86_64` est pris en charge. - -## Utilisation - -- [Instructions d'installation](https://ksunext.org/pages/installation.html) - -## Sécurité - -Pour signaler des vulnérabilités de sécurité dans KernelSU, consultez [SECURITY.md](/SECURITY.md). - -## Licence - -- Les fichiers du répertoire `kernel` sont sous licence [GPL-2.0-only](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html). -- Toutes les autres parties, sauf le répertoire `kernel`, sont sous licence [GPL-3.0-or-later](https://www.gnu.org/licenses/gpl-3.0.html). - -## Crédits - -- [Kernel-Assisted Superuser](https://git.zx2c4.com/kernel-assisted-superuser/about/) : L'idée de KernelSU. -- [Magisk](https://github.com/topjohnwu/Magisk) : L'outil root puissant. -- [genuine](https://github.com/brevent/genuine/) : Validation de signature APK v2. -- [Diamorphine](https://github.com/m0nad/Diamorphine) : Quelques techniques de rootkit. -- [KernelSU](https://github.com/tiann/KernelSU) : Merci à tiann, sans qui KernelSU Next n'existerait même pas. -- [Magic Mount Port](https://github.com/5ec1cff/KernelSU/blob/main/userspace/ksud/src/magic_mount.rs) : 💜 5ec1cff pour avoir sauvé KernelSU ! diff --git a/docs/README_ID.md b/docs/README_ID.md deleted file mode 100644 index 54243be1..00000000 --- a/docs/README_ID.md +++ /dev/null @@ -1,49 +0,0 @@ -[English](README.md) | [简体中文](README_CN.md) | [繁體中文](README_TW.md) | [Türkçe](README_TR.md) | [Português (Brasil)](README_PT-BR.md) | [한국어](README_KO.md) | [Français](README_FR.md) | **Bahasa Indonesia** | [Русский](README_RU.md) | [Український](README_UA.md) | [ภาษาไทย](README_TH.md) | [Tiếng Việt](README_VI.md) | [Italiano](README_IT.md) | [Polski](README_PL.md) | [Български](README_BG.md) | [日本語](README_JA.md) - -# KernelSU Next - -logo - -Sebuah solusi root berbasis Kernel untuk perangkat Android. - -[![Latest Release](https://img.shields.io/github/v/release/KernelSU-Next/KernelSU-Next?label=Release&logo=github)](https://github.com/KernelSU-Next/KernelSU-Next/releases/latest) -[![Nightly Release](https://img.shields.io/badge/Nightly%20Release-gray?logo=hackthebox&logoColor=fff)](https://nightly.link/KernelSU-Next/KernelSU-Next/workflows/build-manager-ci/next/Manager) -[![License: 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) -[![GitHub License](https://img.shields.io/github/license/KernelSU-Next/KernelSU-Next?logo=gnu)](/LICENSE) - -## Fitur - -1. Akses root dan manajemen `su` berbasis Kernel. -2. Sistem modul berbasis sistem mount dinamis [Magic Mount](https://topjohnwu.github.io/Magisk/details.html#magic-mount) / [OverlayFS](https://en.wikipedia.org/wiki/OverlayFS). -3. [Profil Aplikasi](https://kernelsu.org/guide/app-profile.html): Mengunci kekuatan root dalam sebuah kandang. - -## Status Kompatibilitas - -KernelSU Next secara resmi mendukung sebagian besar kernel Android mulai dari 4.4 hingga 6.6. - - Kernel GKI 2.0 (5.10+) dapat menjalankan gambar yang telah dibangun sebelumnya dan LKM/KMI. - - Kernel GKI 1.0 (4.19 - 5.4) perlu dibangun ulang dengan driver KernelSU. - - Kernel EOL (<4.14) juga perlu dibangun ulang dengan driver KernelSU (3.18+ bersifat eksperimental dan mungkin memerlukan beberapa backport fungsi). - -Saat ini, hanya `arm64-v8a`, `armeabi-v7a` & `x86_64` yang didukung. - -## Penggunaan - -- [Petunjuk instalasi](https://ksunext.org/pages/installation.html) - -## Keamanan - -Untuk informasi tentang melaporkan kerentanannya di KernelSU, lihat [SECURITY.md](/SECURITY.md). - -## Lisensi - -- File di bawah direktori `kernel` menggunakan lisensi [GPL-2.0-only](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html). -- Semua bagian lain kecuali direktori `kernel` menggunakan lisensi [GPL-3.0-or-later](https://www.gnu.org/licenses/gpl-3.0.html). - -## Kredit - -- [Kernel-Assisted Superuser](https://git.zx2c4.com/kernel-assisted-superuser/about/): Ide KernelSU. -- [Magisk](https://github.com/topjohnwu/Magisk): Alat root yang kuat. -- [genuine](https://github.com/brevent/genuine/): Validasi tanda tangan APK v2. -- [Diamorphine](https://github.com/m0nad/Diamorphine): Beberapa keterampilan rootkit. -- [KernelSU](https://github.com/tiann/KernelSU): Terima kasih kepada tiann, jika tidak, KernelSU Next bahkan tidak akan ada. -- [Magic Mount Port](https://github.com/5ec1cff/KernelSU/blob/main/userspace/ksud/src/magic_mount.rs): 💜 5ec1cff karena menyelamatkan KernelSU! diff --git a/docs/README_IT.md b/docs/README_IT.md deleted file mode 100644 index bd321e6f..00000000 --- a/docs/README_IT.md +++ /dev/null @@ -1,63 +0,0 @@ -[English](README.md) | [简体中文](README_CN.md) | [繁體中文](README_TW.md) | [Türkçe](README_TR.md) | [Português (Brasil)](README_PT-BR.md) | [한국어](README_KO.md) | [Français](README_FR.md) | [Bahasa Indonesia](README_ID.md) | [Русский](README_RU.md) | [Український](README_UA.md) | [ภาษาไทย](README_TH.md) | [Tiếng Việt](README_VI.md) | **Italiano** | [Polski](README_PL.md) | [Български](README_BG.md) | [日本語](README_JA.md) - -# KernelSU Next - -logo - -Una soluzione root basata sul kernel per dispositivi Android. - -[![Latest Release](https://img.shields.io/github/v/release/KernelSU-Next/KernelSU-Next?label=Release&logo=github)](https://github.com/KernelSU-Next/KernelSU-Next/releases/latest) -[![Nightly Release](https://img.shields.io/badge/Nightly%20Release-gray?logo=hackthebox&logoColor=fff)](https://nightly.link/KernelSU-Next/KernelSU-Next/workflows/build-manager-ci/next/Manager) -[![License: 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) -[![GitHub License](https://img.shields.io/github/license/KernelSU-Next/KernelSU-Next?logo=gnu)](/LICENSE) - -## Caratteristiche - -1. Gestione degli accessi `su` e root basata sul kernel. -2. Sistema modulare basato sul sistema di montaggio dinamico [Magic Mount](https://topjohnwu.github.io/Magisk/details.html#magic-mount) / [OverlayFS](https://en.wikipedia.org/wiki/OverlayFS). -3. [App Profile](https://kernelsu.org/guide/app-profile.html): Rinchiudi il potere della radice in una gabbia. - -## Stato compatibilità - -KernelSU Next supporta ufficialmente la maggior parte dei kernel Android dalla versione 4.4 alla 6.6. - - I kernel GKI 2.0 (5.10+) possono eseguire immagini precostruite e LKM/KMI. - - I kernel GKI 1.0 (4.19 - 5.4) devono essere ricostruiti con il driver KernelSU. - - Anche i kernel EOL (<4.14) devono essere ricostruiti con il driver KernelSU (la versione 3.18+ è sperimentale e potrebbe richiedere alcuni backport di funzioni). - -Attualmente è supportata solo l'architettura `arm64-v8a`, `armeabi-v7a` & `x86_64`. - -## Utilizzo - -- [Istruzioni per l'installazione](https://ksunext.org/pages/installation.html) - -## Security - -Per informazioni sulla segnalazione delle vulnerabilità di sicurezza in KernelSU, vedere [SECURITY.md](/SECURITY.md). - -## Licenza - -- I file nella directory `kernel` sono [GPL-2.0-only](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html). -- Tutte le altre parti eccetto la directory `kernel` sono [GPL-3.0-or-later](https://www.gnu.org/licenses/gpl-3.0.html). - -## Donazioni - -- 0x12b5224b7aca0121c2f003240a901e1d064371c1 [ USDT BEP20 ] - -- TYUVMWGTcnR5svnDoX85DWHyqUAeyQcdjh [ USDT TRC20 ] - -- 0x12b5224b7aca0121c2f003240a901e1d064371c1 [ USDT ERC20 ] - -- 0x12b5224b7aca0121c2f003240a901e1d064371c1 [ ETH ERC20 ] - -- Ld238uYBuRQdZB5YwdbkuU6ektBAAUByoL [ LTC ] - -- 19QgifcjMjSr1wB2DJcea5cxitvWVcXMT6 [ BTC ] - -## Crediti - -- [Kernel-Assisted Superuser](https://git.zx2c4.com/kernel-assisted-superuser/about/): L'idea di KernelSU. -- [Magisk](https://github.com/topjohnwu/Magisk): Il potente strumento di root. -- [genuine](https://github.com/brevent/genuine/): Convalida della firma APK v2. -- [Diamorphine](https://github.com/m0nad/Diamorphine): Alcune competenze sui rootkit. -- [KernelSU](https://github.com/tiann/KernelSU): Grazie a tiann, altrimenti KernelSU Next non esisterebbe nemmeno. -- [Magic Mount Port](https://github.com/5ec1cff/KernelSU/blob/main/userspace/ksud/src/magic_mount.rs): 💜 5ec1cff per aver salvato KernelSU! diff --git a/docs/README_JA.md b/docs/README_JA.md deleted file mode 100644 index 8abe264c..00000000 --- a/docs/README_JA.md +++ /dev/null @@ -1,63 +0,0 @@ -[English](README.md) | [简体中文](README_CN.md) | [繁體中文](README_TW.md) | [Türkçe](README_TR.md) | [Português (Brasil)](README_PT-BR.md) | [한국어](README_KO.md) | [Français](README_FR.md) | [Bahasa Indonesia](README_ID.md) | [Русский](README_RU.md) | [Український](README_UA.md) | [ภาษาไทย](README_TH.md) | [Tiếng Việt](README_VI.md) | [Italiano](README_IT.md) | [Polski](README_PL.md) | [Български](README_BG.md) | **日本語** - -# KernelSU Next - -logo - -Android デバイス用のカーネルベースな root ソリューション。 - -[![Latest Release](https://img.shields.io/github/v/release/KernelSU-Next/KernelSU-Next?label=Release&logo=github)](https://github.com/KernelSU-Next/KernelSU-Next/releases/latest) -[![Nightly Release](https://img.shields.io/badge/Nightly%20Release-gray?logo=hackthebox&logoColor=fff)](https://nightly.link/KernelSU-Next/KernelSU-Next/workflows/build-manager-ci/next/Manager) -[![License: 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) -[![GitHub License](https://img.shields.io/github/license/KernelSU-Next/KernelSU-Next?logo=gnu)](/LICENSE) - -## 機能 - -1. カーネルベースの `su` および root アクセスの管理。 -2. 動的マウントシステム [Magic Mount](https://topjohnwu.github.io/Magisk/details.html#magic-mount) / [OverlayFS](https://en.wikipedia.org/wiki/OverlayFS) をベースとしたモジュールシステム。 -3. [アプリプロファイル](https://kernelsu.org/guide/app-profile.html): root 権限をケージに閉じ込めます。 - -## 互換性の状態 - -KernelSU Next は 4.4 から 6.6 までのほとんどの Android カーネルを公式でサポートしています。 - - GKI 2.0 (5.10 以降) のカーネルはビルド済みイメージで LKM/KMI を実行できます。 - - GKI 1.0 (4.19 - 5.4) のカーネルは、KernelSU ドライバを使用してビルドする必要があります。 - - EOL (4.14 未満) のカーネルも KernelSU ドライバを使用して再ビルドする必要があります (3.18 以降は実験中の段階であり、一部の関数のバックポートが必要になる場合があります)。 - -現在 `arm64-v8a`, `armeabi-v7a` & `x86_64` アーキテクチャのみをサポートしています。 - -## 使い方 - -- [インストール手順](https://ksunext.org/pages/installation.html) - -## セキュリティ - -KernelSU のセキュリティ脆弱性の報告については [SECURITY.md](/SECURITY.md) を参照してください。 - -## ライセンス - -- `kernel` ディレクトリ内のファイルは [GPL-2.0](https://www.gnu.org/licenses/old-licenses/gpl-2.0.ja.html) のみライセンス下にあります。 -- `kernel` ディレクトリを除くその他すべての部分は [GPL-3.0 またはそれ以降](https://www.gnu.org/licenses/gpl-3.0.html) のライセンス下にあります。 - -## 寄付 - -- 0x12b5224b7aca0121c2f003240a901e1d064371c1 [ USDT BEP20 ] - -- TYUVMWGTcnR5svnDoX85DWHyqUAeyQcdjh [ USDT TRC20 ] - -- 0x12b5224b7aca0121c2f003240a901e1d064371c1 [ USDT ERC20 ] - -- 0x12b5224b7aca0121c2f003240a901e1d064371c1 [ ETH ERC20 ] - -- Ld238uYBuRQdZB5YwdbkuU6ektBAAUByoL [ LTC ] - -- 19QgifcjMjSr1wB2DJcea5cxitvWVcXMT6 [ BTC ] - -## クレジット - -- [Kernel-Assisted Superuser](https://git.zx2c4.com/kernel-assisted-superuser/about/): KernelSU のアイデアを考案。 -- [Magisk](https://github.com/topjohnwu/Magisk): パワフルな root ツール。 -- [genuine](https://github.com/brevent/genuine/): APK v2 署名認証。 -- [Diamorphine](https://github.com/m0nad/Diamorphine): いくつかの rootkit スキル。 -- [KernelSU](https://github.com/tiann/KernelSU): tiann に感謝を申し上げます。これが存在しなければ KernelSU Next は存在しませんでした。 -- [Magic Mount Port](https://github.com/5ec1cff/KernelSU/blob/main/userspace/ksud/src/magic_mount.rs): 💜 5ec1cff へ KernelSU を救ってくれてありがとう! diff --git a/docs/README_KO.md b/docs/README_KO.md deleted file mode 100644 index a8f7fbfd..00000000 --- a/docs/README_KO.md +++ /dev/null @@ -1,49 +0,0 @@ -[English](README.md) | [简体中文](README_CN.md) | [繁體中文](README_TW.md) | [Türkçe](README_TR.md) | [Português (Brasil)](README_PT-BR.md) | **한국어** | [Français](README_FR.md) | [Bahasa Indonesia](README_ID.md) | [Русский](README_RU.md) | [Український](README_UA.md) | [ภาษาไทย](README_TH.md) | [Tiếng Việt](README_VI.md) | [Italiano](README_IT.md) | [Polski](README_PL.md) | [Български](README_BG.md) | [日本語](README_JA.md) - -# KernelSU Next - -logo - -안드로이드 기기들을 위한 커널 기반 루팅 솔루션입니다. - -[![Latest Release](https://img.shields.io/github/v/release/KernelSU-Next/KernelSU-Next?label=Release&logo=github)](https://github.com/KernelSU-Next/KernelSU-Next/releases/latest) -[![Nightly Release](https://img.shields.io/badge/Nightly%20Release-gray?logo=hackthebox&logoColor=fff)](https://nightly.link/KernelSU-Next/KernelSU-Next/workflows/build-manager-ci/next/Manager) -[![License: 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) -[![GitHub License](https://img.shields.io/github/license/KernelSU-Next/KernelSU-Next?logo=gnu)](/LICENSE) - -## 기능 - -1. 커널 기반 `su` 및 루트 권한 관리 -2. 동적 마운트 시스템 기반 모듈 시스템 [Magic Mount](https://topjohnwu.github.io/Magisk/details.html#magic-mount) / [OverlayFS](https://en.wikipedia.org/wiki/OverlayFS). -3. [App Profile](https://kernelsu.org/guide/app-profile.html): 루트 권한 제한 - -## 호환 상태 - -KernelSU Next는 공식적으로 대부분의 4.4부터 6.6의 안드로이드 커널을 지원합니다. - - GKI 2.0 (5.10+) 커널은 미리 빌드된 이미지와 LKM/KMI를 지원합니다. - - GKI 1.0 (4.19 - 5.4) 커널은 KernelSU 드라이버로 다시 빌드해야 합니다. - - EOL (<4.14) 커널도 역시 KernelSU 드라이버로 다시 빌드해야 합니다.(3.18+는 실험적이며 일부 함수의 이식이 필요할 수 있습니다.). - -현재는, `arm64-v8a`, `armeabi-v7a` & `x86_64` 만 지원됩니다. - -## 사용 방법 - -- [설치 방법](https://ksunext.org/pages/installation.html) - -## 보안 - -KernelSU의 보안 취약점 보고에 대한 자세한 내용은 [SECURITY.md](/SECURITY.md)를 참조하세요. - -## 저작권 라이센스 - -- `kernel` 디렉터리의 파일은 [GPL-2.0전용](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html)입니다. -- `kernel` 디렉터리를 제외한 모든 파일은 [GPL-3.0-이상](https://www.gnu.org/licenses/gpl-3.0.html)입니다. - -## 크레딧 - -- [Kernel-Assisted Superuser](https://git.zx2c4.com/kernel-assisted-superuser/about/): KernelSU의 아이디어 -- [Magisk](https://github.com/topjohnwu/Magisk): 강력한 루팅 도구 -- [genuine](https://github.com/brevent/genuine/): APK v2 서명 검사 -- [Diamorphine](https://github.com/m0nad/Diamorphine): 일부 rootkit 기술 -- [KernelSU](https://github.com/tiann/KernelSU): KernelSU Next가 존재할 수 있게 해 준 tiann에게 감사드립니다. -- [Magic Mount Port](https://github.com/5ec1cff/KernelSU/blob/main/userspace/ksud/src/magic_mount.rs): KernelSU를 구해준 5ec1cff에게 감사드립니다! diff --git a/docs/README_PL.md b/docs/README_PL.md deleted file mode 100644 index 3e32ed40..00000000 --- a/docs/README_PL.md +++ /dev/null @@ -1,89 +0,0 @@ -[English](README.md) | [简体中文](README_CN.md) | [繁體中文](README_TW.md) | [Türkçe](README_TR.md) | [Português (Brasil)](README_PT-BR.md) | [한국어](README_KO.md) | [Français](README_FR.md) | [Bahasa Indonesia](README_ID.md) | [Русский](README_RU.md) | [Українська](README_UA.md) | [ภาษาไทย](README_TH.md) | [Tiếng Việt](README_VI.md) | [Italiano](README_IT.md) | **Polski** | [Български](README_BG.md) | [日本語](README_JA.md) - ---- - -
- KernelSU Next Logo - -

KernelSU Next

-

Bazujące na jądrze rozwiązanie root dla urządzeń z Androidem.

- -

- - Latest Release - - - Nightly Build - - - License: GPL v2 - - - GitHub License - -

-
- ---- - -## 🚀 Funkcjonalności - -- Oparte na jądrze `su` i zarządzanie dostępem do roota. -- System modułów oparty na [Magic Mount](https://topjohnwu.github.io/Magisk/details.html#magic-mount) i [OverlayFS](https://en.wikipedia.org/wiki/OverlayFS). -- [Profil aplikacji](https://kernelsu.org/guide/app-profile.html): Ograniczaj uprawnienia roota dla poszczególnych aplikacji. - ---- - -## ✅ Kompatybilność - -KernelSU Next obsługuje jądra Androida od wersji **4.4 do 6.6**: - -| Wersja jądra | Informacje techniczne | -|----------------------|-------------------------------------------------------------------------------------------| -| 5.10+ (GKI 2.0) | Obsługuje wstępnie skompilowane obrazy i LKM/KMI | -| 4.19 – 5.4 (GKI 1.0) | Wymaga wbudowania sterownika KernelSU | -| < 4.14 (EOL) | Wymaga sterownika KernelSU (obsługa 3.18+ jest eksperymentalna i może wymagać backportów) | - -**Obsługiwane architektury:** `arm64-v8a`, `armeabi-v7a` i `x86_64` - ---- - -## 📦 Instalacja - -Instrukcje dotyczące instalacji można znaleźć w przewodniku [Instalacja](https://kernelsu-next.github.io/webpage/pages/installation.html). - ---- - -## 🔐 Bezpieczeństwo - -Aby zgłosić problemy związane z bezpieczeństwem, zapoznaj się z [SECURITY.md](/SECURITY.md). - ---- - -## 📜 Licencje - -- **katalog `/kernel`:** [GPL-2.0-only](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html). -- **Wszystkie pozostałe pliki:** [GPL-3.0-or-later](https://www.gnu.org/licenses/gpl-3.0.html). - ---- - -## 💸 Darowizny - -Jeśli chciałbyś wesprzeć projekt: - -- **USDT (BEP20, ERC20)**: `0x12b5224b7aca0121c2f003240a901e1d064371c1` -- **USDT (TRC20)**: `TYUVMWGTcnR5svnDoX85DWHyqUAeyQcdjh` -- **ETH (ERC20)**: `0x12b5224b7aca0121c2f003240a901e1d064371c1` -- **LTC**: `Ld238uYBuRQdZB5YwdbkuU6ektBAAUByoL` -- **BTC**: `19QgifcjMjSr1wB2DJcea5cxitvWVcXMT6` - ---- - -## 🙏 Podziękowania - -- [Kernel-Assisted Superuser](https://git.zx2c4.com/kernel-assisted-superuser/about/) – Inspiracja konceptem -- [Magisk](https://github.com/topjohnwu/Magisk) – Bazowa implementacja roota -- [Genuine](https://github.com/brevent/genuine/) – Walidacja podpisu APK v2 -- [Diamorphine](https://github.com/m0nad/Diamorphine) – Techniki rootkit -- [KernelSU](https://github.com/tiann/KernelSU) – Oryginalna baza, która umożliwiła powstanie KernelSU Next -- [Magic Mount Port](https://github.com/5ec1cff/KernelSU/blob/main/userspace/ksud/src/magic_mount.rs) – 💜 dla 5ec1cff za utrzymanie KernelSU przy życiu diff --git a/docs/README_PT-BR.md b/docs/README_PT-BR.md deleted file mode 100644 index 9841f00b..00000000 --- a/docs/README_PT-BR.md +++ /dev/null @@ -1,89 +0,0 @@ -[English](README.md) | [简体中文](README_CN.md) | [繁體中文](README_TW.md) | [Türkçe](README_TR.md) | **Português (Brasil)** | [한국어](README_KO.md) | [Français](README_FR.md) | [Bahasa Indonesia](README_ID.md) | [Русский](README_RU.md) | [Український](README_UA.md) | [ภาษาไทย](README_TH.md) | [Tiếng Việt](README_VI.md) | [Italiano](README_IT.md) | [Polski](README_PL.md) | [Български](README_BG.md) | [日本語](README_JA.md) - ---- - -
- KernelSU Next Logo - -

KernelSU Next

-

Uma solução root baseada em kernel para dispositivos Android.

- -

- - Latest Release - - - Nightly Build - - - License: GPL v2 - - - GitHub License - -

-
- ---- - -## 🚀 Características - -- `su` e gerenciamento de acesso root baseado em kernel. -- Sistema de módulos baseado em [Magic Mount](https://topjohnwu.github.io/Magisk/details.html#magic-mount) e [OverlayFS](https://en.wikipedia.org/wiki/OverlayFS). -- [Perfil do app](https://kernelsu.org/pt_BR/guide/app-profile.html): Limitar privilégios root por app. - ---- - -## ✅ Compatibilidade - -O KernelSU Next oferece suporte a kernels Android **4.4 até 6.6**: - -| Versão do kernel | Notas de suporte | -|----------------------|-------------------------------------------------------------------------------| -| 5.10+ (GKI 2.0) | Suporta imagens pré-compiladas e LKM/KMI | -| 4.19 – 5.4 (GKI 1.0) | Requer driver do KernelSU integrado | -| < 4.14 (EOL) | Requer driver do KernelSU (3.18+ é experimental e pode precisar de backports) | - -**Arquiteturas suportadas:** `arm64-v8a`, `armeabi-v7a` e `x86_64` - ---- - -## 📦 Instalação - -Consulte o guia de [Instalação](https://kernelsu-next.github.io/webpage/pt_BR/pages/installation.html) para obter instruções de configuração. - ---- - -## 🔐 Segurança - -Para relatar problemas de segurança, consulte [SECURITY.md](/SECURITY.md). - ---- - -## 📜 Licença - -- **Diretório `/kernel`:** [GPL-2.0-only](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html). -- **Todos os outros arquivos:** [GPL-3.0-or-later](https://www.gnu.org/licenses/gpl-3.0.html). - ---- - -## 💸 Doações - -Se você quiser apoiar o projeto: - -- **USDT (BEP20, ERC20)**: `0x12b5224b7aca0121c2f003240a901e1d064371c1` -- **USDT (TRC20)**: `TYUVMWGTcnR5svnDoX85DWHyqUAeyQcdjh` -- **ETH (ERC20)**: `0x12b5224b7aca0121c2f003240a901e1d064371c1` -- **LTC**: `Ld238uYBuRQdZB5YwdbkuU6ektBAAUByoL` -- **BTC**: `19QgifcjMjSr1wB2DJcea5cxitvWVcXMT6` - ---- - -## 🙏 Créditos - -- [Kernel-Assisted Superuser](https://git.zx2c4.com/kernel-assisted-superuser/about/) – Inspiração do conceito -- [Magisk](https://github.com/topjohnwu/Magisk) – Implementação root principal -- [Genuine](https://github.com/brevent/genuine/) – Validação de assinatura APK v2 -- [Diamorphine](https://github.com/m0nad/Diamorphine) – Técnicas de rootkit -- [KernelSU](https://github.com/tiann/KernelSU) – A base original que tornou o KernelSU Next possível -- [Magic Mount Port](https://github.com/5ec1cff/KernelSU/blob/main/userspace/ksud/src/magic_mount.rs) – 💜 para 5ec1cff por manter o KernelSU vivo diff --git a/docs/README_RU.md b/docs/README_RU.md deleted file mode 100644 index 11ef6cce..00000000 --- a/docs/README_RU.md +++ /dev/null @@ -1,63 +0,0 @@ -[English](README.md) | [简体中文](README_CN.md) | [繁體中文](README_TW.md) | [Türkçe](README_TR.md) | [Português (Brasil)](README_PT-BR.md) | [한국어](README_KO.md) | [Français](README_FR.md) | [Bahasa Indonesia](README_ID.md) | **Русский** | [Український](README_UA.md) | [ภาษาไทย](README_TH.md) | [Tiếng Việt](README_VI.md) | [Italiano](README_IT.md) | [Polski](README_PL.md) | [Български](README_BG.md) | [日本語](README_JA.md) - -# KernelSU Next - -logo - -Root-решение для Android на базе ядра. - -[![Latest Release](https://img.shields.io/github/v/release/KernelSU-Next/KernelSU-Next?label=Release&logo=github)](https://github.com/KernelSU-Next/KernelSU-Next/releases/latest) -[![Nightly Release](https://img.shields.io/badge/Nightly%20Release-gray?logo=hackthebox&logoColor=fff)](https://nightly.link/KernelSU-Next/KernelSU-Next/workflows/build-manager-ci/next/Manager) -[![License: 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) -[![GitHub License](https://img.shields.io/github/license/KernelSU-Next/KernelSU-Next?logo=gnu)](/LICENSE) - -## Функции - -1. Реализация `su` и управление root-доступом прямо на уровне ядра. -2. Динамическая система модулей, построенная на [Magic Mount](https://topjohnwu.github.io/Magisk/details.html#magic-mount) / [OverlayFS](https://en.wikipedia.org/wiki/OverlayFS). -3. [Профиль для приложений](https://kernelsu.org/guide/app-profile.html): позволяет ограничить root-доступ в песочницу для отдельных приложений. - -## Совместимость - -KernelSU Next работает с большинством ядер Android (4.4 - 6.6): -- GKI 2.0 (5.10+) могут использовать предсобранные образы и LKM/KMI. -- GKI 1.0 (4.19 - 5.4) требуют пересборки с драйвером KernelSU. -- EOL (<4.14) также требуют пересборки с драйвером KernelSU (версии 3.18+ экспериментальные и могут потребовать некоторые функции бэкпортов). - -Сейчас поддерживается только `arm64-v8a`, `armeabi-v7a` & `x86_64`. - -## Использование - -- [Инструкция по установке](https://ksunext.org/pages/installation.html) - -## Безопасность - -Если нашли баг, посмотри [SECURITY.md](/SECURITY.md) — там гайд, как сообщить о проблеме. - -## Лицензия - -- Всё, что в директории `kernel`, — [GPL-2.0-only](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html). -- Остальной код, кроме директории `kernel`, под [GPL-3.0-or-later](https://www.gnu.org/licenses/gpl-3.0.html). - -## Донаты - -- 0x12b5224b7aca0121c2f003240a901e1d064371c1 [ USDT BEP20 ] - -- TYUVMWGTcnR5svnDoX85DWHyqUAeyQcdjh [ USDT TRC20 ] - -- 0x12b5224b7aca0121c2f003240a901e1d064371c1 [ USDT ERC20 ] - -- 0x12b5224b7aca0121c2f003240a901e1d064371c1 [ ETH ERC20 ] - -- Ld238uYBuRQdZB5YwdbkuU6ektBAAUByoL [ LTC ] - -- 19QgifcjMjSr1wB2DJcea5cxitvWVcXMT6 [ BTC ] - -## Благодарность - -- [Kernel-Assisted Superuser](https://git.zx2c4.com/kernel-assisted-superuser/about/): Идея KernelSU. -- [Magisk](https://github.com/topjohnwu/Magisk): Топовый инструмент для root. -- [genuine](https://github.com/brevent/genuine/): Валидация подписи APK v2. -- [Diamorphine](https://github.com/m0nad/Diamorphine): Некоторые навыки rootkit. -- [KernelSU](https://github.com/tiann/KernelSU): Спасибо tiann, без него KernelSU Next даже не существовал бы. -- [Magic Mount Port](https://github.com/5ec1cff/KernelSU/blob/main/userspace/ksud/src/magic_mount.rs): 💜 5ec1cff за сохранение KernelSU! diff --git a/docs/README_TH.md b/docs/README_TH.md deleted file mode 100644 index a46b856e..00000000 --- a/docs/README_TH.md +++ /dev/null @@ -1,63 +0,0 @@ -[English](README.md) | [简体中文](README_CN.md) | [繁體中文](README_TW.md) | [Türkçe](README_TR.md) | [Português (Brasil)](README_PT-BR.md) | [한국어](README_KO.md) | [Français](README_FR.md) | [Bahasa Indonesia](README_ID.md) | [Русский](README_RU.md) | [Український](README_UA.md) | **ภาษาไทย** | [Tiếng Việt](README_VI.md) | [Italiano](README_IT.md) | [Polski](README_PL.md) | [Български](README_BG.md) | [日本語](README_JA.md) - -# KernelSU Next - -logo - -โซลูชั่นรูทบนพื้นฐานเคอร์เนลสำหรับอุปกรณ์ Android - -[![Latest Release](https://img.shields.io/github/v/release/KernelSU-Next/KernelSU-Next?label=Release&logo=github)](https://github.com/KernelSU-Next/KernelSU-Next/releases/latest) -[![Nightly Release](https://img.shields.io/badge/Nightly%20Release-gray?logo=hackthebox&logoColor=fff)](https://nightly.link/KernelSU-Next/KernelSU-Next/workflows/build-manager-ci/next/Manager) -[![License: 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) -[![GitHub License](https://img.shields.io/github/license/KernelSU-Next/KernelSU-Next?logo=gnu)](/LICENSE) - -## คุณสมบัติ - -1. จัดการการเข้าถึงรูท และ `su` บนพื้นฐานเคอร์เนล -2. ระบบโมดูลแบบไดนามิกเมานต์ [Magic Mount](https://topjohnwu.github.io/Magisk/details.html#magic-mount) / [OverlayFS](https://en.wikipedia.org/wiki/OverlayFS) -3. [App Profile](https://kernelsu.org/guide/app-profile.html): จำกัดสิทธิ์เข้าถึงรูทสำหรับแอปต่างๆ - -## การเข้ากันในอุปกรณ์ต่างๆ - -KernelSU Next รองรับแบบเป็นทางการตั้งแต่เคอร์เนลแอนดรอยด์ 4.4 ถึง 6.6 - - GKI 2.0 (5.10+) เคอร์เนลสามารถรันไฟล์อิมเมจสำเร็จรูป และ LKM/KMI ได้ - - GKI 1.0 (4.19 - 5.4) เคอร์เนลจะต้องรีบิ้วร่วมกับไดร์เวอร์ของ KernelSU - - EOL (<4.14) เคอร์เนลก็ต้องรีบิ้วร่วมกับไดร์เวอร์ของ KernelSU เช่นกัน (3.18+ ยังเป็นเวอร์ชั่นทดลอง และยังต้องเขียนฟังก์ชั่นหลังบ้านเพิ่มเติม) - -ในขณะนี้, มีแค่สถาปัตยกรรม `arm64-v8a`, `armeabi-v7a` & `x86_64` ที่รองรับเท่านั้น - -## การใช้งาน - -- [คำแนะนำในการติดตั้ง](https://ksunext.org/pages/installation.html) - -## ความปลอดภัย - -สำหรับข้อมูลการรายงานช่องโหว่ด้านความปลอดภัยของ KernelSU โปรดดูที่ [SECURITY.md](/SECURITY.md). - -## สัญญาอนุญาต - -- ไฟล์ภายใต้โฟลเดอร์ `kernel` ถือว่าเป็นสัญญาอนุญาต [GPL-2.0-only](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html). -- ไฟล์ที่นอกเหนือจากโฟลเดอร์ `kernel` ถือว่าเป็นสัญญาอนุญาต [GPL-3.0-or-later](https://www.gnu.org/licenses/gpl-3.0.html). - -## การบริจาก - -- 0x12b5224b7aca0121c2f003240a901e1d064371c1 [ USDT BEP20 ] - -- TYUVMWGTcnR5svnDoX85DWHyqUAeyQcdjh [ USDT TRC20 ] - -- 0x12b5224b7aca0121c2f003240a901e1d064371c1 [ USDT ERC20 ] - -- 0x12b5224b7aca0121c2f003240a901e1d064371c1 [ ETH ERC20 ] - -- Ld238uYBuRQdZB5YwdbkuU6ektBAAUByoL [ LTC ] - -- 19QgifcjMjSr1wB2DJcea5cxitvWVcXMT6 [ BTC ] - -## เครดิต - -- [Kernel-Assisted Superuser](https://git.zx2c4.com/kernel-assisted-superuser/about/): ที่เป็นคนริเริ่มไอเดีย KernelSU -- [Magisk](https://github.com/topjohnwu/Magisk): เครื่องมือรูทที่ทรงพลัง -- [genuine](https://github.com/brevent/genuine/): การตรวจสอบลายเซ็น APK v2 -- [Diamorphine](https://github.com/m0nad/Diamorphine): ความรู้เกี่ยวกับ rootkit -- [KernelSU](https://github.com/tiann/KernelSU): ต้องขอบคุณ tiann ไม่งั้นจะไม่มี KernelSU ขึ้นมา -- [Magic Mount Port](https://github.com/5ec1cff/KernelSU/blob/main/userspace/ksud/src/magic_mount.rs): 💜 5ec1cff ที่ช่วย KernelSU เอาไว้! diff --git a/docs/README_TR.md b/docs/README_TR.md deleted file mode 100644 index d84dd59f..00000000 --- a/docs/README_TR.md +++ /dev/null @@ -1,89 +0,0 @@ -[English](README.md) | [简体中文](README_CN.md) | [繁體中文](README_TW.md) | **Türkçe** | [Português (Brasil)](README_PT-BR.md) | [한국어](README_KO.md) | [Français](README_FR.md) | [Bahasa Indonesia](README_ID.md) | [Русский](README_RU.md) | [Українська](README_UA.md) | [ภาษาไทย](README_TH.md) | [Tiếng Việt](README_VI.md) | [Italiano](README_IT.md) | [Polski](README_PL.md) | [Български](README_BG.md) | [日本語](README_JA.md) - ---- - -
- KernelSU Next Logosu - -

KernelSU Next

-

Android cihazlar için çekirdek tabanlı bir root çözümüdür.

- -

- - Latest Release - - - Nightly Build - - - License: GPL v2 - - - GitHub License - -

-
- ---- - -## 🚀 Özellikler - -- Çekirdek tabanlı `su` ve root erişim yönetimi. -- **[Magic Mount](https://topjohnwu.github.io/Magisk/details.html#magic-mount)** ve **[OverlayFS](https://en.wikipedia.org/wiki/OverlayFS)** tabanlı modül sistemi. -- [Uygulama Profili](https://kernelsu.org/guide/app-profile.html): Uygulama başına root yetkisini sınırlandırma. - ---- - -## ✅ Uyumluluk - -KernelSU Next, **4.4 ile 6.6** arasındaki Android çekirdeklerini destekler: - -| Çekirdek Sürümü | Destek Notları | -|------------------------|--------------------------------------------------------------------------| -| 5.10+ (GKI 2.0) | Hazır imajlar ve LKM/KMI desteği | -| 4.19 – 5.4 (GKI 1.0) | KernelSU sürücüsünün çekirdeğe gömülü olması gerekir | -| < 4.14 (EOL) | KernelSU sürücüsü gerekir (3.18+ deneysel olup yama gerektirebilir) | - -**Desteklenen mimariler:** `arm64-v8a`, `armeabi-v7a`, `x86_64` - ---- - -## 📦 Kurulum - -Kurulum talimatları için [Kurulum Kılavuzu](https://kernelsu-next.github.io/webpage/pages/installation.html) sayfasına bakınız. - ---- - -## 🔐 Güvenlik - -Güvenlik açıklarını bildirmek için lütfen [SECURITY.md](/SECURITY.md) dosyasına bakınız. - ---- - -## 📜 Lisans - -- **`/kernel` dizini:** [Yalnızca GPL-2.0](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html) -- **Diğer tüm dosyalar:** [GPL-3.0-veya-sonrası](https://www.gnu.org/licenses/gpl-3.0.html) - ---- - -## 💸 Bağışlar - -Projeye destek olmak isterseniz: - -- **USDT (BEP20, ERC20):** `0x12b5224b7aca0121c2f003240a901e1d064371c1` -- **USDT (TRC20):** `TYUVMWGTcnR5svnDoX85DWHyqUAeyQcdjh` -- **ETH (ERC20):** `0x12b5224b7aca0121c2f003240a901e1d064371c1` -- **LTC:** `Ld238uYBuRQdZB5YwdbkuU6ektBAAUByoL` -- **BTC:** `19QgifcjMjSr1wB2DJcea5cxitvWVcXMT6` - ---- - -## 🙏 Katkıda Bulunanlar - -- [Kernel-Assisted Superuser](https://git.zx2c4.com/kernel-assisted-superuser/about/) – KernelSU Fikrinin temeli -- [Magisk](https://github.com/topjohnwu/Magisk) – Temel root altyapısı -- [Genuine](https://github.com/brevent/genuine/) – APK v2 imza doğrulaması -- [Diamorphine](https://github.com/m0nad/Diamorphine) – Rootkit teknikleri -- [KernelSU](https://github.com/tiann/KernelSU) – KernelSU Next'in temelini oluşturan orijinal proje -- [Magic Mount Port](https://github.com/5ec1cff/KernelSU/blob/main/userspace/ksud/src/magic_mount.rs) – KernelSU’yu kurtardığı için 💜 5ec1cff’e teşekkürler diff --git a/docs/README_TW.md b/docs/README_TW.md deleted file mode 100644 index d435348d..00000000 --- a/docs/README_TW.md +++ /dev/null @@ -1,88 +0,0 @@ -[English](README.md) | [简体中文](README_CN.md) | **繁體中文** | [Türkçe](README_TR.md) | [Português (Brasil)](README_PT-BR.md) | [한국어](README_KO.md) | [Français](README_FR.md) | [Bahasa Indonesia](README_ID.md) | [Русский](README_RU.md) | [Український](README_UA.md) | [ภาษาไทย](README_TH.md) | [Tiếng Việt](README_VI.md) | [Italiano](README_IT.md) | [Polski](README_PL.md) | [Български](README_BG.md) | [日本語](README_JA.md) - ---- - -
- KernelSU Next Logo - -

KernelSU Next

-

基於內核的 Android 設備 Root 解決方案

- -

- - Latest Release - - - Nightly Build - - - License: GPL v2 - - - GitHub License - -

-
- ---- - -## 🚀 特性 - -- 基於內核的 `su` 和 Root 權限管理 -- 模塊系統基於 **[Magic Mount](https://topjohnwu.github.io/Magisk/details.html#magic-mount)** 以及 **[OverlayFS](https://en.wikipedia.org/wiki/OverlayFS)** -- [App Profile](https://kernelsu.org/zh_CN/guide/app-profile.html):把 Root 權限關進籠子裡 - ---- - -## ✅ 兼容狀態 - -KernelSU Next 正式支持大多數從 **4.4 到 6.6** 的 Android 內核 - -| 内核版本 | 支援狀況 | -|----------------|---------------| -| 5.10+ (GKI 2.0) | 可以運行預構建的映像和 LKM/KMI | -| 4.19 – 5.4 (GKI 1.0) | 需要重新編譯 KernelSU 驅動程序 | -| <4.14 (EOL) | 需要重新編譯 KernelSU 驅動程序(3.18+ 是實驗性的,可能需要回溯移植一些功能) | - -**支援的架構:** -`arm64-v8a`、`armeabi-v7a`、`x86_64` - ---- - -## 📦 用法 - -請遵循[安裝説明](https://kernelsu-next.github.io/webpage/pages/installation.html)進行操作 - ---- - -## 🔐 安全性 - -有關報告 KernelSU Next 漏洞的信息,請參閱 [SECURITY.md](/SECURITY.md) - ---- - -## 📜 許可證 - -- **目錄 `/kernel` 下所有文件**為 [GPL-2.0-only](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html) -- **`/kernel` 目錄以外的其他部分**均為 [GPL-3.0-or-later](https://www.gnu.org/licenses/gpl-3.0.html) - ---- - -## 💸 抖内 - -- **USDT (BEP20, ERC20)**: `0x12b5224b7aca0121c2f003240a901e1d064371c1` -- **USDT (TRC20)**: `TYUVMWGTcnR5svnDoX85DWHyqUAeyQcdjh` -- **ETH (ERC20)**: `0x12b5224b7aca0121c2f003240a901e1d064371c1` -- **LTC**: `Ld238uYBuRQdZB5YwdbkuU6ektBAAUByoL` -- **BTC**: `19QgifcjMjSr1wB2DJcea5cxitvWVcXMT6` - ---- - -## 🙏 鳴謝 - -- [Kernel-Assisted Superuser](https://git.zx2c4.com/kernel-assisted-superuser/about/):KernelSU 的靈感. -- [Magisk](https://github.com/topjohnwu/Magisk):強大的 Root 工具. -- [genuine](https://github.com/brevent/genuine/):APK v2 簽名驗證。 -- [Diamorphine](https://github.com/m0nad/Diamorphine):一些 Rootkit 技巧。 -- [KernelSU](https://github.com/tiann/KernelSU):感謝 tiann,否則 KernelSU Next 根本不會存在。 -- [Magic Mount Port](https://github.com/5ec1cff/KernelSU/blob/main/userspace/ksud/src/magic_mount.rs):💜 5ec1cff 為了拯救 KernelSU! diff --git a/docs/README_UA.md b/docs/README_UA.md deleted file mode 100644 index b23b42e0..00000000 --- a/docs/README_UA.md +++ /dev/null @@ -1,90 +0,0 @@ -**Languages**: -[English](README.md) | [简体中文](README_CN.md) | [繁體中文](README_TW.md) | [Türkçe](README_TR.md) | [Português (Brasil)](README_PT-BR.md) | [한국어](README_KO.md) | [Français](README_FR.md) | [Bahasa Indonesia](README_ID.md) | [Русский](README_RU.md) | **Українська** | [ภาษาไทย](README_TH.md) | [Tiếng Việt](README_VI.md) | [Italiano](README_IT.md) | [Polski](README_PL.md) | [Български](README_BG.md) | [日本語](README_JA.md) - ---- - -
- KernelSU Next Logo - -

KernelSU Next

-

Рішення для root-прав на основі ядра для пристроїв Android.

- -

- - Latest Release - - - Nightly Build - - - License: GPL v2 - - - GitHub License - -

-
- ---- - -## 🚀 Особливості - -- Керування `su` та root-доступом на основі ядра. -- Модульна система на основі [Magic Mount](https://topjohnwu.github.io/Magisk/details.html#magic-mount) та [OverlayFS](https://en.wikipedia.org/wiki/OverlayFS). -- [Профілі програм](https://kernelsu.org/guide/app-profile.html): Обмеження root-прав для кожної програми. - ---- - -## ✅ Сумісність - -KernelSU Next підтримує ядра Android від **4.4 до 6.6**: - -| Версія ядра | Примітки підтримки | -|----------------------|-------------------------------------------------------------------------------------------| -| 5.10+ (GKI 2.0) | Підтримує попередньо створені образи та LKM/KMI | -| 4.19 – 5.4 (GKI 1.0) | Потрібен вбудований драйвер KernelSU | -| <4.14 (EOL) | Потрібен драйвер KernelSU (версія 3.18+ є експериментальною, може знадобитися портування) | - -**Підтримувані архітектури:** `arm64-v8a`, `armeabi-v7a`, `x86_64` - ---- - -## 📦 Встановлення - -Будь ласка, зверніться до [Посібника з встановлення](https://kernelsu-next.github.io/webpage/pages/installation.html) для отримання інструкцій з налаштування. - ---- - -## 🔐 Безпека - -Щоб повідомити про проблеми безпеки, див [SECURITY.md](/SECURITY.md). - ---- - -## 📜 Ліцензія - -- **Каталог `/kernel`:** [GPL-2.0-only](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html) -- **Усі інші файли:** [GPL-3.0-or-later](https://www.gnu.org/licenses/gpl-3.0.html) - ---- - -## 💸 Пожертви - -Якщо ви хочете підтримати проєкт: - -- **USDT (BEP20, ERC20)**: `0x12b5224b7aca0121c2f003240a901e1d064371c1` -- **USDT (TRC20)**: `TYUVMWGTcnR5svnDoX85DWHyqUAeyQcdjh` -- **ETH (ERC20)**: `0x12b5224b7aca0121c2f003240a901e1d064371c1` -- **LTC**: `Ld238uYBuRQdZB5YwdbkuU6ektBAAUByoL` -- **BTC**: `19QgifcjMjSr1wB2DJcea5cxitvWVcXMT6` - ---- - -## 🙏 Подяки - -- [Kernel-Assisted Superuser](https://git.zx2c4.com/kernel-assisted-superuser/about/) – Натхнення для концепції -- [Magisk](https://github.com/topjohnwu/Magisk) – Топовий інструмент для root -- [Genuine](https://github.com/brevent/genuine/) – Перевірка підпису APK версії 2 -- [Diamorphine](https://github.com/m0nad/Diamorphine) – Деякі навики RootKit -- [KernelSU](https://github.com/tiann/KernelSU) – Основа для KernelSU Next -- [Magic Mount Port](https://github.com/5ec1cff/KernelSU/blob/main/userspace/ksud/src/magic_mount.rs) – 💜 до 5ec1cff за збереження KernelSU diff --git a/docs/README_VI.md b/docs/README_VI.md deleted file mode 100644 index 85578425..00000000 --- a/docs/README_VI.md +++ /dev/null @@ -1,63 +0,0 @@ -[English](README.md) | [简体中文](README_CN.md) | [繁體中文](README_TW.md) | [Türkçe](README_TR.md) | [Português (Brasil)](README_PT-BR.md) | [한국어](README_KO.md) | [Français](README_FR.md) | [Bahasa Indonesia](README_ID.md) | [Русский](README_RU.md) | [Український](README_UA.md) | [ภาษาไทย](README_TH.md) | **Tiếng Việt** | [Italiano](README_IT.md) | [Polski](README_PL.md) | [Български](README_BG.md) | [日本語](README_JA.md) - -# KernelSU Next - -logo - -Một giải pháp root từ nhân linux dành cho các thiết bị chạy Android - -[![Phiên bản mới nhất](https://img.shields.io/github/v/release/KernelSU-Next/KernelSU-Next?label=Release&logo=github)](https://github.com/KernelSU-Next/KernelSU-Next/releases/latest) -[![CI build mới nhất](https://img.shields.io/badge/Nightly%20Release-gray?logo=hackthebox&logoColor=fff)](https://nightly.link/KernelSU-Next/KernelSU-Next/workflows/build-manager-ci/next/Manager) -[![Gíây pháp: 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) -[![Gíây phép GITHUB](https://img.shields.io/github/license/KernelSU-Next/KernelSU-Next?logo=gnu)](/LICENSE) - -## Tính năng - -1. Quản lý quyền truy cập SU dựa trên kernel android. -2. Hệ thống mount module dựa trên 1 trong 2 cơ chế mount [Magic Mount](https://topjohnwu.github.io/Magisk/details.html#magic-mount) / [OverlayFS](https://en.wikipedia.org/wiki/OverlayFS). -3. [App Profile](https://kernelsu.org/guide/app-profile.html): Quản lý quyền truy cập root 1 cách chặt chẽ - -## Danh sách tương thích - -KernelSU Next hỗ trợ chính thức các kernel Android từ phiên bản 4.4 đến 6.6 - - GKI 2.0 (5.10+) kernels có thể cài đặt qua những .img/.zip đã được build sẵn và LKM/KMI hoặc tự vá qua manager (nếu được) - - GKI 1.0 (4.19 - 5.4) kernels cần dược build lại với các nhân KernelSU Next - - EOL (<4.14) kernels cần dược build lại với các nhân KernelSU Next (các kernels 3.18+ đang dược thử nghiệm và có thể cần backports 1 vài thứ ). - -Hiện tại kernelSU Next chỉ hỗ trợ những cpu có `arm64-v8a`, `armeabi-v7a` & `x86_64` - -## Sử dụng - -- [Hướng dẫn vá KernelSU Next vào Kernel của bạn (yêu cầu kernel source)](https://ksunext.org/pages/installation.html) - -## Bảo mật - -Để biết thêm thông tin về việc báo cáo lỗ hổng bảo mật trong KernelSU Next vui lòng đọc (Thông tin sẽ dược gửi về KernelSU)[SECURITY.md](/SECURITY.md). - -## Gíây phép - -- Những thư mục/tập tin trong `kernel` là giấy phép [GPL-2.0-only](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html). -- Những thư mục/tập tin ngoài `kernel` là giấy phép [GPL-3.0-or-later](https://www.gnu.org/licenses/gpl-3.0.html). - -## Quyên góp/Hỗ trợ - -- 0x12b5224b7aca0121c2f003240a901e1d064371c1 [ USDT BEP20 ] - -- TYUVMWGTcnR5svnDoX85DWHyqUAeyQcdjh [ USDT TRC20 ] - -- 0x12b5224b7aca0121c2f003240a901e1d064371c1 [ USDT ERC20 ] - -- 0x12b5224b7aca0121c2f003240a901e1d064371c1 [ ETH ERC20 ] - -- Ld238uYBuRQdZB5YwdbkuU6ektBAAUByoL [ LTC ] - -- 19QgifcjMjSr1wB2DJcea5cxitvWVcXMT6 [ BTC ] - -## Lời cảm ơn tới... - -- [Kernel-Assisted Superuser](https://git.zx2c4.com/kernel-assisted-superuser/about/): Ý tưởng cho sự ra đời của KernelSU. -- [Magisk](https://github.com/topjohnwu/Magisk): Công cụ root mạnh mẽ, quen thuộc và tương thích cao cho các thiết bị chạy Android. -- [genuine](https://github.com/brevent/genuine/): Chữ kí apk v2. -- [Diamorphine](https://github.com/m0nad/Diamorphine): Một vài kỹ năng rootkit. -- [KernelSU](https://github.com/tiann/KernelSU): Nguồn gốc của KernelSU Next, thanks to tiann. -- [Magic Mount Port](https://github.com/5ec1cff/KernelSU/blob/main/userspace/ksud/src/magic_mount.rs): 5ec1cff - người đã cứu lấy KernelSU💜 ! diff --git a/docs/WebUi_Next/API_DOC.md b/docs/WebUi_Next/API_DOC.md deleted file mode 100644 index 58679752..00000000 --- a/docs/WebUi_Next/API_DOC.md +++ /dev/null @@ -1,326 +0,0 @@ -# WebUI-Next API Documentation - -This document provides examples of how to use the `WebUI-Next` JavaScript APIs exposed to a module WebUI. These APIs allow code to run in the WebUI to interact with the system, execute shell commands, manage packages, control UI elements, and more coming soon. - - -## Table of Contents -1. [exec(cmd)](#exec-cmd) -2. [exec(cmd, callbackFunc)](#exec-cmd-callbackfunc) -3. [exec(cmd, options, callbackFunc)](#exec-cmd-options-callbackfunc) -4. [spawn(command, args, options, callbackFunc)](#spawn-command-args-options-callbackfunc) -5. [toast(msg)](#toast-msg) -6. [fullScreen(enable)](#fullscreen-enable) -7. [moduleInfo()](#moduleinfo) -8. [listSystemPackages()](#listsystempackages) -9. [listUserPackages()](#listuserpackages) -10. [listAllPackages()](#listallpackages) -11. [getPackagesInfo(packageNamesJson)](#getpackagesinfo-packagenamesjson) -12. [cacheAllPackageIcons(size)](#cacheallpackageicons-size) -13. [getPackagesIcons(packageNamesJson, size)](#getpackagesicons-packagenamesjson-size) - ---- - -## exec(cmd) - -Executes a shell command synchronously and returns the output as a string. - -### Parameters -- `cmd` (String): The shell command to execute. - -### Returns -- `String`: The command output (stdout). - -### Example -```javascript -const output = ksu.exec("ls /system"); -console.log("Output:", output); -``` - ---- - -## exec(cmd, callbackFunc) - -Executes a shell command asynchronously and invokes the provided callback function with the result. - -### Parameters -- `cmd` (String): The shell command to execute. -- `callbackFunc` (String): The name of the JavaScript callback function to invoke with the result. - -### Callback Signature -```javascript -function callbackFunc(exitCode, stdout, stderr) { - // Handle result -} -``` - -### Example -```javascript -function handleResult(exitCode, stdout, stderr) { - console.log("Exit Code:", exitCode); - console.log("Stdout:", stdout); - console.log("Stderr:", stderr); -} - -ksu.exec("ls /system", "handleResult"); -``` - ---- - -## exec(cmd, options, callbackFunc) - -Executes a shell command asynchronously with options (e.g., working directory, environment variables) and invokes the provided callback function with the result. - -### Parameters -- `cmd` (String): The shell command to execute. -- `options` (String): A JSON string specifying options like `cwd` (working directory) and `env` (environment variables). -- `callbackFunc` (String): The name of the JavaScript callback function to invoke with the result. - -### Options Format -```javascript -{ - "cwd": "/path/to/working/directory", - "env": { - "KEY1": "VALUE1", - "KEY2": "VALUE2" - } -} -``` - -### Callback Signature -```javascript -function callbackFunc(exitCode, stdout, stderr) { - // Handle result -} -``` - -### Example -```javascript -const options = JSON.stringify({ - cwd: "/system", - env: { PATH: "/system/bin" } -}); - -function handleResult(exitCode, stdout, stderr) { - console.log("Exit Code:", exitCode); - console.log("Stdout:", stdout); - console.log("Stderr:", stderr); -} - -ksu.exec("ls", JSON.stringify(options), "handleResult"); -``` - ---- - -## spawn(command, args, options, callbackFunc) - -Spawns a shell command with arguments and streams output through events to a JavaScript object. - -### Parameters -- `command` (String): The shell command to execute. -- `args` (String): A JSON array of command arguments. -- `options` (String): A JSON string specifying options like `cwd` and `env` (optional). -- `callbackFunc` (String): The name of the JavaScript object to receive events (`stdout`, `stderr`, `exit`, `error`). - -### Callback Object -The callback object should implement methods to handle events: -- `stdout.emit('data', data)`: Emits stdout data. -- `stderr.emit('data', data)`: Emits stderr data. -- `exit(code)`: Emits the exit code. -- `error(err)`: Emits an error object with `exitCode` and `message`. - -### Example -```javascript -const streamHandler = { - stdout: { - emit: (event, data) => { - if (event === "data") console.log("Stdout:", data); - } - }, - stderr: { - emit: (event, data) => { - if (event === "data") console.log("Stderr:", data); - } - }, - emit: (event, data) => { - if (event === "exit") console.log("Exit Code:", data); - if (event === "error") console.error("Error:", data); - } -}; - -const args = JSON.stringify(["-l", "/system"]); -const options = JSON.stringify({ cwd: "/system" }); - -ksu.spawn("ls", args, options, "streamHandler"); -``` - ---- - -## toast(msg) - -Displays a short Android toast message. - -### Parameters -- `msg` (String): The message to display. - -### Example -```javascript -ksu.toast("Hello from WebUI-Next!"); -``` - ---- - -## fullScreen(enable) - -Toggles full-screen mode by hiding or showing system UI (status and navigation bars). - -### Parameters -- `enable` (Boolean): `true` to enable full-screen mode, `false` to disable. - -### Example -```javascript -// Enable full-screen -ksu.fullScreen(true); - -// Disable full-screen -ksu.fullScreen(false); -``` - ---- - -## moduleInfo() - -Returns information about the current module as a JSON string. - -### Returns -- `String`: A JSON string containing module information, including `moduleDir` and other module-specific details. - -### Example -```javascript -const moduleInfo = JSON.parse(ksu.moduleInfo()); -console.log("Module Directory:", moduleInfo.moduleDir); -console.log("Module ID:", moduleInfo.id); -``` - ---- - -## listSystemPackages() - -Returns a list of system package names as a JSON array. - -### Returns -- `String`: A JSON array of system package names. - -### Example -```javascript -const systemPackages = JSON.parse(ksu.listSystemPackages()); -console.log("System Packages:", systemPackages); -``` - ---- - -## listUserPackages() - -Returns a list of user-installed package names as a JSON array. - -### Returns -- `String`: A JSON array of user package names. - -### Example -```javascript -const userPackages = JSON.parse(ksu.listUserPackages()); -console.log("User Packages:", userPackages); -``` - ---- - -## listAllPackages() - -Returns a list of all installed package names as a JSON array. - -### Returns -- `String`: A JSON array of all package names. - -### Example -```javascript -const allPackages = JSON.parse(ksu.listAllPackages()); -console.log("All Packages:", allPackages); -``` - ---- - -## getPackagesInfo(packageNamesJson) - -Returns detailed information about specified packages as a JSON array. - -### Parameters -- `packageNamesJson` (String): A JSON array of package names. - -### Returns -- `String`: A JSON array of objects containing package details (`packageName`, `versionName`, `versionCode`, `appLabel`, `isSystem`, `uid`) or an error object if the package is not found. - -### Example -```javascript -const packageNames = JSON.stringify(["com.android.settings", "com.example.app"]); -const packageInfos = JSON.parse(ksu.getPackagesInfo(packageNames)); -packageInfos.forEach(info => { - if (info.error) { - console.error(`Error for ${info.packageName}: ${info.error}`); - } else { - console.log(`Package: ${info.packageName}, Version: ${info.versionName}, System: ${info.isSystem}`); - } -}); -``` - ---- - -## cacheAllPackageIcons(size) - -Caches icons for all installed packages at the specified size. - -### Parameters -- `size` (Number): The size (in pixels) for the square icon. - -### Example -```javascript -// Cache all package icons at 48x48 pixels -ksu.cacheAllPackageIcons(48); -``` - ---- - -## getPackagesIcons(packageNamesJson, size) - -Returns base64-encoded icons for specified packages as a JSON array. - -### Parameters -- `packageNamesJson` (String): A JSON array of package names. -- `size` (Number): The size (in pixels) for the square icon. - -### Returns -- `String`: A JSON array of objects containing `packageName` and `icon` (base64-encoded PNG or empty string if unavailable). - -### Example -```javascript -const packageNames = JSON.stringify(["com.android.settings", "com.example.app"]); -const packageIcons = JSON.parse(ksu.getPackagesIcons(packageNames, 48)); -packageIcons.forEach(item => { - if (item.icon) { - console.log(`Icon for ${item.packageName}: ${item.icon.substring(0, 30)}...`); - // Example: Display icon in an element - const img = document.createElement("img"); - img.src = item.icon; - document.body.appendChild(img); - } else { - console.log(`No icon for ${item.packageName}`); - } -}); -``` - ---- - -## Notes -- **Root Access**: Methods like `exec` and `spawn` require root access and use the `libsu` library for shell execution. -- **Asynchronous Operations**: Use `WebUI.post` to ensure UI thread safety when invoking JavaScript callbacks. -- **Error Handling**: Always check for errors in callbacks (e.g., `stderr` in `exec`, `error` event in `spawn`). -- **Icon Caching**: Use `cacheAllPackageIcons` to improve performance for subsequent `getPackagesIcons` calls. -- **JSON Parsing**: Ensure valid JSON strings are passed to methods like `getPackagesInfo` and `getPackagesIcons`. \ No newline at end of file diff --git a/justfile b/justfile deleted file mode 100644 index 51bef767..00000000 --- a/justfile +++ /dev/null @@ -1,14 +0,0 @@ -alias bk := build_ksud -alias bm := build_manager - -build_ksud: - cross build --target aarch64-linux-android --release --manifest-path ./userspace/ksud/Cargo.toml - -build_manager: build_ksud - cp userspace/ksud/target/aarch64-linux-android/release/ksud manager/app/src/main/jniLibs/arm64-v8a/libksud.so - cd manager && ./gradlew aDebug - -clippy: - cargo fmt --manifest-path ./userspace/ksud/Cargo.toml - cross clippy --target x86_64-pc-windows-gnu --release --manifest-path ./userspace/ksud/Cargo.toml - cross clippy --target aarch64-linux-android --release --manifest-path ./userspace/ksud/Cargo.toml diff --git a/manager/.gitignore b/manager/.gitignore deleted file mode 100644 index 14febcea..00000000 --- a/manager/.gitignore +++ /dev/null @@ -1,11 +0,0 @@ -*.iml -.gradle -.idea -.kotlin -.DS_Store -build -captures -.cxx -local.properties -key.jks -setup.sh \ No newline at end of file diff --git a/manager/app/.gitignore b/manager/app/.gitignore deleted file mode 100644 index dc5ca963..00000000 --- a/manager/app/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -/build -/release/ diff --git a/manager/app/build.gradle.kts b/manager/app/build.gradle.kts deleted file mode 100644 index 24d058bd..00000000 --- a/manager/app/build.gradle.kts +++ /dev/null @@ -1,138 +0,0 @@ -@file:Suppress("UnstableApiUsage") - -import com.android.build.gradle.internal.api.BaseVariantOutputImpl -import com.android.build.gradle.tasks.PackageAndroidArtifact - -plugins { - alias(libs.plugins.agp.app) - alias(libs.plugins.kotlin) - alias(libs.plugins.compose.compiler) - alias(libs.plugins.ksp) - alias(libs.plugins.lsplugin.apksign) - id("kotlin-parcelize") -} - -val managerVersionCode: Int by rootProject.extra -val managerVersionName: String by rootProject.extra - -apksign { - storeFileProperty = "KEYSTORE_FILE" - storePasswordProperty = "KEYSTORE_PASSWORD" - keyAliasProperty = "KEY_ALIAS" - keyPasswordProperty = "KEY_PASSWORD" -} - -android { - namespace = "com.rifsxd.ksunext" - - buildTypes { - release { - isMinifyEnabled = true - isShrinkResources = true - proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro") - } - } - - buildFeatures { - aidl = true - buildConfig = true - compose = true - prefab = true - } - - kotlinOptions { - jvmTarget = "21" - } - - packaging { - jniLibs { - useLegacyPackaging = true - } - resources { - // https://stackoverflow.com/a/58956288 - // It will break Layout Inspector, but it's unused for release build. - excludes += "META-INF/*.version" - // https://github.com/Kotlin/kotlinx.coroutines?tab=readme-ov-file#avoiding-including-the-debug-infrastructure-in-the-resulting-apk - excludes += "DebugProbesKt.bin" - // https://issueantenna.com/repo/kotlin/kotlinx.coroutines/issues/3158 - excludes += "kotlin-tooling-metadata.json" - } - } - - externalNativeBuild { - cmake { - path("src/main/cpp/CMakeLists.txt") - } - } - - applicationVariants.all { - outputs.forEach { - val output = it as BaseVariantOutputImpl - output.outputFileName = "KernelSU_Next_${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 { - implementation(libs.androidx.activity.compose) - implementation(libs.androidx.navigation.compose) - - implementation(platform(libs.androidx.compose.bom)) - implementation(libs.androidx.compose.material.icons.extended) - implementation(libs.androidx.compose.material) - implementation(libs.androidx.compose.material3) - implementation(libs.androidx.compose.ui) - implementation(libs.androidx.compose.ui.tooling.preview) - - debugImplementation(libs.androidx.compose.ui.test.manifest) - debugImplementation(libs.androidx.compose.ui.tooling) - - implementation(libs.androidx.lifecycle.runtime.compose) - implementation(libs.androidx.lifecycle.runtime.ktx) - implementation(libs.androidx.lifecycle.viewmodel.compose) - - implementation(libs.compose.destinations.core) - ksp(libs.compose.destinations.ksp) - - implementation(libs.com.github.topjohnwu.libsu.core) - implementation(libs.com.github.topjohnwu.libsu.service) - implementation(libs.com.github.topjohnwu.libsu.io) - - implementation(libs.dev.rikka.rikkax.parcelablelist) - - implementation(libs.io.coil.kt.coil.compose) - - implementation(libs.kotlinx.coroutines.core) - - implementation(libs.me.zhanghai.android.appiconloader.coil) - - implementation(libs.sheet.compose.dialogs.core) - implementation(libs.sheet.compose.dialogs.list) - implementation(libs.sheet.compose.dialogs.input) - - implementation(libs.markdown) - implementation(libs.androidx.webkit) - - implementation(libs.lsposed.cxx) - - implementation(libs.mmrl.ui) -} \ No newline at end of file diff --git a/manager/app/proguard-rules.pro b/manager/app/proguard-rules.pro deleted file mode 100644 index 6434212b..00000000 --- a/manager/app/proguard-rules.pro +++ /dev/null @@ -1,39 +0,0 @@ --verbose --optimizationpasses 5 - --dontwarn org.conscrypt.** --dontwarn kotlinx.serialization.** - -# Please add these rules to your existing keep rules in order to suppress warnings. -# This is generated automatically by the Android Gradle plugin. --dontwarn com.google.auto.service.AutoService --dontwarn com.google.j2objc.annotations.RetainedWith --dontwarn javax.lang.model.SourceVersion --dontwarn javax.lang.model.element.AnnotationMirror --dontwarn javax.lang.model.element.AnnotationValue --dontwarn javax.lang.model.element.Element --dontwarn javax.lang.model.element.ElementKind --dontwarn javax.lang.model.element.ElementVisitor --dontwarn javax.lang.model.element.ExecutableElement --dontwarn javax.lang.model.element.Modifier --dontwarn javax.lang.model.element.Name --dontwarn javax.lang.model.element.PackageElement --dontwarn javax.lang.model.element.TypeElement --dontwarn javax.lang.model.element.TypeParameterElement --dontwarn javax.lang.model.element.VariableElement --dontwarn javax.lang.model.type.ArrayType --dontwarn javax.lang.model.type.DeclaredType --dontwarn javax.lang.model.type.ExecutableType --dontwarn javax.lang.model.type.TypeKind --dontwarn javax.lang.model.type.TypeMirror --dontwarn javax.lang.model.type.TypeVariable --dontwarn javax.lang.model.type.TypeVisitor --dontwarn javax.lang.model.util.AbstractAnnotationValueVisitor8 --dontwarn javax.lang.model.util.AbstractTypeVisitor8 --dontwarn javax.lang.model.util.ElementFilter --dontwarn javax.lang.model.util.Elements --dontwarn javax.lang.model.util.SimpleElementVisitor8 --dontwarn javax.lang.model.util.SimpleTypeVisitor7 --dontwarn javax.lang.model.util.SimpleTypeVisitor8 --dontwarn javax.lang.model.util.Types --dontwarn javax.tools.Diagnostic$Kind \ No newline at end of file diff --git a/manager/app/src/main/AndroidManifest.xml b/manager/app/src/main/AndroidManifest.xml deleted file mode 100644 index 40d3d311..00000000 --- a/manager/app/src/main/AndroidManifest.xml +++ /dev/null @@ -1,64 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/manager/app/src/main/aidl/com/rifsxd/ksunext/IKsuInterface.aidl b/manager/app/src/main/aidl/com/rifsxd/ksunext/IKsuInterface.aidl deleted file mode 100644 index 899e79b8..00000000 --- a/manager/app/src/main/aidl/com/rifsxd/ksunext/IKsuInterface.aidl +++ /dev/null @@ -1,9 +0,0 @@ -// IKsuInterface.aidl -package com.rifsxd.ksunext; - -import android.content.pm.PackageInfo; -import rikka.parcelablelist.ParcelableListSlice; - -interface IKsuInterface { - ParcelableListSlice getPackages(int flags); -} \ No newline at end of file diff --git a/manager/app/src/main/assets/eruda.min.js b/manager/app/src/main/assets/eruda.min.js deleted file mode 100644 index 0663865a..00000000 --- a/manager/app/src/main/assets/eruda.min.js +++ /dev/null @@ -1,9 +0,0 @@ -/** - * Skipped minification because the original files appears to be already minified. - * Original file: /npm/eruda@3.4.3/eruda.js - * - * Do NOT use SRI with dynamically generated files! More information: https://www.jsdelivr.com/using-sri-with-dynamic-files - */ -/*! eruda v3.4.3 https://eruda.liriliri.io/ */ -!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.eruda=t():e.eruda=t()}(self,(function(){return function(){var __webpack_modules__={8:function(e,t){t=function(e){var t,i,a=e[0]/255,s=e[1]/255,c=e[2]/255,l=n(a,s,c),u=o(a,s,c),d=u-l;(t=n(60*(t=u===l?0:a===u?(s-c)/d:s===u?2+(c-a)/d:4+(a-s)/d),360))<0&&(t+=360);var h=(l+u)/2;i=u===l?0:h<=.5?d/(u+l):d/(2-u-l);var f=[r(t),r(100*i),r(100*h)];return e[3]&&(f[3]=e[3]),f};var n=Math.min,o=Math.max,r=Math.round;e.exports=t},15:function(e,t,n){var o=n(5651),r=n(8971);t=function(e,t){var n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{};o(n,i);var a=n.ellipsis,s=n.separator;if(t>e.length)return e;var c=t-a.length;if(c<1)return a;var l=e.slice(0,c);if(r(s))return l+a;if(e.indexOf(s,c)!==c){var u=l.lastIndexOf(s);u>-1&&(l=l.slice(0,u))}return l+a};var i={ellipsis:"..."};e.exports=t},21:function(e,t,n){var o=n(2717),r=n(1738),i=n(9100),a=n(8178),s=new(t=o({className:"Select",initialize:function(e){return this.length=0,e?r(e)?s.find(e):void(e.nodeType&&(this[0]=e,this.length=1)):this},find:function(e){var n=new t;return this.each((function(){a(n,this.querySelectorAll(e))})),n},each:function(e){return i(this,(function(t,n){e.call(t,n,t)})),this}}))(document);e.exports=t},96:function(e,t,n){var o=n(2561);t=function(e){return o(e).toLocaleLowerCase()},e.exports=t},128:function(e,t,n){"use strict";n.d(t,{A:function(){return K}});var o=n(3029),r=n(2901),i=n(388),a=n(3954),s=n(5361),c=n(3915),l=n.n(c),u=n(6097),d=n.n(u),h=n(1738),f=n.n(h),p=n(4994),v=n.n(p),g=n(9405),m=n.n(g),b=n(5169),y=n.n(b),A=n(9548),w=n.n(A),_=n(3249),x=n.n(_),k=n(6030),C=n.n(k),S=n(5004),E=n.n(S);n(9410),n(8609);function T(e){var t="luna-".concat(e,"-");function n(e){return l()(m()(e).split(/\s+/),(function(e){return x()(e,t)?e:e.replace(/[\w-]+/,(function(e){return"".concat(t).concat(e)}))})).join(" ")}return function(e){if(/<[^>]*>/g.test(e))try{var t=w().parse(e);return N(t,(function(e){e.attrs&&e.attrs.class&&(e.attrs.class=n(e.attrs.class))})),w().stringify(t)}catch(t){return n(e)}return n(e)}}function N(e,t){for(var n=0,o=e.length;n2&&void 0!==arguments[2]?arguments[2]:{}).theme,u=void 0===l?"light":l;return(0,o.A)(this,t),r=function(e,t,n){return t=(0,a.A)(t),(0,i.A)(e,U()?Reflect.construct(t,n||[],(0,a.A)(e).constructor):t.apply(e,n))}(this,t),r.subComponents=[],r.theme="",r.onThemeChange=function(e){"auto"===r.options.theme&&r.setTheme(e)},r.compName=c,r.c=T(c),r.options={},r.container=e,r.$container=z()(e),r.$container.addClass(["luna-".concat(c),r.c("platform-".concat((s=E()(),"os x"===s?"mac":s)))]),r.on("changeOption",(function(e,t){if("theme"===e&&t){var n=t;"auto"===t&&(n=$().get()),r.setTheme(n),B()(r.subComponents,(function(e){return e.setOption("theme",t)}))}})),$().on("change",r.onThemeChange),r.setOption("theme",u),r}return(0,s.A)(t,e),(0,r.A)(t,[{key:"destroy",value:function(){var e=this;this.destroySubComponents();var t=this.$container,n=t.attr("class");B()(n.split(/\s+/),(function(n){Q()(n,"luna-".concat(e.compName))&&t.rmClass(n)})),t.html(""),this.emit("destroy"),this.removeAllListeners(),$().off("change",this.onThemeChange)}},{key:"setOption",value:function(e,t){var n=this,o=this.options,r={};"string"==typeof e?r[e]=t:r=e,B()(r,(function(e,t){var r=o[t];o[t]=e,e!==r&&n.emit("changeOption",t,e,r)}))}},{key:"getOption",value:function(e){return this.options[e]}},{key:"addSubComponent",value:function(e){e.setOption("theme",this.options.theme),this.subComponents.push(e)}},{key:"removeSubComponent",value:function(e){H()(this.subComponents,(function(t){return t===e}))}},{key:"destroySubComponents",value:function(){B()(this.subComponents,(function(e){return e.destroy()})),this.subComponents=[]}},{key:"initOptions",value:function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};G()(e,t),R()(this.options,e)}},{key:"find",value:function(e){return this.$container.find(this.c(e))}},{key:"setTheme",value:function(e){var t=this.c,n=this.$container;this.theme&&n.rmClass(t("theme-".concat(this.theme))),n.addClass(t("theme-".concat(e))),this.theme=e}}])}(M());function J(){try{var e=!Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){})))}catch(e){}return(J=function(){return!!e})()}e=n.hmd(e);var K=function(e){function t(e){var n,r,s,c,l=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};return(0,o.A)(this,t),r=this,s=t,c=[e,{compName:"box-model"}],s=(0,a.A)(s),(n=(0,i.A)(r,J()?Reflect.construct(s,c||[],(0,a.A)(r).constructor):s.apply(r,c))).initOptions(l),n.options.element&&n.render(),n.bindEvent(),n}return(0,s.A)(t,e),(0,r.A)(t,[{key:"bindEvent",value:function(){var e=this;this.on("changeOption",(function(t){if("element"===t)e.render()}))}},{key:"render",value:function(){var e=this,t=this.c,n=this.$container,o=this.getBoxModelData();n.html([o.position?'
'):"",o.position?'
position
').concat(o.position.top,'

').concat(o.position.left,"
"):"",'
'),'
margin
').concat(o.margin.top,'

').concat(o.margin.left,"
"),'
'),'
border
').concat(o.border.top,'

').concat(o.border.left,"
"),'
'),'
padding
').concat(o.padding.top,'

').concat(o.padding.left,"
"),'
'),"".concat(o.content.width," × ").concat(o.content.height,""),"
",'
').concat(o.padding.right,'

').concat(o.padding.bottom,"
"),"
",'
').concat(o.border.right,'

').concat(o.border.bottom,"
"),"
",'
').concat(o.margin.right,'

').concat(o.margin.bottom,"
"),"
",o.position?'
').concat(o.position.right,'

').concat(o.position.bottom,"
"):"",o.position?"
":""].join(""));var r=this.find(".margin"),i=this.find(".border"),a=this.find(".padding"),s=this.find(".content"),c=function(){r.addClass(t("highlighted")),i.addClass(t("highlighted")),a.addClass(t("highlighted")),s.addClass(t("highlighted"))};c();var l=function(n){var o;switch(e.find(".highlighted").rmClass(t("highlighted")),n){case"margin":o=r;break;case"border":o=i;break;case"padding":o=a;break;default:o=s}o.addClass(t("highlighted")),e.emit("highlight",n)},u=v()(l,this,"margin"),d=v()(l,this,"border"),h=v()(l,this,"padding"),f=v()(l,this,"content");r.on("mouseenter",u).on("mouseleave",(function(){c(),e.emit("highlight","all")})),i.on("mouseenter",d).on("mouseleave",u),a.on("mouseenter",h).on("mouseleave",d),s.on("mouseenter",f).on("mouseleave",h)}},{key:"getBoxModelData",value:function(){var e=this.options.element,t=window.getComputedStyle(e);function n(e){var n=["top","left","right","bottom"];return"position"!==e&&(n=l()(n,(function(t){return"".concat(e,"-").concat(t)}))),"border"===e&&(n=l()(n,(function(e){return"".concat(e,"-width")}))),{top:V(t[n[0]],e),left:V(t[n[1]],e),right:V(t[n[2]],e),bottom:V(t[n[3]],e)}}var o,r,i,a,s,c,u={margin:n("margin"),border:n("border"),padding:n("padding"),content:(o=e,r=window.getComputedStyle(o),i=O(r.paddingLeft)+O(r.paddingRight),a=O(r.paddingTop)+O(r.paddingBottom),s=O(r.borderLeftWidth)+O(r.borderRightWidth),c=O(r.borderTopWidth)+O(r.borderBottomWidth),{width:V(o.offsetWidth-i-s),height:V(o.offsetHeight-a-c)})};return"static"!==t.position&&(u.position=n("position")),u}}])}(W);function V(e,t){if(d()(e))return e;if(!f()(e))return"‒";var n=O(e);return isNaN(n)?e:"position"===t?n:0===n?"‒":n}(function(e,t){try{e.exports=t,e.exports.default=t}catch(e){}})(e,K)},162:function(e,t){"use strict";var n=this&&this.__values||function(e){var t="function"==typeof Symbol&&Symbol.iterator,n=t&&e[t],o=0;if(n)return n.call(e);if(e&&"number"==typeof e.length)return{next:function(){return e&&o>=e.length&&(e=void 0),{value:e&&e[o++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},o=this&&this.__read||function(e,t){var n="function"==typeof Symbol&&e[Symbol.iterator];if(!n)return e;var o,r,i=n.call(e),a=[];try{for(;(void 0===t||t-- >0)&&!(o=i.next()).done;)a.push(o.value)}catch(e){r={error:e}}finally{try{o&&!o.done&&(n=i.return)&&n.call(i)}finally{if(r)throw r.error}}return a},r=this&&this.__spreadArray||function(e,t,n){if(n||2===arguments.length)for(var o,r=0,i=t.length;rn&&(e=n),e},t.adoptStyleSheet=c},187:function(e,t,n){(t=n(6314)(!1)).push([e.id,'.luna-setting{min-width:320px;color:rgba(0,0,0,.88);color:var(--luna-color-text,rgba(0,0,0,.88));background-color:rgba(0,0,0,0);font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";font-family:var(--luna-font-family, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji");box-sizing:border-box;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;font-size:14px}.luna-setting .luna-setting-hidden,.luna-setting.luna-setting-hidden{display:none}.luna-setting .luna-setting-invisible,.luna-setting.luna-setting-invisible{visibility:hidden}.luna-setting *{box-sizing:border-box}.luna-setting.luna-setting-theme-dark{color-scheme:dark;color:hsla(0,0%,100%,.85);color:var(--luna-color-text-dark,rgba(255,255,255,.85));background-color:rgba(0,0,0,0)}.luna-setting-item.luna-setting-selected:focus{outline:1px solid}.luna-setting-item .luna-setting-title{line-height:1.4em;font-weight:600}.luna-setting-item .luna-setting-description,.luna-setting-item.luna-setting-item-markdown{line-height:1.4em}.luna-setting-item .luna-setting-description *,.luna-setting-item.luna-setting-item-markdown *{margin:0}.luna-setting-item .luna-setting-description strong,.luna-setting-item.luna-setting-item-markdown strong{font-weight:600}.luna-setting-item .luna-setting-description a,.luna-setting-item.luna-setting-item-markdown a{background-color:rgba(0,0,0,0);text-decoration:none}.luna-setting-item .luna-setting-control,.luna-setting-item .luna-setting-description{font-size:12px}.luna-setting-item .luna-setting-description{margin-bottom:8px}.luna-setting-item .luna-setting-control{display:flex;align-items:center}.luna-setting-item-button,.luna-setting-item-checkbox,.luna-setting-item-input,.luna-setting-item-markdown,.luna-setting-item-number,.luna-setting-item-select,.luna-setting-item-title{padding:10px}.luna-setting-item-title{font-weight:600}.luna-setting-item-title.luna-setting-level-1{font-size:18px}.luna-setting-item-title.luna-setting-level-2{font-size:16px}.luna-setting-item-title.luna-setting-level-3{font-size:14px}.luna-setting-item-input.luna-setting-disabled input{opacity:.6}.luna-setting-item-input input{-webkit-tap-highlight-color:transparent;color:rgba(0,0,0,.88);-webkit-appearance:none;-moz-appearance:none;appearance:none;border:1px solid;outline:0;padding:2px 8px;border-radius:2px;font-size:14px;width:100%}.luna-setting-item-number.luna-setting-disabled .luna-setting-range-container,.luna-setting-item-number.luna-setting-disabled input{opacity:.6}.luna-setting-item-number.luna-setting-disabled .luna-setting-range-container input{opacity:1}.luna-setting-item-number input[type=number]{-webkit-tap-highlight-color:transparent;color:rgba(0,0,0,.88);-webkit-appearance:none;-moz-appearance:none;appearance:none;border:1px solid;outline:0;padding:2px 8px;border-radius:2px;font-size:14px;width:200px;padding:2px}.luna-setting-item-number .luna-setting-range-container{flex:2;position:relative;top:1px}.luna-setting-item-number .luna-setting-range-container .luna-setting-range-track{height:4px;width:100%;padding:0 10px;position:absolute;left:0;top:4px}.luna-setting-item-number .luna-setting-range-container .luna-setting-range-track .luna-setting-range-track-bar{border-radius:2px;overflow:hidden;width:100%;height:4px}.luna-setting-item-number .luna-setting-range-container .luna-setting-range-track .luna-setting-range-track-bar .luna-setting-range-track-progress{height:100%;width:50%}.luna-setting-item-number .luna-setting-range-container input{-webkit-appearance:none;background:rgba(0,0,0,0);height:4px;width:100%;position:relative;top:-3px;margin:0 auto;outline:0;border-radius:2px}.luna-setting-item-number .luna-setting-range-container input::-webkit-slider-thumb{-webkit-appearance:none;position:relative;top:0;z-index:1;width:16px;border:none;height:16px;border-radius:10px;border:1px solid}.luna-setting-item-checkbox.luna-setting-disabled .luna-setting-control{opacity:.6}.luna-setting-item-checkbox input{-webkit-appearance:none;-moz-appearance:none;appearance:none;width:14px;height:14px;border:1px solid;border-radius:0;position:relative;outline:0;margin-left:0;margin-right:8px;transition:background-color .1s;align-self:flex-start;flex-shrink:0}.luna-setting-item-checkbox input:checked:after{content:"";width:100%;height:100%;position:absolute;left:0;top:0;background-image:url(data:image/svg+xml;base64,PHN2ZyBoZWlnaHQ9JzMwMHB4JyB3aWR0aD0nMzAwcHgnICBmaWxsPSIjZmZmZmZmIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB2aWV3Qm94PSIwIDAgMTAwIDEwMCIgdmVyc2lvbj0iMS4xIiB4PSIwcHgiIHk9IjBweCI+PHRpdGxlPmljb25fYnlfUG9zaGx5YWtvdjEwPC90aXRsZT48ZGVzYz5DcmVhdGVkIHdpdGggU2tldGNoLjwvZGVzYz48ZyBzdHJva2U9Im5vbmUiIHN0cm9rZS13aWR0aD0iMSIgZmlsbD0ibm9uZSIgZmlsbC1ydWxlPSJldmVub2RkIj48ZyBmaWxsPSIjZmZmZmZmIj48ZyB0cmFuc2Zvcm09InRyYW5zbGF0ZSgyNi4wMDAwMDAsIDI2LjAwMDAwMCkiPjxwYXRoIGQ9Ik0xNy45OTk5ODc4LDMyLjQgTDEwLjk5OTk4NzgsMjUuNCBDMTAuMjI2Nzg5MSwyNC42MjY4MDE0IDguOTczMTg2NDQsMjQuNjI2ODAxNCA4LjE5OTk4Nzc5LDI1LjQgTDguMTk5OTg3NzksMjUuNCBDNy40MjY3ODkxNCwyNi4xNzMxOTg2IDcuNDI2Nzg5MTQsMjcuNDI2ODAxNCA4LjE5OTk4Nzc5LDI4LjIgTDE2LjU4NTc3NDIsMzYuNTg1Nzg2NCBDMTcuMzY2ODIyOCwzNy4zNjY4MzUgMTguNjMzMTUyOCwzNy4zNjY4MzUgMTkuNDE0MjAxNCwzNi41ODU3ODY0IEw0MC41OTk5ODc4LDE1LjQgQzQxLjM3MzE4NjQsMTQuNjI2ODAxNCA0MS4zNzMxODY0LDEzLjM3MzE5ODYgNDAuNTk5OTg3OCwxMi42IEw0MC41OTk5ODc4LDEyLjYgQzM5LjgyNjc4OTEsMTEuODI2ODAxNCAzOC41NzMxODY0LDExLjgyNjgwMTQgMzcuNzk5OTg3OCwxMi42IEwxNy45OTk5ODc4LDMyLjQgWiI+PC9wYXRoPjwvZz48L2c+PC9nPjwvc3ZnPg==);background-size:30px;background-repeat:no-repeat;background-position:center}.luna-setting-item-checkbox label{-webkit-tap-highlight-color:transparent}.luna-setting-item-checkbox label *{margin:0}.luna-setting-item-select.luna-setting-disabled .luna-setting-select{opacity:.6}.luna-setting-item-select .luna-setting-select{position:relative}.luna-setting-item-select .luna-setting-select select{margin:0;font-size:14px;-webkit-appearance:none;-moz-appearance:none;appearance:none;border:1px solid;padding:2px 8px;padding-right:18px;outline:0;border-radius:2px;-webkit-tap-highlight-color:transparent}.luna-setting-item-select .luna-setting-select:after{content:"";width:0;height:0;border-left:5px solid transparent;border-right:5px solid transparent;border-top:5px solid;position:absolute;top:0;bottom:0;right:6px;margin:auto;pointer-events:none}.luna-setting-item-select .luna-setting-select select{width:300px}.luna-setting-item-button button{-webkit-tap-highlight-color:transparent;border:1px solid;padding:2px 8px;font-size:14px;border-radius:2px}.luna-setting-item-button button:active{border:1px solid}.luna-setting-item-separator{border-bottom:1px solid}.luna-setting-theme-light .luna-setting-item.luna-setting-selected,.luna-setting-theme-light .luna-setting-item:hover{background-color:rgba(0,0,0,.06);background-color:var(--luna-color-fill-secondary,rgba(0,0,0,.06))}.luna-setting-theme-light .luna-setting-item.luna-setting-selected:focus{outline-color:#1a73e8;outline-color:var(--luna-color-primary,#1a73e8)}.luna-setting-theme-light .luna-setting-item .luna-setting-description a,.luna-setting-theme-light .luna-setting-item.luna-setting-item-markdown a{color:#1a73e8;color:var(--luna-color-primary,#1a73e8)}.luna-setting-theme-light .luna-setting-item-separator{border-color:#d9d9d9;border-color:var(--luna-color-border,#d9d9d9)}.luna-setting-theme-light .luna-setting-item-input input{background-color:#fff;background-color:var(--luna-color-bg-container,#fff);color:rgba(0,0,0,.88);color:var(--luna-color-text,rgba(0,0,0,.88));border-color:#d9d9d9;border-color:var(--luna-color-border,#d9d9d9)}.luna-setting-theme-light .luna-setting-item-checkbox input{border-color:#d9d9d9;border-color:var(--luna-color-border,#d9d9d9)}.luna-setting-theme-light .luna-setting-item-checkbox input:checked{background-color:#1a73e8;background-color:var(--luna-color-primary,#1a73e8);border-color:#1a73e8;border-color:var(--luna-color-primary,#1a73e8)}.luna-setting-theme-light .luna-setting-item-select .luna-setting-select select{background-color:#fff;background-color:var(--luna-color-bg-container,#fff);color:rgba(0,0,0,.88);color:var(--luna-color-text,rgba(0,0,0,.88));border-color:#d9d9d9;border-color:var(--luna-color-border,#d9d9d9)}.luna-setting-theme-light .luna-setting-item-select .luna-setting-select:after{border-top-color:rgba(0,0,0,.88);border-top-color:var(--luna-color-text,rgba(0,0,0,.88))}.luna-setting-theme-light .luna-setting-item-button button{background-color:#fff;background-color:var(--luna-color-bg-container,#fff);border-color:#d9d9d9;border-color:var(--luna-color-border,#d9d9d9);color:#1a73e8;color:var(--luna-color-primary,#1a73e8)}.luna-setting-theme-light .luna-setting-item-button button:active,.luna-setting-theme-light .luna-setting-item-button button:hover{background-color:rgba(0,0,0,.06);background-color:var(--luna-color-fill-secondary,rgba(0,0,0,.06))}.luna-setting-theme-light .luna-setting-item-button button:active{border-color:#1a73e8;border-color:var(--luna-color-primary,#1a73e8)}.luna-setting-theme-light .luna-setting-item-number input[type=number]{background-color:#fff;background-color:var(--luna-color-bg-container,#fff);color:rgba(0,0,0,.88);color:var(--luna-color-text,rgba(0,0,0,.88));border-color:#d9d9d9;border-color:var(--luna-color-border,#d9d9d9)}.luna-setting-theme-light .luna-setting-item-number .luna-setting-range-container .luna-setting-range-track .luna-setting-range-track-bar{background-color:#d9d9d9;background-color:var(--luna-color-border,#d9d9d9)}.luna-setting-theme-light .luna-setting-item-number .luna-setting-range-container .luna-setting-range-track .luna-setting-range-track-bar .luna-setting-range-track-progress{background-color:#1a73e8;background-color:var(--luna-color-primary,#1a73e8)}.luna-setting-theme-light .luna-setting-item-number .luna-setting-range-container input::-webkit-slider-thumb{border-color:#d9d9d9;border-color:var(--luna-color-border,#d9d9d9);background:radial-gradient(circle at center,#eee 0,#eee 15%,#fff 22%,#fff 100%)}.luna-setting-theme-dark .luna-setting-item.luna-setting-selected,.luna-setting-theme-dark .luna-setting-item:hover{background-color:hsla(0,0%,100%,.12);background-color:var(--luna-color-fill-secondary,rgba(255,255,255,.12))}.luna-setting-theme-dark .luna-setting-item.luna-setting-selected:focus{outline-color:#1965c8;outline-color:var(--luna-color-primary,#1965c8)}.luna-setting-theme-dark .luna-setting-item .luna-setting-description a,.luna-setting-theme-dark .luna-setting-item.luna-setting-item-markdown a{color:#1965c8;color:var(--luna-color-primary,#1965c8)}.luna-setting-theme-dark .luna-setting-item-separator{border-color:#424242;border-color:var(--luna-color-border,#424242)}.luna-setting-theme-dark .luna-setting-item-input input{background-color:#141414;background-color:var(--luna-color-bg-container,#141414);color:hsla(0,0%,100%,.85);color:var(--luna-color-text,rgba(255,255,255,.85));border-color:#424242;border-color:var(--luna-color-border,#424242)}.luna-setting-theme-dark .luna-setting-item-checkbox input{border-color:#424242;border-color:var(--luna-color-border,#424242)}.luna-setting-theme-dark .luna-setting-item-checkbox input:checked{background-color:#1965c8;background-color:var(--luna-color-primary,#1965c8);border-color:#1965c8;border-color:var(--luna-color-primary,#1965c8)}.luna-setting-theme-dark .luna-setting-item-select .luna-setting-select select{background-color:#141414;background-color:var(--luna-color-bg-container,#141414);color:hsla(0,0%,100%,.85);color:var(--luna-color-text,rgba(255,255,255,.85));border-color:#424242;border-color:var(--luna-color-border,#424242)}.luna-setting-theme-dark .luna-setting-item-select .luna-setting-select:after{border-top-color:hsla(0,0%,100%,.85);border-top-color:var(--luna-color-text,rgba(255,255,255,.85))}.luna-setting-theme-dark .luna-setting-item-button button{background-color:#141414;background-color:var(--luna-color-bg-container,#141414);border-color:#424242;border-color:var(--luna-color-border,#424242);color:#1965c8;color:var(--luna-color-primary,#1965c8)}.luna-setting-theme-dark .luna-setting-item-button button:active,.luna-setting-theme-dark .luna-setting-item-button button:hover{background-color:hsla(0,0%,100%,.12);background-color:var(--luna-color-fill-secondary,rgba(255,255,255,.12))}.luna-setting-theme-dark .luna-setting-item-button button:active{border-color:#1965c8;border-color:var(--luna-color-primary,#1965c8)}.luna-setting-theme-dark .luna-setting-item-number input[type=number]{background-color:#141414;background-color:var(--luna-color-bg-container,#141414);color:hsla(0,0%,100%,.85);color:var(--luna-color-text,rgba(255,255,255,.85));border-color:#424242;border-color:var(--luna-color-border,#424242)}.luna-setting-theme-dark .luna-setting-item-number .luna-setting-range-container .luna-setting-range-track .luna-setting-range-track-bar{background-color:#424242;background-color:var(--luna-color-border,#424242)}.luna-setting-theme-dark .luna-setting-item-number .luna-setting-range-container .luna-setting-range-track .luna-setting-range-track-bar .luna-setting-range-track-progress{background-color:#1965c8;background-color:var(--luna-color-primary,#1965c8)}.luna-setting-theme-dark .luna-setting-item-number .luna-setting-range-container input::-webkit-slider-thumb{border-color:#424242;border-color:var(--luna-color-border,#424242);background:radial-gradient(circle at center,#aaa 0,#aaa 15%,#ccc 22%,#ccc 100%)}',""]),e.exports=t},199:function(e,t,n){var o=n(3089),r=n(7468);t=function(e){return e=o({},e),function(t){return r(t,e)}},e.exports=t},282:function(e,t,n){var o=n(438),r=n(9405),i=n(3915),a=n(769);t=function(e){var t=a(e.match(s));return o(i(t,(function(e){return r(e)})))};var s=/((https?)|(ftp)):\/\/[\w.]+[^ \f\n\r\t\v"\\<>[\]\u2100-\uFFFF(),]*/gi;e.exports=t},311:function(e,t,n){var o=n(3957),r=n(9993),i=n(5651),a=n(9760),s=n(7257);function c(e,t,n,r){return o(t)&&(r=n,n=t,t={}),{url:e,data:t,success:n,dataType:r}}(t=function(e){i(e,t.setting);var n,o=e.type,c=e.url,l=e.data,u=e.dataType,d=e.success,h=e.error,f=e.timeout,p=e.complete,v=e.xhr();return v.onreadystatechange=function(){if(4===v.readyState){var e;clearTimeout(n);var t=v.status;if(t>=200&&t<300||304===t){e=v.responseText,"xml"===u&&(e=v.responseXML);try{"json"===u&&(e=JSON.parse(e))}catch(e){}d(e,v)}else h(v);p(v)}},"GET"===o?(l=s.stringify(l))&&(c+=c.indexOf("?")>-1?"&"+l:"?"+l):"application/x-www-form-urlencoded"===e.contentType?a(l)&&(l=s.stringify(l)):"application/json"===e.contentType&&a(l)&&(l=JSON.stringify(l)),v.open(o,c,!0),v.setRequestHeader("Content-Type",e.contentType),f>0&&(n=setTimeout((function(){v.onreadystatechange=r,v.abort(),h(v,"timeout"),p(v)}),f)),v.send("GET"===o?null:l),v}).setting={type:"GET",success:r,error:r,complete:r,dataType:"json",contentType:"application/x-www-form-urlencoded",data:{},xhr:function(){return new XMLHttpRequest},timeout:0},t.get=function(){return t(c.apply(null,arguments))},t.post=function(){var e=c.apply(null,arguments);return e.type="POST",t(e)},e.exports=t},365:function(e,t){var n=Object.prototype.hasOwnProperty;t=function(e,t){return n.call(e,t)},e.exports=t},383:function(e,t,n){var o=n(3497),r=n(1849),i=n(1009),a=n(96);t=function(e,t){for(var n,r=[],h=e;e;){if(n=!0,o(r)&&d[o(r)]){var f=new RegExp("]*>")).exec(e);if(f){var p=e.substring(0,f.index);e=e.substring(f.index+f[0].length),p&&t.text&&t.text(p)}_("",o(r))}else{if(i(e,"\x3c!--")){var v=e.indexOf("--\x3e");v>=0&&(t.comment&&t.comment(e.substring(4,v)),e=e.substring(v+3),n=!1)}else if(i(e,"=0&&r[o]!==n;o--);else o=0;if(o>=0){for(var i=r.length-1;i>=o;i--)t.end&&t.end(r[i]);r.length=o}}_()};var s=/^\s]+))?)*)\s*(\/?)>/i,c=/^<\/([-A-Za-z0-9_]+)[^>]*>/,l=/^<([-A-Za-z0-9_]+)((?:\s+[-A-Za-z0-9_:@.]+(?:\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|[^>\s]+))?)*)\s*(\/?)>/i,u=/([-A-Za-z0-9_:@.]+)(?:\s*=\s*(?:(?:"((?:\\.|[^"])*)")|(?:'((?:\\.|[^'])*)')|([^>\s]+)))?/g,d=r("script,style".split(","));e.exports=t},387:function(e,t,n){var o=n(6833);function r(e,t){this[t]=e.replace(/\w/,(function(e){return e.toUpperCase()}))}t=function(e){var t=o(e),n=t[0];return t.shift(),t.forEach(r,t),n+=t.join("")},e.exports=t},388:function(e,t,n){"use strict";n.d(t,{A:function(){return r}});var o=n(2284);function r(e,t){if(t&&("object"==(0,o.A)(t)||"function"==typeof t))return t;if(void 0!==t)throw new TypeError("Derived constructors may only return object or undefined");return function(e){if(void 0===e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return e}(e)}},438:function(e,t,n){var o=n(2571);function r(e,t){return e===t}t=function(e,t){return t=t||r,o(e,(function(e,n,o){for(var r=o.length;++n0&&r[r.length-1])||6!==s[0]&&2!==s[0])){i=0;continue}if(3===s[0]&&(!r||s[1]>r[0]&&s[1]A&&xw,S=f-y;S=(0,c.constrainNumber)(S,m,r-p-m);var E=o.minY-g-v,T=!0;E<0?(E=Math.min(i-v,o.maxY+g),T=!1):o.minY>i&&(E=i-g-v);var N=S>=o.minX&&S+p<=o.maxX&&E>=o.minY&&E+v<=o.maxY,O=So.minX&&Eo.minY;if(O&&!N)return void(d.style.display="none");if(d.style.top=E+"px",d.style.left=S+"px",C)return;var j=(0,c.createChild)(d,"div","tooltip-arrow");j.style.clipPath=T?"polygon(0 0, 100% 0, 50% 100%)":"polygon(50% 0, 0 100%, 100% 100%)",j.style.top=(T?v-1:-g)+"px",j.style.left=f-S+"px"}(this.tooltip,e.elementInfo,e.colorFormat,t,this.canvasWidth,this.canvasHeight)),this.context.restore(),{bounds:t}},t.prototype.drawAxis=function(e,t,n){e.save();var o=this.pageZoomFactor*this.pageScaleFactor*this.emulationScaleFactor,r=this.scrollX*this.pageScaleFactor,i=this.scrollY*this.pageScaleFactor;function a(e){return Math.round(e*o)}function s(e){return Math.round(e/o)}var c=this.canvasWidth/o,l=this.canvasHeight/o,u=50;e.save(),e.fillStyle=f,n?e.fillRect(0,a(l)-15,a(c),a(l)):e.fillRect(0,0,a(c),15),e.globalCompositeOperation="destination-out",e.fillStyle="red",t?e.fillRect(a(c)-15,0,a(c),a(l)):e.fillRect(0,0,15,a(l)),e.restore(),e.fillStyle=f,t?e.fillRect(a(c)-15,0,a(c),a(l)):e.fillRect(0,0,15,a(l)),e.lineWidth=1,e.strokeStyle=h,e.fillStyle=h,e.save(),e.translate(-r,.5-i);for(var p=l+s(i),v=100;v1&&o--,r=6*o<1?l+6*(t-l)*o:2*o<1?t:3*o<2?l+(t-l)*(2/3-o)*6:l,c[u]=n(255*r);return c};var n=Math.round;e.exports=t},961:function(e,t,n){var o=n(5693);t=function(e,t,n){var r=[];t=o(t,n);for(var i=-1,a=e.length;++i-1&&this._listeners.splice(t,1)},rmAllListeners:function(){this._listeners=[]},emit:function(){var e=this,t=a(arguments),n=r(this._listeners);i(n,(function(n){return n.apply(e,t)}),this)}},{mixin:function(e){i(["addListener","rmListener","emit","rmAllListeners"],(function(n){e[n]=t.prototype[n]})),e._listeners=e._listeners||[]}}),e.exports=t},1034:function(e,t,n){var o=n(9760),r=n(3957),i=n(6214),a=n(5154);t=function(e){return i(e)?e.map((function(e){return t(e)})):o(e)&&!r(e)?a(e,(function(e){return t(e)})):e},e.exports=t},1107:function(e,t,n){(t=n(6314)(!1)).push([e.id,"@font-face{font-family:luna-text-viewer-icon;src:url('data:application/x-font-woff;charset=utf-8;base64,d09GRgABAAAAAAS0AAsAAAAAB2QAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAABHU1VCAAABCAAAAFQAAAB0INElr09TLzIAAAFcAAAAPQAAAFZL+0klY21hcAAAAZwAAACfAAACEAEewxRnbHlmAAACPAAAAIYAAACkNSDggmhlYWQAAALEAAAALgAAADZzrb4oaGhlYQAAAvQAAAAWAAAAJAGRANNobXR4AAADDAAAABAAAAAoAZAAAGxvY2EAAAMcAAAAEAAAABYBWgFIbWF4cAAAAywAAAAdAAAAIAEXADtuYW1lAAADTAAAASkAAAIWm5e+CnBvc3QAAAR4AAAAOwAAAFJIWdOleJxjYGRgYOBiMGCwY2BycfMJYeDLSSzJY5BiYGGAAJA8MpsxJzM9kYEDxgPKsYBpDiBWAdNMDGwMQkAWK1CGlYEZyGMCstiBMpxAUUYGZgDbGgXDeJxjYGTQYJzAwMrAwFDH0AMkZaB0AgMngzEDAxMDKzMDVhCQ5prCcIAh+SMTwwkgVwhMMjAwgggAY84IrgAAAHicvZFLCsMwDERHzsdJ6aL0HD1VQiDQRbIN9Axd9aI+QTpjq5Bdd5F4Bo1lybIBNAAq8iA1YB8YZG+qlvUKl6zXGBjf6MofMWHGEyu2FPb9oCxULCtHs3yy+J2urg1rtojo0HM/MKnFGabOGlbdYvdT+1N6/7drXl8e6Vajo3efHP3b7HAUvntBMy1OJKujMTeHNZMV9McpFBC+tLgY4QB4nGNgZACBEwzrGdgZGOwZxdnVDdXNPfKEGlhchO0KhZtZ3IQYmMFq1jCsZpBi0GLQY2AwNzGzZjQSk2UUYdNmVFID8UyVRUXYlNRMlVGlTM1FjU3tmZkTmVhYmFRBhHwoCyuzKgtTIjMzWJg3ZClIGMRlZQmVB7GhMixM0aGhQIsB52sTqgAAeJxjYGRgYADi2JNxkvH8Nl8ZuBlOAAWiOB/va0DQQHCCYT2Q5GBgAnEANJ0KnQAAeJxjYGRgYDjBwIBEMjKgAi4AOvoCZQAAeJxjYACCE1CMBwAAM7gBkXicY2AAAiGGIFQIABXIAqN4nGNgZGBg4GLQZ2BmAAEmMI8LSP4H8xkADjQBUwAAAHicZZA9bsJAFITHYEgCUoIUKSmzVQoimZ+SA0BPQZfCmLUxsr3WekGiywlyhBwhp4hyghwoY/NoYC0/fzNv3u7KAAb4hYd6ebhtar1auKE6cZv0IOyTn4U76ONFuEt/KNzDG6bCfTzinTt4/h2dAUrhFu7xIdym/ynsk7+EO3jCt3CX/o9wDyv8Cffx6g3TyBSxKdxSJ/sstGd5/q60rVJTqEkwPlsLXWgbOr1R66OqDsnUuVjF1uRqzq7OMqNKa3Y6csHWuXI2GsXiB5HJkSKCQYG4qQ5LaCTYI0MIe9W91CumLSr6tVaYIMD4KrVgqmiSIZXGhsk1jqwVDjxtStcxrfhazuSkucxq3iQjK/7vurejE9EPsG2mSsww4hNf5IPmDvk/PRFeqAAAAHicXcU7CsAgFEXBe4x/l/kQBAtt3X0KSZNpRk7X91/F8eAJRBKZQqUp2Og2va19MAadyWJzpBd4kgcWAA==') format('woff')}[class*=' luna-text-viewer-icon-'],[class^=luna-text-viewer-icon-]{display:inline-block;font-family:luna-text-viewer-icon!important;font-size:16px;font-style:normal;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.luna-text-viewer-icon-check:before{content:'\\f101'}.luna-text-viewer-icon-copy:before{content:'\\f102'}.luna-text-viewer{color:#333;background-color:#fff;font-family:Arial,Helvetica,sans-serif;box-sizing:border-box;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;font-size:14px;padding:0;unicode-bidi:embed;position:relative;overflow:auto;border:1px solid #ccc}.luna-text-viewer.luna-text-viewer-platform-windows{font-family:'Segoe UI',Tahoma,sans-serif}.luna-text-viewer.luna-text-viewer-platform-linux{font-family:Roboto,Ubuntu,Arial,sans-serif}.luna-text-viewer .luna-text-viewer-hidden,.luna-text-viewer.luna-text-viewer-hidden{display:none}.luna-text-viewer .luna-text-viewer-invisible,.luna-text-viewer.luna-text-viewer-invisible{visibility:hidden}.luna-text-viewer *{box-sizing:border-box}.luna-text-viewer.luna-text-viewer-theme-dark{color:#d9d9d9;border-color:#3d3d3d;background:#242424}.luna-text-viewer:hover .luna-text-viewer-copy{opacity:1}.luna-text-viewer-table{display:table}.luna-text-viewer-table .luna-text-viewer-line-number,.luna-text-viewer-table .luna-text-viewer-line-text{padding:0}.luna-text-viewer-table-row{display:table-row}.luna-text-viewer-line-number{display:table-cell;padding:0 3px 0 8px!important;text-align:right;vertical-align:top;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;border-right:1px solid #ccc}.luna-text-viewer-line-text{display:table-cell;padding-left:4px!important;-webkit-user-select:text;-moz-user-select:text;-ms-user-select:text;user-select:text}.luna-text-viewer-copy{background:#fff;opacity:0;position:absolute;right:5px;top:5px;border:1px solid #ccc;border-radius:4px;width:25px;height:25px;text-align:center;line-height:25px;cursor:pointer;transition:opacity .3s,top .3s}.luna-text-viewer-copy .luna-text-viewer-icon-check{color:#188037}.luna-text-viewer-text{padding:4px;font-size:12px;font-family:ui-monospace,SFMono-Regular,SF Mono,Menlo,Consolas,Liberation Mono,monospace;box-sizing:border-box;white-space:pre;display:block}.luna-text-viewer-text.luna-text-viewer-line-numbers{padding:0}.luna-text-viewer-text.luna-text-viewer-wrap-long-lines{white-space:pre-wrap}.luna-text-viewer-text.luna-text-viewer-wrap-long-lines .luna-text-viewer-line-text{word-break:break-all}.luna-text-viewer-theme-dark{color-scheme:dark}.luna-text-viewer-theme-dark .luna-text-viewer-copy,.luna-text-viewer-theme-dark .luna-text-viewer-line-number{border-color:#3d3d3d}.luna-text-viewer-theme-dark .luna-text-viewer-copy .luna-text-viewer-icon-check{color:#81c995}.luna-text-viewer-theme-dark .luna-text-viewer-copy{background-color:#242424}",""]),e.exports=t},1162:function(e,t,n){"use strict";n.d(t,{Ay:function(){return ve}});var o=n(991),r=n(3029),i=n(2901),a=n(388),s=n(3954),c=n(5361),l=n(3693),u=n.n(l),d=n(5241),h=n.n(d),f=n(5902),p=n.n(f),v=n(5630),g=n.n(v),m=n(9760),b=n.n(m),y=n(5651),A=n.n(y),w=n(3915),_=n.n(w),x=n(6030),k=n.n(x),C=n(2561),S=n.n(C),E=n(3957),T=n.n(E),N=n(3497),O=n.n(N),j=n(1976),M=n.n(j),I=n(1738),z=n.n(I),D=n(9405),B=n.n(D),F=n(3249),R=n.n(F),L=n(96),G=n.n(L),P=n(4236),H=n.n(P),Y=n(9100),$=n.n(Y);var q=n(2263),Q=n.n(q),U=n(5169),W=n.n(U),J=n(9548),K=n.n(J),V=(n(6097),n(5004)),Z=n.n(V);n(9410),n(8609);function X(e){var t="luna-".concat(e,"-");function n(e){return _()(B()(e).split(/\s+/),(function(e){return R()(e,t)?e:e.replace(/[\w-]+/,(function(e){return"".concat(t).concat(e)}))})).join(" ")}return function(e){if(/<[^>]*>/g.test(e))try{var t=K().parse(e);return ee(t,(function(e){e.attrs&&e.attrs.class&&(e.attrs.class=n(e.attrs.class))})),K().stringify(t)}catch(t){return n(e)}return n(e)}}function ee(e,t){for(var n=0,o=e.length;n2&&void 0!==arguments[2]?arguments[2]:{}).theme,d=void 0===l?"light":l;return(0,r.A)(this,t),o=function(e,t,n){return t=(0,s.A)(t),(0,a.A)(e,le()?Reflect.construct(t,n||[],(0,s.A)(e).constructor):t.apply(e,n))}(this,t),o.subComponents=[],o.theme="",o.onThemeChange=function(e){"auto"===o.options.theme&&o.setTheme(e)},o.compName=c,o.c=X(c),o.options={},o.container=e,o.$container=u()(e),o.$container.addClass(["luna-".concat(c),o.c("platform-".concat((i=Z()(),"os x"===i?"mac":i)))]),o.on("changeOption",(function(e,t){if("theme"===e&&t){var n=t;"auto"===t&&(n=ae().get()),o.setTheme(n),$()(o.subComponents,(function(e){return e.setOption("theme",t)}))}})),ae().on("change",o.onThemeChange),o.setOption("theme",d),o}return(0,c.A)(t,e),(0,i.A)(t,[{key:"destroy",value:function(){var e=this;this.destroySubComponents();var t=this.$container,n=t.attr("class");$()(n.split(/\s+/),(function(n){ce()(n,"luna-".concat(e.compName))&&t.rmClass(n)})),t.html(""),this.emit("destroy"),this.removeAllListeners(),ae().off("change",this.onThemeChange)}},{key:"setOption",value:function(e,t){var n=this,o=this.options,r={};"string"==typeof e?r[e]=t:r=e,$()(r,(function(e,t){var r=o[t];o[t]=e,e!==r&&n.emit("changeOption",t,e,r)}))}},{key:"getOption",value:function(e){return this.options[e]}},{key:"addSubComponent",value:function(e){e.setOption("theme",this.options.theme),this.subComponents.push(e)}},{key:"removeSubComponent",value:function(e){re()(this.subComponents,(function(t){return t===e}))}},{key:"destroySubComponents",value:function(){$()(this.subComponents,(function(e){return e.destroy()})),this.subComponents=[]}},{key:"initOptions",value:function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};A()(e,t),ne()(this.options,e)}},{key:"find",value:function(e){return this.$container.find(this.c(e))}},{key:"setTheme",value:function(e){var t=this.c,n=this.$container;this.theme&&n.rmClass(t("theme-".concat(this.theme))),n.addClass(t("theme-".concat(e))),this.theme=e}}])}(Q()),de=function(e,t,n){return((e-t)/(n-t)*100).toFixed(2)};function he(e,t,n,r){var i=(0,o.A)((0,s.A)(1&r?e.prototype:e),t,n);return 2&r&&"function"==typeof i?function(e){return i.apply(n,e)}:i}function fe(e,t,n){return t=(0,s.A)(t),(0,a.A)(e,pe()?Reflect.construct(t,n||[],(0,s.A)(e).constructor):t.apply(e,n))}function pe(){try{var e=!Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){})))}catch(e){}return(pe=function(){return!!e})()}e=n.hmd(e);var ve=function(e){function t(e){var n,o=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};return(0,r.A)(this,t),(n=fe(this,t,[e,{compName:"setting"},o])).items=[],n.selectedItem=null,n.initOptions(o,{separatorCollapse:!0,filter:""}),n.bindEvent(),n}return(0,c.A)(t,e),(0,i.A)(t,[{key:"appendTitle",value:function(e){var t=new me(this,e,arguments.length>1&&void 0!==arguments[1]?arguments[1]:1);return this.append(t),t}},{key:"appendSeparator",value:function(){var e=this.items,t=this.options.separatorCollapse,n=O()(e);if(t&&n instanceof ye)return n;var o=new ye(this);return this.append(o),o}},{key:"appendNumber",value:function(e,t,n,o,r){b()(o)&&(r=o,o="");var i=new we(this,e,t,n,o,r);return this.append(i),i}},{key:"appendButton",value:function(e,t,n){T()(t)&&(n=t,t="");var o=new ke(this,e,t,n);return this.append(o),o}},{key:"appendHtml",value:function(e){var t=new Ce(this,e);return this.append(t),t}},{key:"appendMarkdown",value:function(e){var t=new be(this,e);return this.append(t),t}},{key:"appendInput",value:function(e,t,n){var o=new Ae(this,e,t,n,arguments.length>3&&void 0!==arguments[3]?arguments[3]:"");return this.append(o),o}},{key:"appendCheckbox",value:function(e,t,n,o){o||(o=n,n="");var r=new _e(this,e,t,n,o);return this.append(r),r}},{key:"appendSelect",value:function(e,t,n,o,r){b()(o)&&(r=o,o="");var i=new xe(this,e,t,n,o,r);return this.append(i),i}},{key:"remove",value:function(e){var t=this.items,n=t.indexOf(e);n>-1&&(e.detach(),t.splice(n,1),e===this.selectedItem&&this.selectItem(null))}},{key:"clear",value:function(){$()(this.items,(function(e){return e.detach()})),this.items=[],this.selectItem(null)}},{key:"selectItem",value:function(e){var t;(this.selectedItem&&(this.selectedItem.deselect(),this.selectedItem=null),H()(e))||(this.selectedItem=e,null===(t=this.selectedItem)||void 0===t||t.select())}},{key:"renderSettings",value:function(){var e=this,t=this.items;$()(t,(function(e){return e.detach()})),$()(t,(function(t){e.filterItem(t)&&e.$container.append(t.container)}))}},{key:"bindEvent",value:function(){var e=this,t=this.c;this.on("changeOption",(function(t){if("filter"===t)e.renderSettings()}));var n=this;this.$container.on("click",t(".item"),(function(){n.selectItem(this.settingItem)}))}},{key:"filterItem",value:function(e){var t=this.options.filter;if(t){if(T()(t))return t(e);if(M()(t))return t.test(e.text());if(z()(t)&&(t=B()(t)))return R()(G()(e.text()),G()(t))}return!0}},{key:"append",value:function(e){this.items.push(e),this.filterItem(e)&&this.$container.append(e.container)}}])}(ue),ge=function(){return(0,i.A)((function e(t,n,o,i){(0,r.A)(this,e),this.container=h()("div",{tabindex:"0"}),this.setting=t,this.container.settingItem=this,this.$container=u()(this.container),this.$container.addClass(t.c("item")).addClass(t.c("item-".concat(i))),this.key=n,this.value=o}),[{key:"select",value:function(){this.$container.addClass(this.setting.c("selected"))}},{key:"deselect",value:function(){this.$container.rmClass(this.setting.c("selected"))}},{key:"detach",value:function(){this.$container.remove()}},{key:"disable",value:function(){this.$container.addClass(this.setting.c("disabled"))}},{key:"enable",value:function(){this.$container.rmClass(this.setting.c("disabled"))}},{key:"text",value:function(){return this.$container.text()}},{key:"onChange",value:function(e){this.value!==e&&(this.setting.emit("change",this.key,e,this.value),this.value=e)}}])}(),me=function(e){function t(e,n,o){var i;return(0,r.A)(this,t),(i=fe(this,t,[e,"","","title"])).$container.addClass(e.c("level-".concat(o))),i.$container.text(n),i}return(0,c.A)(t,e),(0,i.A)(t)}(ge),be=function(e){function t(e,n){var o;return(0,r.A)(this,t),(o=fe(this,t,[e,"","","markdown"])).$container.html(n),o}return(0,c.A)(t,e),(0,i.A)(t)}(ge),ye=function(e){function t(e){return(0,r.A)(this,t),fe(this,t,[e,"","","separator"])}return(0,c.A)(t,e),(0,i.A)(t)}(ge),Ae=function(e){function t(e,n,o,i,a){var s;(0,r.A)(this,t),s=fe(this,t,[e,n,o,"input"]);var c=e.c;s.$container.html('
').concat(p()(i),'
\n
').concat(a,'
\n
\n \n
'));var l=s.$container.find("input");return l.val(o),l.on("change",(function(){return s.onChange(l.val())})),s.$input=l,s}return(0,c.A)(t,e),(0,i.A)(t,[{key:"setValue",value:function(e){this.$input.val(e),this.value=e}},{key:"disable",value:function(){he(t,"disable",this,3)([]),this.$input.attr("disabled","")}},{key:"enable",value:function(){he(t,"enable",this,3)([]),this.$input.rmAttr("disabled")}}])}(ge),we=function(e){function t(e,n,o,i,a){var s,c=arguments.length>5&&void 0!==arguments[5]?arguments[5]:{};(0,r.A)(this,t),(s=fe(this,t,[e,n,o,"number"])).renderRange=function(){var e=k()(s.$input.val()),t=s.options;s.$trackProgress.css("width",de(e,t.min,t.max)+"%"),s.$value.text(S()(e))},A()(c,{min:0,max:10,step:1}),s.options=c;var l=s.$container,u=e.c,d=!!c.range;delete c.range;var h=c.min,f=c.max,v='");d&&(v="".concat(h,'
\n
\n
\n
\n
\n
\n ').concat(v,'\n
').concat(o,"/").concat(f)),l.html('
').concat(p()(i),'
\n
').concat(a,'
\n
').concat(v,"
"));var g=l.find("input");return s.$value=l.find(u(".value")),s.$trackProgress=l.find(u(".range-track-progress")),g.val(S()(o)),g.on("change",(function(){var e=k()(g.val());s.onChange(e)})),g.on("input",s.renderRange),s.$input=g,s}return(0,c.A)(t,e),(0,i.A)(t,[{key:"setValue",value:function(e){this.$input.val(S()(e)),this.value=e,this.renderRange()}},{key:"disable",value:function(){he(t,"disable",this,3)([]),this.$input.attr("disabled","")}},{key:"enable",value:function(){he(t,"enable",this,3)([]),this.$input.rmAttr("disabled")}}])}(ge),_e=function(e){function t(e,n,o,i,a){var s;(0,r.A)(this,t),s=fe(this,t,[e,n,o,"checkbox"]);var c=e.c,l=g()(e.c("checkbox-"));s.$container.html('
').concat(p()(i),'
\n
\n \n \n
"));var u=s.$container.find("input"),d=u.get(0);return d.checked=o,u.on("change",(function(){return s.onChange(d.checked)})),s.$input=u,s.input=d,s}return(0,c.A)(t,e),(0,i.A)(t,[{key:"setValue",value:function(e){this.input.checked=e,this.value=e}},{key:"disable",value:function(){he(t,"disable",this,3)([]),this.$input.attr("disabled","")}},{key:"enable",value:function(){he(t,"enable",this,3)([]),this.$input.rmAttr("disabled")}}])}(ge),xe=function(e){function t(e,n,o,i,a,s){var c;(0,r.A)(this,t),c=fe(this,t,[e,n,o,"select"]);var l=e.c;c.$container.html('
').concat(p()(i),'
\n
').concat(a,'
\n
\n
\n \n
\n
'));var u=c.$container.find("select");return c.$select=u,c.setOptions(s),u.on("change",(function(){return c.onChange(u.val())})),c}return(0,c.A)(t,e),(0,i.A)(t,[{key:"setValue",value:function(e){this.$select.val(e),this.value=e}},{key:"setOptions",value:function(e){var t=this;this.$select.html(_()(e,(function(e,n){return'")})).join(""))}},{key:"disable",value:function(){he(t,"disable",this,3)([]),this.$select.attr("disabled","")}},{key:"enable",value:function(){he(t,"enable",this,3)([]),this.$select.rmAttr("disabled")}}])}(ge),ke=function(e){function t(e,n,o,i){var a;return(0,r.A)(this,t),o||(o=n,n=""),(a=fe(this,t,[e,"","","button"])).$container.html(e.c('
'.concat(p()(n),'
\n
\n \n
"))),a.$container.find("button").on("click",i),a}return(0,c.A)(t,e),(0,i.A)(t)}(ge),Ce=function(e){function t(e,n){var o;return(0,r.A)(this,t),(o=fe(this,t,[e,"","","html"])).$container.append(n),o}return(0,c.A)(t,e),(0,i.A)(t)}(ge);(function(e,t){try{e.exports=t,e.exports.default=t}catch(e){}})(e,ve)},1167:function(e,t,n){var o=n(1909),r=n(5869),i=/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino/i,a=/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw-(n|u)|c55\/|capi|ccwa|cdm-|cell|chtm|cldc|cmd-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc-s|devi|dica|dmob|do(c|p)o|ds(12|-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(-|_)|g1 u|g560|gene|gf-5|g-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd-(m|p|t)|hei-|hi(pt|ta)|hp( i|ip)|hs-c|ht(c(-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i-(20|go|ma)|i230|iac( |-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|-[a-w])|libw|lynx|m1-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|-([1-8]|c))|phil|pire|pl(ay|uc)|pn-2|po(ck|rt|se)|prox|psio|pt-g|qa-a|qc(07|12|21|32|60|-[2-7]|i-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h-|oo|p-)|sdk\/|se(c(-|0|1)|47|mc|nd|ri)|sgh-|shar|sie(-|m)|sk-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h-|v-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl-|tdg-|tel(i|m)|tim-|t-mo|to(pl|sh)|ts(70|m-|m3|m5)|tx-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas-|your|zeto|zte-/i;t=r((function(e){return e=e||(o?navigator.userAgent:""),i.test(e)||a.test(e.substr(0,4))})),e.exports=t},1168:function(e,t,n){var o=n(3422);t=function(e){if(o(e))return"";try{return r.call(e)}catch(e){}try{return e+""}catch(e){}return""};var r=Function.prototype.toString;e.exports=t},1277:function(e,t,n){(t=n(6314)(!1)).push([e.id,'.luna-box-model{display:inline-block;text-align:center;white-space:nowrap;color:rgba(0,0,0,.88);color:var(--luna-color-text,rgba(0,0,0,.88));background-color:#fff;background-color:var(--luna-color-bg-container,#fff);font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";font-family:var(--luna-font-family, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji");box-sizing:border-box;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;font-size:14px}.luna-box-model .luna-box-model-hidden,.luna-box-model.luna-box-model-hidden{display:none}.luna-box-model .luna-box-model-invisible,.luna-box-model.luna-box-model-invisible{visibility:hidden}.luna-box-model *{box-sizing:border-box}.luna-box-model.luna-box-model-theme-dark{color-scheme:dark;color:hsla(0,0%,100%,.85);color:var(--luna-color-text-dark,rgba(255,255,255,.85));background-color:#141414;background-color:var(--luna-color-bg-container-dark,#141414)}.luna-box-model{font-size:12px}.luna-box-model-label{position:absolute;margin-left:3px;padding:0 2px}.luna-box-model-bottom,.luna-box-model-left,.luna-box-model-right,.luna-box-model-top{display:inline-block}.luna-box-model-left,.luna-box-model-right{vertical-align:middle}.luna-box-model-border,.luna-box-model-content,.luna-box-model-margin,.luna-box-model-padding,.luna-box-model-position{position:relative;display:inline-block;text-align:center;vertical-align:middle;padding:3px;margin:3px;color:rgba(0,0,0,.88);background:#fff}.luna-box-model-position{border:1px gray dotted}.luna-box-model-margin{border:1px dashed}.luna-box-model-margin.luna-box-model-highlighted{color:rgba(0,0,0,.88)!important;background:rgba(246,178,107,.66)!important}.luna-box-model-border{border:1px #000 solid}.luna-box-model-border.luna-box-model-highlighted{color:rgba(0,0,0,.88)!important;background:rgba(255,229,153,.66)!important}.luna-box-model-padding{border:1px gray dashed}.luna-box-model-padding.luna-box-model-highlighted{color:rgba(0,0,0,.88)!important;background:rgba(147,196,125,.55)!important}.luna-box-model-content{border:1px gray solid;min-width:100px}.luna-box-model-content.luna-box-model-highlighted{color:rgba(0,0,0,.88)!important;background:rgba(111,168,220,.66)!important}.luna-box-model-theme-dark .luna-box-model-border,.luna-box-model-theme-dark .luna-box-model-content,.luna-box-model-theme-dark .luna-box-model-margin,.luna-box-model-theme-dark .luna-box-model-padding,.luna-box-model-theme-dark .luna-box-model-position{color:hsla(0,0%,100%,.85);background:#141414}.luna-box-model-theme-dark .luna-box-model-border{border-color:gray}',""]),e.exports=t},1443:function(e,t,n){(t=n(6314)(!1)).push([e.id,'#_resources{overflow-y:auto;-webkit-overflow-scrolling:touch;padding:10px;font-size:14px}#_resources ._section{margin-bottom:10px;overflow:hidden;border:1px solid var(--border)}#_resources ._section._warn{border:1px solid var(--console-warn-border)}#_resources ._section._warn ._title{background:var(--console-warn-background);color:var(--console-warn-foreground)}#_resources ._section._danger{border:1px solid var(--console-error-border)}#_resources ._section._danger ._title{background:var(--console-error-background);color:var(--console-error-foreground)}#_resources ._section._cookie,#_resources ._section._local-storage,#_resources ._section._session-storage{border:none}#_resources ._section._cookie ._title,#_resources ._section._local-storage ._title,#_resources ._section._session-storage ._title{border:1px solid var(--border);border-bottom:none}#_resources ._title{padding:10px;line-height:18px;color:var(--primary);background:var(--darker-background)}#_resources ._title ._btn{margin-left:5px;float:right;color:var(--primary);width:18px;height:18px;font-size:16px;cursor:pointer;transition:color .3s}#_resources ._title ._btn._filter-text{width:auto;max-width:80px;font-size:14px;overflow:hidden;font-weight:400;text-overflow:ellipsis;display:inline-block}#_resources ._title ._btn:active{color:var(--accent)}#_resources ._title ._btn._btn-disabled{color:inherit!important;cursor:default!important;pointer-events:none;opacity:.5}#_resources ._title ._btn._btn-disabled *{pointer-events:none}#_resources ._link-list{font-size:12px;color:var(--foreground)}#_resources ._link-list li{padding:10px;word-break:break-all}#_resources ._link-list li a{color:var(--link-color)!important}#_resources ._image-list{color:var(--foreground);font-size:12px;display:flex;flex-wrap:wrap;padding-left:10px;padding-top:10px}#_resources ._image-list::after{content:"";flex-grow:1000}#_resources ._image-list li{flex-grow:1;cursor:pointer;overflow-y:hidden;margin-right:10px;margin-bottom:10px;border:1px solid var(--border)}#_resources ._image-list li._image{height:100px;font-size:0}#_resources ._image-list li img{height:100px;min-width:100%;-o-object-fit:cover;object-fit:cover}._safe-area #_resources{padding-bottom:calc(10px + env(safe-area-inset-bottom))}',""]),e.exports=t},1505:function(e,t,n){var o=n(7308),r=n(6026),i=n(9405),a=n(9100),s=n(3915),c=n(1932);t=function(){var e=o("viewport");if(!e)return 1;e=s(e.split(","),(function(e){return i(e)}));var t=.25,n=5,l=1;a(e,(function(e){var o=(e=e.split("="))[0];e=e[1],"initial-scale"===o&&(l=+e),"maximum-scale"===o&&(n=+e),"minimum-scale"===o&&(t=+e)}));var u=r(l,t,n);return c(u)?1:u},e.exports=t},1532:function(e,t,n){var o=n(1009),r=n(5169),i=n(2561);function a(e,t){if(e=i(e),t=i(t),o(e,"_")&&!o(t,"_"))return 1;if(o(t,"_")&&!o(e,"_"))return-1;for(var n,a,s,c,l=/^\d+|^\D+/;;){if(!e)return t?-1:0;if(!t)return 1;if(n=e.match(l)[0],a=t.match(l)[0],s=!r.isNaN(n),c=!r.isNaN(a),s&&!c)return-1;if(c&&!s)return 1;if(s&&c){var u=n-a;if(u)return u;if(n.length!==a.length)return+n||+a?a.length-n.length:n.length-a.length}else if(n!==a)return n=0&&e.indexOf(t,n)===n},e.exports=t},1710:function(e,t){t=function(e,t){var n="";if(t<1)return"";for(;t>0;)1&t&&(n+=e),t>>=1,e+=e;return n},e.exports=t},1714:function(e,t,n){"use strict";n.d(t,{A:function(){return ne}});var o=n(3029),r=n(2901),i=n(388),a=n(3954),s=n(991),c=n(5361),l=n(2263),u=n.n(l),d=n(3693),h=n.n(d),f=n(3915),p=n.n(f),v=n(9405),g=n.n(v),m=n(5169),b=n.n(m),y=n(9548),A=n.n(y),w=(n(6097),n(3249)),_=n.n(w),x=(n(6030),n(5004)),k=n.n(x);n(8609);function C(e){var t="luna-".concat(e,"-");function n(e){return p()(g()(e).split(/\s+/),(function(e){return _()(e,t)?e:e.replace(/[\w-]+/,(function(e){return"".concat(t).concat(e)}))})).join(" ")}return function(e){if(/<[^>]*>/g.test(e))try{var t=A().parse(e);return S(t,(function(e){e.attrs&&e.attrs.class&&(e.attrs.class=n(e.attrs.class))})),A().stringify(t)}catch(t){return n(e)}return n(e)}}function S(e,t){for(var n=0,o=e.length;n2&&void 0!==arguments[2]?arguments[2]:{}).theme,f=void 0===d?"light":d;return(0,o.A)(this,t),s=this,c=t,c=(0,a.A)(c),(r=(0,i.A)(s,F()?Reflect.construct(c,l||[],(0,a.A)(s).constructor):c.apply(s,l))).subComponents=[],r.compName=u,r.c=C(u),r.options={},r.container=e,r.$container=h()(e),r.$container.addClass(["luna-".concat(u),r.c("platform-".concat(T()))]),r.on("optionChange",(function(e,t,n){var o=r.c;"theme"===e&&(r.$container.rmClass(o("theme-".concat(n))).addClass(o("theme-".concat(t))),O()(r.subComponents,(function(e){return e.setOption("theme",t)})))})),r.setOption("theme",f),r}return(0,c.A)(t,e),(0,r.A)(t,[{key:"destroy",value:function(){this.destroySubComponents();var e=this.c;this.$container.rmClass("luna-".concat(this.compName)).rmClass(e("platform-".concat(T()))).rmClass(e("theme-".concat(this.options.theme))),this.$container.html(""),this.emit("destroy"),this.removeAllListeners()}},{key:"setOption",value:function(e,t){var n=this,o=this.options,r={};"string"==typeof e?r[e]=t:r=e,O()(r,(function(e,t){var r=o[t];o[t]=e,n.emit("optionChange",t,e,r)}))}},{key:"getOption",value:function(e){return this.options[e]}},{key:"addSubComponent",value:function(e){e.setOption("theme",this.options.theme),this.subComponents.push(e)}},{key:"removeSubComponent",value:function(e){B()(this.subComponents,(function(t){return t===e}))}},{key:"destroySubComponents",value:function(){O()(this.subComponents,(function(e){return e.destroy()})),this.subComponents=[]}},{key:"initOptions",value:function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};z()(e,t),M()(this.options,e)}},{key:"find",value:function(e){return this.$container.find(this.c(e))}}])}(u()),L=n(3497),G=n.n(L),P=n(9464),H=n.n(P),Y=n(5865),$=n.n(Y),q=n(4534),Q=n.n(q),U=n(4844),W=n.n(U),J=n(5902),K=n.n(J),V=n(9389),Z=n.n(V),X=n(6948),ee=n.n(X);function te(){try{var e=!Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){})))}catch(e){}return(te=function(){return!!e})()}e=n.hmd(e);var ne=function(e){function t(e){var n,r,s,c,l=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};return(0,o.A)(this,t),r=this,s=t,c=[e,{compName:"text-viewer"},l],s=(0,a.A)(s),(n=(0,i.A)(r,te()?Reflect.construct(s,c||[],(0,a.A)(r).constructor):s.apply(r,c))).lineNum=0,n.copy=function(){var e=n.c,t=n.options,o=t.text,r=t.escape;W()(r?o:Z()(ee()(o)));var i=n.$copy.find(e(".icon"));i.addClass(e("icon-check")).rmClass(e("icon-copy")),setTimeout((function(){i.rmClass(e("icon-check")).addClass(e("icon-copy"))}),1e3)},n._updateCopyPos=function(){var e=n.container;n.$copy.css({top:e.scrollTop+5,right:5-e.scrollLeft})},n.initOptions(l,{text:"",escape:!0,showLineNumbers:!0,wrapLongLines:!0,maxHeight:1/0}),n.render=$()((function(){return n._render()}),16),n.updateCopyPos=Q()((function(){return n._updateCopyPos()}),300),n.initTpl(),n.$text=n.find(".text"),n.$copy=n.find(".copy"),E&&n.$copy.css("opacity","1"),n.options.text&&n.render(),n.bindEvent(),n.updateHeight(),n}return(0,c.A)(t,e),(0,r.A)(t,[{key:"append",value:function(e){var t=this,n=this.options,o=this.$copy,r=this.c,i=this.$text,a=n.showLineNumbers;if(this.options.text+=e,!a)return this.$text.append(n.escape?K()(e):e);var s=function(e){if(0===e.length)return[];return e.split(oe)}(e);H()(s)&&(s=[" "]),g()(G()(s))||s.pop();var c="";O()(s,(function(e,o){t.lineNum+=1,c+='
').concat(t.lineNum,'
').concat(n.escape?K()(e):e||" ","
")})),i.find(r(".table")).append(c),o.hide(),i.offset().height>40&&o.show(),this.updateCopyPos()}},{key:"destroy",value:function(){var e,n,o,r,i;this.$container.off("scroll",this.updateCopyPos),(e=t,n="destroy",o=this,r=3,i=(0,s.A)((0,a.A)(1&r?e.prototype:e),n,o),2&r&&"function"==typeof i?function(e){return i.apply(o,e)}:i)([])}},{key:"updateHeight",value:function(){var e=this.options.maxHeight;e>0&&e!==1/0?this.$text.css("max-height",e):this.$text.css("max-height","none")}},{key:"initTpl",value:function(){this.$container.html(this.c('
'))}},{key:"bindEvent",value:function(){var e=this;this.on("optionChange",(function(t){if("maxHeight"===t)e.updateHeight();else e.render()})),this.$container.on("scroll",this.updateCopyPos),this.$copy.on("click",this.copy)}},{key:"_render",value:function(){var e=this.c,t=this.$text,n=this.options,o=n.text,r=n.wrapLongLines,i=n.showLineNumbers;r?t.addClass(e("wrap-long-lines")):t.rmClass(e("wrap-long-lines")),i?t.addClass(e("line-numbers")):t.rmClass(e("line-numbers")),t.html('
')),this.lineNum=0,this.options.text="",this.append(o)}}])}(R),oe=/\r\n|\r|\n/g;(function(e,t){try{e.exports=t,e.exports.default=t}catch(e){}})(e,ne)},1738:function(e,t,n){var o=n(3974);t=function(e){return"[object String]"===o(e)},e.exports=t},1751:function(e,t,n){var o=n(3974);t=function(e){return"[object Map]"===o(e)},e.exports=t},1849:function(e,t,n){var o=n(9100),r=n(8971),i=n(3957);t=function(e,t){r(t)&&(t=!0);var n=i(t),a={};return o(e,(function(e){a[e]=n?t(e):t})),a},e.exports=t},1909:function(e,t){t="object"==typeof window&&"object"==typeof document&&9===document.nodeType,e.exports=t},1931:function(e,t,n){var o=n(1947);t=function(e){var t;switch(e=e||"local"){case"local":t=window.localStorage;break;case"session":t=window.sessionStorage}try{var n="test-localStorage-"+Date.now();t.setItem(n,n);var r=t.getItem(n);if(t.removeItem(n),r!==n)throw new Error}catch(e){return o}return t},e.exports=t},1932:function(e,t,n){var o=n(6097);t=function(e){return o(e)&&e!==+e},e.exports=t},1944:function(e,t,n){"use strict";n.d(t,{A:function(){return Z}});var o=n(5458),r=n(4467),i=n(7528),a=n(3029),s=n(2901),c=n(388),l=n(3954),u=n(991),d=n(5361),h=n(2263),f=n.n(h),p=n(3693),v=n.n(p),g=n(3915),m=n.n(g),b=n(9405),y=n.n(b),A=n(5169),w=n.n(A),_=n(9548),x=n.n(_),k=(n(6097),n(3249)),C=n.n(k),S=(n(6030),n(5004)),E=n.n(S);n(9410),n(8609);function T(e){var t="luna-".concat(e,"-");function n(e){return m()(y()(e).split(/\s+/),(function(e){return C()(e,t)?e:e.replace(/[\w-]+/,(function(e){return"".concat(t).concat(e)}))})).join(" ")}return function(e){if(/<[^>]*>/g.test(e))try{var t=x().parse(e);return N(t,(function(e){e.attrs&&e.attrs.class&&(e.attrs.class=n(e.attrs.class))})),x().stringify(t)}catch(t){return n(e)}return n(e)}}function N(e,t){for(var n=0,o=e.length;n2&&void 0!==arguments[2]?arguments[2]:{}).theme,u=void 0===s?"light":s;return(0,a.A)(this,t),o=function(e,t,n){return t=(0,l.A)(t),(0,c.A)(e,H()?Reflect.construct(t,n||[],(0,l.A)(e).constructor):t.apply(e,n))}(this,t),o.subComponents=[],o.theme="",o.onThemeChange=function(e){"auto"===o.options.theme&&o.setTheme(e)},o.compName=i,o.c=T(i),o.options={},o.container=e,o.$container=v()(e),o.$container.addClass(["luna-".concat(i),o.c("platform-".concat((r=E()(),"os x"===r?"mac":r)))]),o.on("changeOption",(function(e,t){if("theme"===e&&t){var n=t;"auto"===t&&(n=L().get()),o.setTheme(n),j()(o.subComponents,(function(e){return e.setOption("theme",t)}))}})),L().on("change",o.onThemeChange),o.setOption("theme",u),o}return(0,d.A)(t,e),(0,s.A)(t,[{key:"destroy",value:function(){var e=this;this.destroySubComponents();var t=this.$container,n=t.attr("class");j()(n.split(/\s+/),(function(n){P()(n,"luna-".concat(e.compName))&&t.rmClass(n)})),t.html(""),this.emit("destroy"),this.removeAllListeners(),L().off("change",this.onThemeChange)}},{key:"setOption",value:function(e,t){var n=this,o=this.options,r={};"string"==typeof e?r[e]=t:r=e,j()(r,(function(e,t){var r=o[t];o[t]=e,e!==r&&n.emit("changeOption",t,e,r)}))}},{key:"getOption",value:function(e){return this.options[e]}},{key:"addSubComponent",value:function(e){e.setOption("theme",this.options.theme),this.subComponents.push(e)}},{key:"removeSubComponent",value:function(e){F()(this.subComponents,(function(t){return t===e}))}},{key:"destroySubComponents",value:function(){j()(this.subComponents,(function(e){return e.destroy()})),this.subComponents=[]}},{key:"initOptions",value:function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};D()(e,t),I()(this.options,e)}},{key:"find",value:function(e){return this.$container.find(this.c(e))}},{key:"setTheme",value:function(e){var t=this.c,n=this.$container;this.theme&&n.rmClass(t("theme-".concat(this.theme))),n.addClass(t("theme-".concat(e))),this.theme=e}}])}(f()),q=n(5773),Q=n.n(q),U=n(5241),W=n.n(U),J=n(6741),K=n.n(J);function V(){try{var e=!Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){})))}catch(e){}return(V=function(){return!!e})()}e=n.hmd(e);var Z=function(e){function t(e){var n,o,r,i,s=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};return(0,a.A)(this,t),o=this,r=t,i=[e,{compName:"modal"},s],r=(0,l.A)(r),(n=(0,c.A)(o,V()?Reflect.construct(r,i||[],(0,l.A)(o).constructor):r.apply(o,i))).render=function(){var e=n,t=e.options,o=e.c,r=e.$body;t.title?(r.rmClass(o("no-title")),n.$title.text(t.title)):r.addClass(o("no-title")),t.footer?(r.rmClass(o("no-footer")),n.$footer.html("").append(t.footer)):r.addClass(o("no-footer")),t.showClose?n.$close.show():n.$close.hide(),n.$body.css("width",t.width+"px"),n.renderContent()},n.hide(),n.initOptions(s,{title:"",content:"",footer:"",showClose:!0,width:oe()}),n.initTpl(),n.$title=n.find(".title"),n.$content=n.find(".content"),n.$body=n.find(".body"),n.$footer=n.find(".footer"),n.$close=n.find(".icon-close"),n.bindEvent(),n}return(0,d.A)(t,e),(0,s.A)(t,[{key:"show",value:function(){this.render(),this.$container.rmClass(this.c("hidden"))}},{key:"hide",value:function(){this.$container.addClass(this.c("hidden"))}},{key:"destroy",value:function(){var e,n,o,r,i;(e=t,n="destroy",o=this,r=3,i=(0,u.A)((0,l.A)(1&r?e.prototype:e),n,o),2&r&&"function"==typeof i?function(e){return i.apply(o,e)}:i)([]),this.$container.rmClass(this.c("hidden"))}},{key:"renderContent",value:function(){this.$content.html("").append(this.options.content)}},{key:"bindEvent",value:function(){var e=this;this.$body.on("click",this.c(".icon-close"),(function(){return e.hide()})),this.on("changeOption",this.render)}},{key:"initTpl",value:function(){this.$container.html(this.c(Q()(Y||(Y=(0,i.A)(['\n
\n \n
\n
\n \n
\n '])))))}}],[{key:"alert",value:function(e){return new Promise((function(n){var o=te(),i=o.c;o.setOption({title:"",content:e,width:oe(),footer:ne((0,r.A)({},t.i18n.t("ok"),{type:"primary",onclick:function(){o.hide(),n()}}),i)}),o.show()}))}},{key:"confirm",value:function(e){return new Promise((function(n){var o=te(),i=o.c;o.setOption({title:"",content:e,width:oe(),footer:ne((0,r.A)((0,r.A)({},t.i18n.t("cancel"),{type:"secondary",onclick:function(){o.hide(),n(!1)}}),t.i18n.t("ok"),{type:"primary",onclick:function(){o.hide(),n(!0)}}),i)}),o.show()}))}},{key:"prompt",value:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"",n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"";return new Promise((function(o){var i=te(),a=i.c,s=W()("input"+a(".input"),{value:n});function c(){i.hide(),o(s.value)}v()(s).on("keypress",(function(e){"Enter"===(e=e.origEvent).key&&c()})),i.setOption({title:e,content:s,width:oe(),footer:ne((0,r.A)((0,r.A)({},t.i18n.t("cancel"),{type:"secondary",onclick:function(){i.hide(),o(null)}}),t.i18n.t("ok"),{type:"primary",onclick:c}),a)}),i.show();var l=s.value.length;s.setSelectionRange(l,l),s.focus()}))}},{key:"setContainer",value:function(e){ee=e}}])}($);Z.i18n=new(K())("zh-CN"!==navigator.language?"en-US":"zh-CN",{"en-US":{ok:"OK",cancel:"Cancel"},"zh-CN":{ok:"确定",cancel:"取消"}});var X=null,ee=null;function te(){return ee||(ee=W()("div"),document.body.append(ee)),X||(X=new Z(ee,{showClose:!1})),X}function ne(e,t){var n=m()(e,(function(e,n){return W()(t(".button")+t("."+e.type),{onclick:e.onclick},n)}));return W().apply(void 0,[t(".button-group"),{}].concat((0,o.A)(n)))}function oe(){return window.innerWidth<500?window.innerWidth-32:500}(function(e,t){try{e.exports=t,e.exports.default=t}catch(e){}})(e,Z)},1947:function(e,t,n){var o=n(3145);t={getItem:function(e){return(i[e]?r[e]:this[e])||null},setItem:function(e,t){i[e]?r[e]=t:this[e]=t},removeItem:function(e){i[e]?delete r[e]:delete this[e]},key:function(e){var t=a();return e>=0&&e-1&&n[e].splice(o,1),this}},once:function(e,t){return this.on(e,s(t)),this},emit:function(e){var t=this;if(r(this._events,e)){var n=a(arguments,1),o=c(this._events[e]);return i(o,(function(e){return e.apply(t,n)}),this),this}},removeAllListeners:function(e){return e?delete this._events[e]:this._events={},this}},{mixin:function(e){i(["on","off","once","emit","removeAllListeners"],(function(n){e[n]=t.prototype[n]})),e._events=e._events||{}}}),e.exports=t},2284:function(e,t,n){"use strict";function o(e){return o="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},o(e)}n.d(t,{A:function(){return o}})},2388:function(e,t,n){"use strict";n.d(t,{A:function(){return Le}});var o=n(7528),r=n(3029),i=n(2901),a=n(388),s=n(3954),c=n(991),l=n(5361),u=n(3693),d=n.n(u),h=n(5773),f=n.n(h),p=n(2263),v=n.n(p),g=n(3915),m=n.n(g),b=n(9405),y=n.n(b),A=n(5169),w=n.n(A),_=n(9548),x=n.n(_),k=(n(6097),n(3249)),C=n.n(k),S=n(6030),E=n.n(S),T=n(5004),N=n.n(T),O=(n(9410),n(8609)),j=n.n(O);function M(e){var t="luna-".concat(e,"-");function n(e){return m()(y()(e).split(/\s+/),(function(e){return C()(e,t)?e:e.replace(/[\w-]+/,(function(e){return"".concat(t).concat(e)}))})).join(" ")}return function(e){if(/<[^>]*>/g.test(e))try{var t=x().parse(e);return I(t,(function(e){e.attrs&&e.attrs.class&&(e.attrs.class=n(e.attrs.class))})),x().stringify(t)}catch(t){return n(e)}return n(e)}}function I(e,t){for(var n=0,o=e.length;n2&&void 0!==arguments[2]?arguments[2]:{}).theme,u=void 0===l?"light":l;return(0,r.A)(this,t),o=function(e,t,n){return t=(0,s.A)(t),(0,a.A)(e,W()?Reflect.construct(t,n||[],(0,s.A)(e).constructor):t.apply(e,n))}(this,t),o.subComponents=[],o.theme="",o.onThemeChange=function(e){"auto"===o.options.theme&&o.setTheme(e)},o.compName=c,o.c=M(c),o.options={},o.container=e,o.$container=d()(e),o.$container.addClass(["luna-".concat(c),o.c("platform-".concat((i=N()(),"os x"===i?"mac":i)))]),o.on("changeOption",(function(e,t){if("theme"===e&&t){var n=t;"auto"===t&&(n=q().get()),o.setTheme(n),F()(o.subComponents,(function(e){return e.setOption("theme",t)}))}})),q().on("change",o.onThemeChange),o.setOption("theme",u),o}return(0,l.A)(t,e),(0,i.A)(t,[{key:"destroy",value:function(){var e=this;this.destroySubComponents();var t=this.$container,n=t.attr("class");F()(n.split(/\s+/),(function(n){U()(n,"luna-".concat(e.compName))&&t.rmClass(n)})),t.html(""),this.emit("destroy"),this.removeAllListeners(),q().off("change",this.onThemeChange)}},{key:"setOption",value:function(e,t){var n=this,o=this.options,r={};"string"==typeof e?r[e]=t:r=e,F()(r,(function(e,t){var r=o[t];o[t]=e,e!==r&&n.emit("changeOption",t,e,r)}))}},{key:"getOption",value:function(e){return this.options[e]}},{key:"addSubComponent",value:function(e){e.setOption("theme",this.options.theme),this.subComponents.push(e)}},{key:"removeSubComponent",value:function(e){Y()(this.subComponents,(function(t){return t===e}))}},{key:"destroySubComponents",value:function(){F()(this.subComponents,(function(e){return e.destroy()})),this.subComponents=[]}},{key:"initOptions",value:function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};P()(e,t),L()(this.options,e)}},{key:"find",value:function(e){return this.$container.find(this.c(e))}},{key:"setTheme",value:function(e){var t=this.c,n=this.$container;this.theme&&n.rmClass(t("theme-".concat(this.theme))),n.addClass(t("theme-".concat(e))),this.theme=e}}])}(v()),V=n(5902),Z=n.n(V),X=n(5241),ee=n.n(X),te=n(2561),ne=n.n(te),oe=n(2708),re=n.n(oe),ie=n(8971),ae=n.n(ie),se=n(4095),ce=n.n(se),le=n(5865),ue=n.n(le),de=n(1532),he=n.n(de),fe=n(4236),pe=n.n(fe),ve=n(3957),ge=n.n(ve),me=n(1976),be=n.n(me),ye=n(6214),Ae=n.n(ye),we=n(1738),_e=n.n(we),xe=n(96),ke=n.n(xe),Ce=n(6026),Se=n.n(Ce),Ee=n(3539),Te=n.n(Ee),Ne=n(8785),Oe=n.n(Ne),je=n(6024),Me=n.n(je),Ie=n(3981),ze=n.n(Ie),De=n(5546),Be=n.n(De);function Fe(){try{var e=!Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){})))}catch(e){}return(Fe=function(){return!!e})()}e=n.hmd(e);var Re=d()(document),Le=function(e){function t(e,n){var o,i,c,l;(0,r.A)(this,t),i=this,c=t,l=[e,{compName:"data-grid"},n],c=(0,s.A)(c),(o=(0,a.A)(i,Fe()?Reflect.construct(c,l||[],(0,s.A)(i).constructor):c.apply(i,l))).resizeIdx=0,o.resizeStartX=0,o.resizeStartLeft=0,o.resizeDeltaX=0,o.nodes=[],o.displayNodes=[],o.colWidthsInitialized=!1,o.colMap={},o.selectedNode=null,o.isAscending=!0,o.sorted=!1,o.colWidths=[],o.spaceHeight=0,o.topSpaceHeight=0,o.lastScrollTop=0,o.lastTimestamp=0,o.speedToleranceFactor=100,o.maxSpeedTolerance=2e3,o.minSpeedTolerance=100,o.scrollTimer=null,o.onResizeColMove=function(e){var t=o,n=t.resizeIdx,r=t.$resizers,i=t.colWidths,a=t.$colgroup,s=z("x",e=e.origEvent)-o.resizeStartX,c=i[n],l=i[n+1],u=Oe()(24-c,0),h=Te()(l-24,0);s=Se()(s,u,h),a.each((function(){var e=d()(this).find("col");e.eq(n).css("width",c+s+"px"),e.eq(n+1).css("width",l-s+"px")})),o.resizeDeltaX=s;var f=o.resizeStartLeft+s;r.eq(n).css("left","".concat(f,"px"))},o.onResizeColEnd=function(e){o.onResizeColMove(e);var t=o,n=t.c,r=t.colWidths,i=t.resizeIdx,a=t.resizeDeltaX,s=o.options.columns,c=s[i],l=s[i+1],u=r[i]+a,h=u+(r[i+1]-a),f=c.weight+l.weight,p=f*(u/h),v=f-p;c.weight=p,l.weight=v,o.applyColWeights(),d()(document.body).rmClass(n("resizing")),Re.off(Be()("move"),o.onResizeColMove),Re.off(Be()("up"),o.onResizeColEnd)},o.onScroll=function(){var e=o.dataContainer,t=e.scrollHeight,n=e.clientHeight,r=e.scrollTop;if(!(r<=0||n+r>t)){var i=o.lastScrollTop,a=o.lastTimestamp,s=ze()(),c=s-a,l=r-i,u=Math.abs(l/c)*o.speedToleranceFactor;c>1e3&&(u=1e3),u>o.maxSpeedTolerance&&(u=o.maxSpeedTolerance),ur+n+h||(o.renderData({topTolerance:2*d,bottomTolerance:2*h}),o.scrollTimer&&clearTimeout(o.scrollTimer),o.scrollTimer=setTimeout((function(){o.renderData()}),100))}},o.renderData=ue()((function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},t=e.topTolerance,n=void 0===t?500:t,r=e.bottomTolerance,i=void 0===r?500:r;o.sortId&&!o.sorted&&o.sortNodes(o.sortId,o.isAscending);for(var a=o,s=a.dataContainer,c=a.displayNodes,l=a.tableBody,u=s.scrollTop,d=u-n,h=u+s.clientHeight+i,f=0,p=0,v=c.length,g=[],m=0;md?(0===g.length&&Me()(m)&&(g.push(c[m-1]),f-=20),g.push(b)):p1&&void 0!==arguments[1]?arguments[1]:{};P()(t,{selectable:this.options.selectable});var n=new Ge(this,e,t);this.nodes.push(n);var o=this.filterNode(n);return o&&this.displayNodes.push(n),(this.sortId||o)&&(this.sortId&&(this.sorted=!1),this.renderData()),this.updateHeight(),n}},{key:"setData",value:function(e,t){var n=this,o=m()(e,(function(e){return Ae()(e)?(P()(e[1],{selectable:n.options.selectable}),e):[e,{selectable:n.options.selectable}]}));if(t){var r={};F()(this.nodes,(function(e){r[e.data[t]]=e}));var i=[],a=[];F()(o,(function(e){var o,s=e[0][t];r[s]?((o=r[s]).data=e[0],o.render()):o=new Ge(n,e[0],e[1]),i.push(o),n.filterNode(o)&&a.push(o)})),this.selectedNode&&!C()(i,this.selectedNode)&&this.selectNode(null),this.nodes=i,this.displayNodes=a}else this.clearData(),F()(o,(function(e){var t=new Ge(n,e[0],e[1]);n.nodes.push(t),n.filterNode(t)&&n.displayNodes.push(t)}));this.sortId&&(this.sorted=!1),this.renderData(),this.updateHeight()}},{key:"clear",value:function(){this.clearData(),this.renderData(),this.updateHeight()}},{key:"fit",value:function(){if(!j()(this.container)){var e=this.$container.parent().get(0),t=window.getComputedStyle(e),n=e.clientHeight-D(t.paddingTop)-D(t.paddingBottom);this.setOption({minHeight:n,maxHeight:n})}}},{key:"clearData",value:function(){this.nodes=[],this.displayNodes=[],this.selectNode(null)}},{key:"updateHeight",value:function(){var e=this.$fillerRow,t=this.$container,n=this.options,o=n.maxHeight,r=n.minHeight,i=this.$headerRow.offset().height+D(t.css("border-top-width"))+D(t.css("border-bottom-width"));(r-=i)<0&&(r=0),o-=i;var a=this.displayNodes.length,s=0;a>0&&(s=20*a),s>r?e.hide():e.show(),s=o&&(s=o),this.$dataContainer.css({height:s})}},{key:"selectNode",value:function(e){(pe()(e)||null!=e&&e.selectable)&&this.selectedNode!==e&&(this.selectedNode&&(this.selectedNode.deselect(),this.selectedNode=null,pe()(e)&&this.emit("deselect")),pe()(e)||(this.selectedNode=e,e.select(),this.emit("select",e)))}},{key:"onResizeColStart",value:function(e){var t=this.c,n=this.resizeIdx,o=this.$resizers;e.stopPropagation(),e.preventDefault(),e=e.origEvent,this.resizeStartX=z("x",e),this.resizeStartLeft=D(o.eq(n).css("left")),d()(document.body).addClass(t("resizing")),Re.on(Be()("move"),this.onResizeColMove),Re.on(Be()("up"),this.onResizeColEnd)}},{key:"bindEvent",value:function(){var e=this,t=this.c,n=this.$headerRow,o=this.$tableBody,r=this.$resizers,i=this.$dataContainer;this.resizeSensor.addListener(this.onResize),i.on("scroll",this.onScroll);var a=this;o.on("click",t(".node"),(function(e){var t=this;a.selectNode(this.dataGridNode),setTimeout((function(){t.hasDoubleClick||a.emit("click",e.origEvent,t.dataGridNode)}),200)})).on("dblclick",t(".node"),(function(e){var t=this;e.stopPropagation(),this.hasDoubleClick=!0,a.emit("dblclick",e.origEvent,this.dataGridNode),setTimeout((function(){t.hasDoubleClick=!1}),300)})).on("contextmenu",t(".node"),(function(e){e.preventDefault(),e.stopPropagation(),a.selectNode(this.dataGridNode),a.emit("contextmenu",e.origEvent,this.dataGridNode)})),n.on("click",t(".sortable"),(function(e){e.stopPropagation();var o=d()(this),r=o.data("id"),i="descending"!==o.data("order");o.data("order",i?"descending":"ascending"),n.find(t(".icon-caret-up")).hide(),n.find(t(".icon-caret-down")).hide();var s=o.find(t(".icon-caret-up")),c=o.find(t(".icon-caret-down"));i?s.show():c.show(),a.sortNodes(r,i),a.renderData(),n.find("th").each((function(){var e=d()(this);e.data("id")!==r&&e.rmAttr("data-order")}))})),r.on(Be()("down"),(function(e){var t=d()(this);a.resizeIdx=E()(t.data("idx")),a.onResizeColStart(e)})),this.on("changeOption",(function(t){switch(t){case"minHeight":case"maxHeight":e.updateHeight();break;case"filter":e.displayNodes=[],F()(e.nodes,(function(t){e.filterNode(t)&&e.displayNodes.push(t)})),e.selectedNode&&!e.filterNode(e.selectedNode)&&e.selectNode(null),e.renderData(),e.updateHeight()}}))}},{key:"sortNodes",value:function(e,t){var n=this.colMap[e].comparator||he().comparator;function o(o,r){var i=o.data[e],a=r.data[e];return re()(i)&&(i=i.innerText),re()(a)&&(a=a.innerText),t?n(i,a):n(a,i)}this.nodes.sort(o),this.displayNodes.sort(o),this.sorted=!0,this.sortId=e,this.isAscending=t}},{key:"updateWeights",value:function(){var e=this.container,t=this.$headerRow,n=this.options.columns,o=e.offsetWidth;if(!this.colWidthsInitialized&&o){for(var r=0,i=n.length;r'),this.colWidths[u]=h}t.html(s),this.positionResizers()}}},{key:"positionResizers",value:function(){for(var e=this.colWidths,t=[],n=e.length-1,o=0;o\n ').concat(r,'\n \n \n ')):t+="".concat(r,""),n+=""})),this.$headerRow.html(t),this.$fillerRow.html(n)}},{key:"renderResizers",value:function(){for(var e="",t=this.options.columns.length-1,n=0;n'));this.$container.append(e),this.$resizers=this.find(".resizer")}},{key:"initTpl",value:function(){this.$container.html(this.c(f()(J||(J=(0,o.A)(['\n
\n \n \n \n \n \n
\n
\n
\n
\n \n \n \n \n \n
\n
\n
\n '])))))}}])}(K),Ge=function(){return(0,i.A)((function e(t,n,o){(0,r.A)(this,e),this.container=ee()("tr"),this.selectable=!1,this.container.dataGridNode=this,this.$container=d()(this.container),this.$container.addClass(t.c("node")),this.dataGrid=t,this.data=n,o.selectable&&(this.selectable=o.selectable,this.$container.addClass(t.c("selectable"))),this.render()}),[{key:"text",value:function(){return this.$container.text()}},{key:"select",value:function(){this.$container.addClass(this.dataGrid.c("selected"))}},{key:"deselect",value:function(){this.$container.rmClass(this.dataGrid.c("selected"))}},{key:"render",value:function(){var e=this.data,t=this.$container,n=this.container,o=this.dataGrid.getOption("columns");t.html(""),F()(o,(function(t){var o=ee()("td"),r=e[t.id];ae()(r)||(re()(r)?o.appendChild(r):o.innerText=ne()(r)),n.appendChild(o)}))}}])}();(function(e,t){try{e.exports=t,e.exports.default=t}catch(e){}})(e,Le)},2455:function(e,t){t=function(e){return e},e.exports=t},2480:function(e,t,n){"use strict";var o=this&&this.__createBinding||(Object.create?function(e,t,n,o){void 0===o&&(o=n);var r=Object.getOwnPropertyDescriptor(t,n);r&&!("get"in r?!t.__esModule:r.writable||r.configurable)||(r={enumerable:!0,get:function(){return t[n]}}),Object.defineProperty(e,o,r)}:function(e,t,n,o){void 0===o&&(o=n),e[o]=t[n]}),r=this&&this.__setModuleDefault||(Object.create?function(e,t){Object.defineProperty(e,"default",{enumerable:!0,value:t})}:function(e,t){e.default=t}),i=this&&this.__importStar||function(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var n in e)"default"!==n&&Object.prototype.hasOwnProperty.call(e,n)&&o(t,e,n);return r(t,e),t},a=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0}),t.enable=function(){if(x)return;var e=(0,h.default)("div",{class:"__chobitsu-hide__",style:{all:"initial"}});c=(0,d.default)(e),document.documentElement.appendChild(e);var t=null,n=null;e.attachShadow?n=e.attachShadow({mode:"open"}):e.createShadowRoot&&(n=e.createShadowRoot());if(n){var o=document.createElement("style");o.textContent=S,o.type="text/css",n.appendChild(o),t=document.createElement("div"),n.appendChild(t)}else t=document.createElement("div"),e.appendChild(t),_||((0,f.default)(S),_=!0);s=new A.default(t,{monitorResize:(0,b.default)(m.default.ResizeObserver),showInfo:k}),window.addEventListener("resize",R),x=!0},t.disable=function(){s.destroy(),c.remove(),window.removeEventListener("resize",R),x=!1},t.highlightNode=E,t.hideHighlight=T,t.setShowViewportSizeOnResize=function(e){N=e.show},t.setInspectMode=function(e){O=e.highlightConfig,j=e.mode};var s,c,l=n(9893),u=n(6192),d=a(n(3693)),h=a(n(5241)),f=a(n(3048)),p=a(n(5651)),v=a(n(8105)),g=a(n(8665)),m=a(n(5169)),b=a(n(9e3)),y=a(n(8534)),A=a(n(9196)),w=i(n(2484)),_=!1,x=!1,k=(0,y.default)("clip-path","polygon(50% 0px, 0px 100%, 100% 100%)"),C="ontouchstart"in m.default,S=n(7119).replace("/*# sourceMappingURL=luna-dom-highlighter.css.map*/","");function E(e){var t,n=e.nodeId,o=e.highlightConfig,r=e.objectId;n&&(t=(0,l.getNode)(n)),r&&(t=w.getObj(r)),1!==t.nodeType&&3!==t.nodeType||((0,p.default)(o,{contentColor:"transparent",paddingColor:"transparent",borderColor:"transparent",marginColor:"transparent"}),k||(0,v.default)(o,{showInfo:!1}),s.highlight(t,o))}function T(){s.hide()}var N=!1;var O={},j="none";function M(e){if(C){var t=e.touches[0]||e.changedTouches[0];return document.elementFromPoint(t.clientX,t.clientY)}return document.elementFromPoint(e.clientX,e.clientY)}var I=-1;function z(e){if("none"!==j){var t=M(e);if(t&&(0,l.isValidNode)(t)){var n=(0,l.getNodeId)(t);n||(n=(0,u.pushNodesToFrontend)(t)),E({nodeId:n,highlightConfig:O}),n!==I&&(g.default.trigger("Overlay.nodeHighlightRequested",{nodeId:n}),I=n)}}}function D(e){if("none"!==j){e.preventDefault(),e.stopImmediatePropagation();var t=M(e);g.default.trigger("Overlay.inspectNodeRequested",{backendNodeId:(0,l.getNodeId)(t)}),I=-1,T()}}function B(e,t){document.documentElement.addEventListener(e,t,!0)}C?(B("touchstart",z),B("touchmove",z),B("touchend",D)):(B("mousemove",z),B("mouseout",(function(){"none"!==j&&T()})),B("click",D));var F=(0,h.default)("div",{class:"__chobitsu-hide__",style:{position:"fixed",right:0,top:0,background:"#fff",fontSize:13,opacity:.5,padding:"4px 6px"}});function R(){N&&(G.text("".concat(window.innerWidth,"px × ").concat(window.innerHeight,"px")),L?clearTimeout(L):document.documentElement.appendChild(F),L=setTimeout((function(){G.remove(),L=null}),1e3))}var L,G=(0,d.default)(F)},2484:function(e,t,n){"use strict";var o=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0}),t.clear=function(){A.clear(),w.clear(),_.clear()},t.wrap=S,t.getObj=E,t.releaseObj=function(e){var t=E(e);w.delete(t),_.delete(e),A.delete(e)},t.getProperties=function(e){for(var t=e.accessorPropertiesOnly,n=e.objectId,o=e.ownProperties,i=e.generatePreview,a=[],c={prototype:!o,unenumerable:!0,symbol:!t},l=A.get(n),h=_.get(n),f=(0,v.default)(l,c),p=(0,m.default)(l),w=0,k=f.length;wT&&(a=T,o=!0);for(var s=0;sT){o=!0;break}l.push({key:N(p),value:N(e.get(p))}),s++,p=h.next().value}n.entries=l}else if((0,d.default)(e)){var v=[],g=(s=0,e.keys());for(p=g.next().value;p;){if(s>T){o=!0;break}v.push({value:N(p)}),s++,p=g.next().value}n.entries=v}return n.overflow=o,n}function O(e,t){var n=M(t);n.name=e;var o,i=n.subtype;return o="object"===n.type?"null"===i?"null":"array"===i?"Array(".concat(t.length,")"):"map"===i?"Map(".concat(t.size,")"):"set"===i?"Set(".concat(t.size,")"):(0,y.getType)(t,!1):(0,r.default)(t),n.value=o,n}function j(e,t){void 0===t&&(t=e);var n=M(e),o=n.type,i=n.subtype;return"string"===o?e:"number"===o||"symbol"===o?(0,r.default)(e):"function"===o?(0,p.default)(e):"array"===i?"Array(".concat(e.length,")"):"map"===i?"Map(".concat(t.size,")"):"set"===i?"Set(".concat(t.size,")"):"regexp"===i?(0,r.default)(e):"error"===i?e.stack:"internal#entry"===i?e.name?'{"'.concat((0,r.default)(e.name),'" => "').concat((0,r.default)(e.value),'"}'):'"'.concat((0,r.default)(e.value),'"'):(0,y.getType)(e,!1)}function M(e){var t=typeof e,n="object";if(e instanceof I)n="internal#entry";else if((0,i.default)(e))n="null";else if((0,a.default)(e))n="array";else if((0,h.default)(e))n="regexp";else if((0,l.default)(e))n="error";else if((0,u.default)(e))n="map";else if((0,d.default)(e))n="set";else try{(0,c.default)(e)&&(n="node")}catch(e){}return{type:t,subtype:n}}var I=function(e,t){t&&(this.name=t),this.value=e};function z(e){return e instanceof I||!!(e[0]&&e[0]instanceof I)}},2508:function(e,t,n){var o=n(6186),r=n(2561),i=/{{(.*?)}}/g;t=function(e,t){return e.replace(i,(function(e,n){return r(o(t,n))}))},e.exports=t},2510:function(e,t){t=function(e,t){return t=null==t?e.length-1:+t,function(){var n,o=Math.max(arguments.length-t,0),r=new Array(o);for(n=0;n]*>/g.test(e))try{var t=C().parse(e);return j(t,(function(e){e.attrs&&e.attrs.class&&(e.attrs.class=n(e.attrs.class))})),C().stringify(t)}catch(t){return n(e)}return n(e)}}function j(e,t){for(var n=0,o=e.length;n2&&void 0!==arguments[2]?arguments[2]:{}).theme,d=void 0===u?"light":u;return(0,o.A)(this,t),r=function(e,t,n){return t=(0,a.A)(t),(0,i.A)(e,$()?Reflect.construct(t,n||[],(0,a.A)(e).constructor):t.apply(e,n))}(this,t),r.subComponents=[],r.theme="",r.onThemeChange=function(e){"auto"===r.options.theme&&r.setTheme(e)},r.compName=c,r.c=O(c),r.options={},r.container=e,r.$container=l()(e),r.$container.addClass(["luna-".concat(c),r.c("platform-".concat((s=N()(),"os x"===s?"mac":s)))]),r.on("changeOption",(function(e,t){if("theme"===e&&t){var n=t;"auto"===t&&(n=P().get()),r.setTheme(n),I()(r.subComponents,(function(e){return e.setOption("theme",t)}))}})),P().on("change",r.onThemeChange),r.setOption("theme",d),r}return(0,s.A)(t,e),(0,r.A)(t,[{key:"destroy",value:function(){var e=this;this.destroySubComponents();var t=this.$container,n=t.attr("class");I()(n.split(/\s+/),(function(n){Y()(n,"luna-".concat(e.compName))&&t.rmClass(n)})),t.html(""),this.emit("destroy"),this.removeAllListeners(),P().off("change",this.onThemeChange)}},{key:"setOption",value:function(e,t){var n=this,o=this.options,r={};"string"==typeof e?r[e]=t:r=e,I()(r,(function(e,t){var r=o[t];o[t]=e,e!==r&&n.emit("changeOption",t,e,r)}))}},{key:"getOption",value:function(e){return this.options[e]}},{key:"addSubComponent",value:function(e){e.setOption("theme",this.options.theme),this.subComponents.push(e)}},{key:"removeSubComponent",value:function(e){L()(this.subComponents,(function(t){return t===e}))}},{key:"destroySubComponents",value:function(){I()(this.subComponents,(function(e){return e.destroy()})),this.subComponents=[]}},{key:"initOptions",value:function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};F()(e,t),D()(this.options,e)}},{key:"find",value:function(e){return this.$container.find(this.c(e))}},{key:"setTheme",value:function(e){var t=this.c,n=this.$container;this.theme&&n.rmClass(t("theme-".concat(this.theme))),n.addClass(t("theme-".concat(e))),this.theme=e}}])}(m()),Q=n(8971),U=n.n(Q);function W(){try{var e=!Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){})))}catch(e){}return(W=function(){return!!e})()}e=n.hmd(e);var J=function(e){function t(e){var n,r,s,c,l=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};return(0,o.A)(this,t),r=this,s=t,c=[e,{compName:"notification"},l],s=(0,a.A)(s),(n=(0,i.A)(r,W()?Reflect.construct(s,c||[],(0,a.A)(r).constructor):s.apply(r,c))).notifications=[],n.initOptions(l,{position:{x:"right",y:"bottom"},inline:!1,duration:2e3}),n.options.inline||n.$container.addClass(n.c("full")),n.initTpl(),n}return(0,s.A)(t,e),(0,r.A)(t,[{key:"notify",value:function(e){var t=this,n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};U()(n.duration)&&(n.duration=this.options.duration);var o=new K(this,e,{icon:n.icon||"none"});this.notifications.push(o),this.add(o),setTimeout((function(){return t.remove(o.id)}),n.duration)}},{key:"dismissAll",value:function(){for(var e=this.notifications,t=e[0];t;)this.remove(t.id),t=e[0]}},{key:"add",value:function(e){this.container.appendChild(e.container)}},{key:"remove",value:function(e){var t=this.notifications,n=f()(t,(function(t){return t.id===e}));if(n){n.destroy();var o=t.indexOf(n);t.splice(o,1)}}},{key:"initTpl",value:function(){var e=this.$container,t=this.options.position,n=t.x,o=t.y,r="flex-end",i="flex-end";switch(n){case"center":i="center";break;case"left":i="flex-start"}"top"===o&&(r="flex-start"),e.attr("style","justify-content: ".concat(r,"; align-items: ").concat(i))}}])}(q),K=function(){return(0,r.A)((function e(t,n,r){(0,o.A)(this,e),this.container=v()("div"),this.$container=l()(this.container),this.notification=t,this.content=n,this.id=d()("luna-notification-"),this.$container.attr({id:this.id,class:t.c("item ".concat("bottom"===t.getOption("position").y?"lower":"upper"))}),this.initTpl(r.icon)}),[{key:"destroy",value:function(){this.$container.remove()}},{key:"initTpl",value:function(e){var t=e;"success"===e?t="check":"warning"===e&&(t="warn");var n="none"===e?"":'
');this.$container.html(this.notification.c("".concat(n,'
').concat(this.content,"
")))}}])}();(function(e,t){try{e.exports=t,e.exports.default=t}catch(e){}})(e,J)},2561:function(e,t){t=function(e){return null==e?"":e.toString()},e.exports=t},2571:function(e,t,n){var o=n(5693),r=n(9100);t=function(e,t,n){var i=[];return t=o(t,n),r(e,(function(e,n,o){t(e,n,o)&&i.push(e)})),i},e.exports=t},2627:function(e,t,n){"use strict";var o=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0}),t.setGlobal=function(e,t){u[e]=t},t.default=function(e){var t;(0,l.default)(u,(function(e,t){window[t]||(window[t]=e)}));try{t=eval.call(window,"(".concat(e,")"))}catch(n){t=eval.call(window,e)}return function(){(0,l.default)(u,(function(e,t){window[t]&&window[t]===e&&delete window[t]}))}(),t};var r=o(n(1738)),i=o(n(4844)),a=o(n(769)),s=o(n(3145)),c=o(n(8862)),l=o(n(9100)),u={copy:function(e){(0,r.default)(e)||(e=JSON.stringify(e,null,2)),(0,i.default)(e)},$:function(e){return document.querySelector(e)},$$:function(e){return(0,a.default)(document.querySelectorAll(e))},$x:function(e){return(0,c.default)(e)},keys:s.default}},2650:function(e,t,n){var o=n(3974);t=function(e){switch(o(e)){case"[object Error]":case"[object DOMException]":return!0;default:return e instanceof Error}},e.exports=t},2693:function(e,t,n){(t=n(6314)(!1)).push([e.id,'._container a,._container abbr,._container acronym,._container address,._container applet,._container article,._container aside,._container audio,._container b,._container big,._container blockquote,._container canvas,._container caption,._container center,._container cite,._container code,._container dd,._container del,._container details,._container dfn,._container dl,._container dt,._container em,._container embed,._container fieldset,._container figcaption,._container figure,._container footer,._container form,._container h1,._container h2,._container h3,._container h4,._container h5,._container h6,._container header,._container hgroup,._container i,._container iframe,._container img,._container ins,._container kbd,._container label,._container legend,._container li,._container mark,._container menu,._container nav,._container object,._container ol,._container output,._container p,._container pre,._container q,._container ruby,._container s,._container samp,._container section,._container small,._container span,._container strike,._container strong,._container sub,._container summary,._container sup,._container table,._container tbody,._container td,._container tfoot,._container th,._container thead,._container time,._container tr,._container tt,._container u,._container ul,._container var,._container video{margin:0;padding:0;border:0;font-size:100%}._container article,._container aside,._container details,._container figcaption,._container figure,._container footer,._container header,._container hgroup,._container menu,._container nav,._container section{display:block}._container body{line-height:1}._container ol,._container ul{list-style:none}._container blockquote,._container q{quotes:none}._container blockquote:after,._container blockquote:before,._container q:after,._container q:before{content:"";content:none}._container table{border-collapse:collapse;border-spacing:0;color:inherit;font-size:1em;font-style:inherit;font-variant:inherit;font-weight:inherit;line-height:inherit;text-decoration:inherit;white-space:inherit}',""]),e.exports=t},2708:function(e,t){t=function(e){return!(!e||1!==e.nodeType)},e.exports=t},2717:function(e,t,n){var o=n(8105),r=n(769),i=n(8009),a=n(6186),s=n(4460);var c=(t=function(e,t){return c.extend(e,t)}).Base=function e(t,n,c){c=c||{};var l=n.className||a(n,"initialize.name")||"";delete n.className;var u=function(){var e=r(arguments);return this.initialize&&this.initialize.apply(this,e)||this};if(!s)try{u=new Function("toArr","return function "+l+"(){var args = toArr(arguments);return this.initialize ? this.initialize.apply(this, args) || this : this;};")(r)}catch(e){}return i(u,t),u.prototype.constructor=u,u.extend=function(t,n){return e(u,t,n)},u.inherits=function(e){i(u,e)},u.methods=function(e){return o(u.prototype,e),u},u.statics=function(e){return o(u,e),u},u.methods(n).statics(c),u}(Object,{className:"Base",callSuper:function(e,t,n){return e.prototype[t].apply(this,n)},toString:function(){return this.constructor.name}});e.exports=t},2767:function(e,t,n){var o=n(1738),r=n(5282);t=function(e){return o(e)?i[e]:u[e]};for(var i={backspace:8,tab:9,enter:13,shift:16,ctrl:17,alt:18,"pause/break":19,"caps lock":20,esc:27,space:32,"page up":33,"page down":34,end:35,home:36,left:37,up:38,right:39,down:40,insert:45,delete:46,windows:91,"right windows":92,"windows menu":93,"numpad *":106,"numpad +":107,"numpad -":109,"numpad .":110,"numpad /":111,"num lock":144,"scroll lock":145,";":186,"=":187,",":188,"-":189,".":190,"/":191,"`":192,"[":219,"\\":220,"]":221,"'":222},a=97;a<123;a++)i[String.fromCharCode(a)]=a-32;for(var s=48;s<58;s++)i[s-48]=s;for(var c=1;c<13;c++)i["f"+c]=c+111;for(var l=0;l<10;l++)i["numpad "+l]=l+96;var u=r(i);e.exports=t},2797:function(e,t,n){var o=n(5693),r=n(5793),i=n(3145);t=function(e,t,n){t=o(t,n);for(var a=!r(e)&&i(e),s=(a||e).length,c=0;c1&&void 0!==arguments[1])||arguments[1];return null===e&&(t="Null"),void 0===e&&(t="Undefined"),r(e)&&(t="NaN"),a(e)&&(t="Buffer"),t||(t=o(e).match(s))&&(t=t[1]),t?n?i(t):t:""};var s=/^\[object\s+(.*?)]$/;e.exports=t},2990:function(e,t){t={encode:function(e){var t,n,r=[],i=e.length,a=i%3;i-=a;for(var s=0;s>18&63]+o[t>>12&63]+o[t>>6&63]+o[63&t]));return i=e.length,1===a?(n=e[i-1],r.push(o[n>>2]),r.push(o[n<<4&63]),r.push("==")):2===a&&(n=(e[i-2]<<8)+e[i-1],r.push(o[n>>10]),r.push(o[n>>4&63]),r.push(o[n<<2&63]),r.push("=")),r.join("")},decode:function(e){var t=e.length,o=0;"="===e[t-2]?o=2:"="===e[t-1]&&(o=1);var r,i,a,s,c,l,u,d=new Array(3*t/4-o);for(t=o>0?t-4:t,r=0,i=0;r>16&255,d[i++]=h>>8&255,d[i++]=255&h}return 2===o?(u=n[e.charCodeAt(r)]<<2|n[e.charCodeAt(r+1)]>>4,d[i++]=255&u):1===o&&(u=n[e.charCodeAt(r)]<<10|n[e.charCodeAt(r+1)]<<4|n[e.charCodeAt(r+2)]>>2,d[i++]=u>>8&255,d[i++]=255&u),d}};for(var n=[],o="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",r=0,i=o.length;r-1:(i(e)||(e=a(e)),o(e,t)>=0)},e.exports=t},3270:function(e,t,n){"use strict";n.r(t),n.d(t,{default:function(){return g}});var o={a:7,c:6,h:1,l:2,m:2,q:4,s:4,t:2,v:1,z:0},r=/([astvzqmhlc])([^astvzqmhlc]*)/gi,i=/-?[0-9]*\.?[0-9]+(?:e[-+]?\d+)?/gi;var a=function(e){var t=[],n=String(e).trim();return"M"!==n[0]&&"m"!==n[0]||n.replace(r,(function(e,n,r){var a=n.toLowerCase(),s=function(e){var t=e.match(i);return t?t.map(Number):[]}(r),c=n;if("m"===a&&s.length>2&&(t.push([c].concat(s.splice(0,2))),a="l",c="m"===c?"l":"L"),s.length=o[a]&&s.length&&o[a];)t.push([c].concat(s.splice(0,o[a])));return""})),t};function s(e,t){for(var n=0;ne.length)&&(t=e.length);for(var n=0,o=new Array(t);n1&&(y*=l=Math.sqrt(l),A*=l),u=y*y*A*A,f=y*y*s.y*s.y+A*A*s.x*s.x,h(k={x:y*s.y/A,y:-A*s.x/y},i!==r?Math.sqrt((u-f)/f)||0:-Math.sqrt((u-f)/f)||0),o=Math.atan2((s.y-k.y)/A,(s.x-k.x)/y),n=Math.atan2(-(s.y+k.y)/A,-(s.x+k.x)/y),d(k,c),O=k,j=(a.x+z.x)/2,M=(a.y+z.y)/2,O.x+=j,O.y+=M,e.save(),e.translate(k.x,k.y),e.rotate(c),e.scale(y,A),e.arc(0,0,1,o,n,!i),e.restore();break;case"C":C=B[3],S=B[4],p=B[5],g=B[6],e.bezierCurveTo(B[1],B[2],C,S,p,g);break;case"c":e.bezierCurveTo(B[1]+p,B[2]+g,B[3]+p,B[4]+g,B[5]+p,B[6]+g),C=B[3]+p,S=B[4]+g,p+=B[5],g+=B[6];break;case"S":null!==C&&null!==S||(C=p,S=g),e.bezierCurveTo(2*p-C,2*g-S,B[1],B[2],B[3],B[4]),C=B[1],S=B[2],p=B[3],g=B[4];break;case"s":null!==C&&null!==S||(C=p,S=g),e.bezierCurveTo(2*p-C,2*g-S,B[1]+p,B[2]+g,B[3]+p,B[4]+g),C=B[1]+p,S=B[2]+g,p+=B[3],g+=B[4];break;case"Q":E=B[1],T=B[2],p=B[3],g=B[4],e.quadraticCurveTo(E,T,p,g);break;case"q":E=B[1]+p,T=B[2]+g,p+=B[3],g+=B[4],e.quadraticCurveTo(E,T,p,g);break;case"T":null!==E&&null!==T||(E=p,T=g),E=2*p-E,T=2*g-T,p=B[1],g=B[2],e.quadraticCurveTo(E,T,p,g);break;case"t":null!==E&&null!==T||(E=p,T=g),E=2*p-E,T=2*g-T,p+=B[1],g+=B[2],e.quadraticCurveTo(E,T,p,g);break;case"z":case"Z":p=I.x,g=I.y,I=void 0,e.closePath();break;case"AC":p=B[1],g=B[2],b=B[3],o=B[4],n=B[5],N=B[6],e.arc(p,g,b,o,n,N);break;case"AT":v=B[1],m=B[2],p=B[3],g=B[4],b=B[5],e.arcTo(v,m,p,g,b);break;case"E":p=B[1],g=B[2],y=B[3],A=B[4],c=B[5],o=B[6],n=B[7],N=B[8],e.save(),e.translate(p,g),e.rotate(c),e.scale(y,A),e.arc(0,0,1,o,n,N),e.restore();break;case"R":p=B[1],g=B[2],w=B[3],_=B[4],I={x:p,y:g},e.rect(p,g,w,_)}z.x=p,z.y=g}}},p=a,v=f;"undefined"!=typeof window&&v(window);var g={path2dPolyfill:v,parsePath:p}},3277:function(e,t,n){(t=n(6314)(!1)).push([e.id,'@font-face{font-family:luna-data-grid-icon;src:url(\'data:application/x-font-woff;charset=utf-8;base64,d09GRgABAAAAAAScAAsAAAAAB4wAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAABHU1VCAAABCAAAAFwAAACGIRYl5k9TLzIAAAFkAAAAPQAAAFZLxUkYY21hcAAAAaQAAADLAAACXsU3J5xnbHlmAAACcAAAACUAAAAwNxN2HmhlYWQAAAKYAAAALgAAADZzjr4QaGhlYQAAAsgAAAAYAAAAJAFyANhobXR4AAAC4AAAABAAAAA8AZAAAGxvY2EAAALwAAAAEAAAACAAnACobWF4cAAAAwAAAAAfAAAAIAEaAA9uYW1lAAADIAAAASkAAAIWm5e+CnBvc3QAAARMAAAATQAAAG/8EX8xeJxjYGRgYOBiMGCwY2BycfMJYeDLSSzJY5BiYGGAAJA8MpsxJzM9kYEDxgPKsYBpDiA2A9NMDGwMMkAWF1CGFchmB4pxAnncDDxAOQ4kUV4GPqA6RgZmAE91Bj94nGNgZJBmnMDAysDAUMfQAyRloHQCAyeDMQMDEwMrMwNWEJDmmsJwgEH3IxPDCSBXCEwyMDCCCABbKQhrAAAAeJzNkksKwjAQhr+kvqpVxIUL8Qh6KFG3KtLidVx5Bq/VE+g/ySCIILhzhq+Qv8lkHgG6QCFWogPhRsDsKjUkvWCY9A611jOmUiJrNuw5cabh0sbHQ/9M23KQVksLSXu3oPPmcxbypRwGilxQ0qPSTRNl1GfMSHujDvQ+Yny3wY/7SXUX5eueaphSsAz+xar0vftqgnU6Y9VuHE1P3c/YRHdOKfaOVXlwLOrR0YQ1y4zFPzv2OmqnLxpnLC6O+tSGjL2YNmaITyYUHqgAeJxjYGQAgZUM0xmYGBjMlcVXNjtO9wCKoImbA8X9geIAqRwH2QAAAHicY2BkYGAA4hwJ4Yp4fpuvDNwMJ4ACUZyP9zUgaCBYyTAdSHIwMIE4ABioCdgAAHicY2BkYGA4wcAAJ1cyMDKgAn4AOLICS3icY2AAghNQTCIAAFMMAZF4nGNgAAIeBglcEAARPAFFeJxjYGRgYOBnYGYA0QwMTEDMBYQMDP/BfAYACkEBKgB4nGWQPW7CQBSEx2BIAlKCFCkps1UKIpmfkgNAT0GXwpi1MbK91npBossJcoQcIaeIcoIcKGPzaGAtP38zb97uygAG+IWHenm4bWq9WrihOnGb9CDsk5+FO+jjRbhLfyjcwxumwn084p07eP4dnQFK4Rbu8SHcpv8p7JO/hDt4wrdwl/6PcA8r/An38eoN08gUsSncUif7LLRnef6utK1SU6hJMD5bC11oGzq9Ueujqg7J1LlYxdbkas6uzjKjSmt2OnLB1rlyNhrF4geRyZEigkGBuKkOS2gk2CNDCHvVvdQrpi0q+rVWmCDA+Cq1YKpokiGVxobJNY6sFQ48bUrXMa34Ws7kpLnMat4kIyv+77q3oxPRD7BtpkrMMOITX+SD5g75Pz0RXqgAAAB4nF3GOQ6AIBQA0T+44YoX4VAEaNEYCNe3IDa+ZkaUNF//DIqOnoGRCc3MwsrGzoHhlMW7J2Ybrpp023LjcTxEMpbARSVRuEVebe8MtgAAAA==\') format(\'woff\')}[class*=\' luna-data-grid-icon-\'],[class^=luna-data-grid-icon-]{display:inline-block;font-family:luna-data-grid-icon!important;font-size:16px;font-style:normal;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.luna-data-grid-icon-caret-down:before{content:\'\\f101\'}.luna-data-grid-icon-caret-up:before{content:\'\\f102\'}.luna-data-grid{position:relative;border:1px solid;overflow:hidden;outline:0;color:rgba(0,0,0,.88);color:var(--luna-color-text,rgba(0,0,0,.88));background-color:#fff;background-color:var(--luna-color-bg-container,#fff);font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";font-family:var(--luna-font-family, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji");box-sizing:border-box;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;font-size:14px}.luna-data-grid .luna-data-grid-hidden,.luna-data-grid.luna-data-grid-hidden{display:none}.luna-data-grid .luna-data-grid-invisible,.luna-data-grid.luna-data-grid-invisible{visibility:hidden}.luna-data-grid *{box-sizing:border-box}.luna-data-grid.luna-data-grid-theme-dark{color-scheme:dark;color:hsla(0,0%,100%,.85);color:var(--luna-color-text-dark,rgba(255,255,255,.85));background-color:#141414;background-color:var(--luna-color-bg-container-dark,#141414)}.luna-data-grid{font-size:12px}.luna-data-grid table{width:100%;min-height:100%;border-collapse:separate;border-spacing:0;table-layout:fixed}.luna-data-grid td,.luna-data-grid th{padding:1px 4px;border-left:1px solid;white-space:nowrap;text-overflow:ellipsis;overflow:hidden}.luna-data-grid td:first-child,.luna-data-grid th:first-child{border-left:none}.luna-data-grid th{font-weight:400;border-bottom:1px solid;text-align:left;position:relative}.luna-data-grid th.luna-data-grid-sortable{padding-right:12px}.luna-data-grid th .luna-data-grid-icon-caret-down,.luna-data-grid th .luna-data-grid-icon-caret-up{font-size:12px;position:absolute;display:none;top:6px;right:2px}.luna-data-grid td{height:20px;cursor:default;-webkit-user-select:text;-moz-user-select:text;-ms-user-select:text;user-select:text}.luna-data-grid:focus .luna-data-grid-node.luna-data-grid-selectable.luna-data-grid-selected{color:#fff}.luna-data-grid-data-container,.luna-data-grid-header-container{overflow:hidden}.luna-data-grid-header-container{height:21px}.luna-data-grid-data-container{overflow-y:auto;position:relative}.luna-data-grid-data-space{min-height:100%}.luna-data-grid-data{position:absolute;left:0;top:0}.luna-data-grid-filler-row td{height:auto}.luna-data-grid-resizer{position:absolute;top:0;bottom:0;width:5px;z-index:500;touch-action:none;cursor:col-resize}.luna-data-grid-resizing{cursor:col-resize!important}.luna-data-grid-resizing .luna-data-grid *{cursor:col-resize!important}.luna-data-grid-theme-light{border-color:#d9d9d9;border-color:var(--luna-color-border,#d9d9d9)}.luna-data-grid-theme-light td,.luna-data-grid-theme-light th{border-color:#d9d9d9;border-color:var(--luna-color-border,#d9d9d9)}.luna-data-grid-theme-light th{background-color:rgba(0,0,0,.06);background-color:var(--luna-color-fill-secondary,rgba(0,0,0,.06))}.luna-data-grid-theme-light th.luna-data-grid-sortable:hover{background-color:rgba(0,0,0,.15);background-color:var(--luna-color-fill,rgba(0,0,0,.15))}.luna-data-grid-theme-light .luna-data-grid-data-container .luna-data-grid-node.luna-data-grid-selectable:hover,.luna-data-grid-theme-light .luna-data-grid-data-container .luna-data-grid-node.luna-data-grid-selected{background-color:#ddd}.luna-data-grid-theme-light .luna-data-grid-data-container tr:nth-child(even){background-color:#f2f7fd}.luna-data-grid-theme-light:focus .luna-data-grid-node.luna-data-grid-selectable.luna-data-grid-selected{background-color:#1a73e8;background-color:var(--luna-color-primary,#1a73e8)}.luna-data-grid-theme-dark{border-color:#424242;border-color:var(--luna-color-border,#424242)}.luna-data-grid-theme-dark td,.luna-data-grid-theme-dark th{border-color:#424242;border-color:var(--luna-color-border,#424242)}.luna-data-grid-theme-dark th{background-color:hsla(0,0%,100%,.12);background-color:var(--luna-color-fill-secondary,rgba(255,255,255,.12))}.luna-data-grid-theme-dark th.luna-data-grid-sortable:hover{background-color:hsla(0,0%,100%,.18);background-color:var(--luna-color-fill,rgba(255,255,255,.18))}.luna-data-grid-theme-dark .luna-data-grid-data-container .luna-data-grid-node.luna-data-grid-selectable:hover,.luna-data-grid-theme-dark .luna-data-grid-data-container .luna-data-grid-node.luna-data-grid-selected{background-color:#393939}.luna-data-grid-theme-dark .luna-data-grid-data-container tr:nth-child(even){background-color:#0b2544}.luna-data-grid-theme-dark:focus .luna-data-grid-node.luna-data-grid-selectable.luna-data-grid-selected{background-color:#1965c8;background-color:var(--luna-color-primary,#1965c8)}',""]),e.exports=t},3290:function(e,t,n){var o=n(975);t=function(e){var t,n=window.location,r=n.hostname,i=n.pathname,a=r.split("."),s=i.split("/"),c="",l=s.length;if(!p())for(var u=a.length-1;u>=0;u--){var d=a[u];if(""!==d){if(p({domain:c=""===c?d:d+"."+c,path:t="/"})||p({domain:c}))return;for(var h=0;h-1&&(t=(0,d.default)(t.split("?")[0])),""===t)&&(t=new l.default(e).hostname);return t}function S(e){if(!e)return{type:"unknown",subType:"unknown"};var t=e.split(";")[0].split("/");return{type:t[0],subType:(0,c.default)(t)}}var E=window.location.origin;function T(e){var t=encodeURIComponent(e).match(/%[89ABab]/g);return e.length+(t?t.length:0)}},3422:function(e,t){t=function(e){return null==e},e.exports=t},3497:function(e,t){t=function(e){var t=e?e.length:0;if(t)return e[t-1]},e.exports=t},3539:function(e,t){t=function(){for(var e=arguments,t=e[0],n=1,o=e.length;nt&&(t=e[n]);return t},e.exports=t},3578:function(e,t,n){"use strict";n.d(t,{A:function(){return on}});var o=n(7528);var r=n(7800);function i(e,t){return function(e){if(Array.isArray(e))return e}(e)||function(e,t){var n=null==e?null:"undefined"!=typeof Symbol&&e[Symbol.iterator]||e["@@iterator"];if(null!=n){var o,r,i,a,s=[],c=!0,l=!1;try{if(i=(n=n.call(e)).next,0===t){if(Object(n)!==n)return;c=!1}else for(;!(c=(o=i.call(n)).done)&&(s.push(o.value),s.length!==t);c=!0);}catch(e){l=!0,r=e}finally{try{if(!c&&null!=n.return&&(a=n.return(),Object(a)!==a))return}finally{if(l)throw r}}return s}}(e,t)||(0,r.A)(e,t)||function(){throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}()}var a=n(3029),s=n(2901),c=n(388),l=n(3954),u=n(991),d=n(5361),h=n(5902),f=n.n(h),p=n(2561),v=n.n(p),g=n(3249),m=n.n(g),b=n(1009),y=n.n(b),A=n(4950),w=n.n(A),_=n(9100),x=n.n(_),k=n(1580),C=n.n(k),S=n(9464),E=n.n(S),T=n(15),N=n.n(T),O=n(9931),j=n.n(O);function M(e){return e.constructor&&e.constructor.name?e.constructor.name:j()({}.toString.call(e).replace(/(\[object )|]/g,""))}var I=n(3915),z=n.n(I),D=n(9405),B=n.n(D),F=n(5169),R=n.n(F),L=n(9548),G=n.n(L),P=n(6097),H=n.n(P),Y=n(6030),$=n.n(Y),q=n(5004),Q=n.n(q),U=(n(9410),n(8609)),W=n.n(U);function J(e){var t="luna-".concat(e,"-");function n(e){return z()(B()(e).split(/\s+/),(function(e){return m()(e,t)?e:e.replace(/[\w-]+/,(function(e){return"".concat(t).concat(e)}))})).join(" ")}return function(e){if(/<[^>]*>/g.test(e))try{var t=G().parse(e);return K(t,(function(e){e.attrs&&e.attrs.class&&(e.attrs.class=n(e.attrs.class))})),G().stringify(t)}catch(t){return n(e)}return n(e)}}function K(e,t){for(var n=0,o=e.length;n1&&void 0!==arguments[1]?arguments[1]:{},o=n.topObj,r=n.level,i=void 0===r?0:r,a=n.getterVal,s=void 0!==a&&a,c=n.unenumerable,l=void 0===c||c,u="",d="",h=[],p=[],g="";o=o||e;var b={getterVal:s,unenumerable:l,level:i+1},A=0===i,w=''),_=''),k=''),S=''),E=''),T=''),O=function(e){return f()(e).replace(/\\n/g,"↵").replace(/\\f|\\r|\\t/g,"").replace(/\\/g,"")},j="";function I(e){return e=v()(e),m()(ee,e)||y()(e,"Array[")?T+O(e)+j:(e.length>100&&(e=N()(e,100,{separator:" ",ellipsis:"…"})),S+O('"'.concat(e,'"'))+j)}function z(n){if(t>5)g=", …";else{var r=function(e){return w+O(e)+j}(ne(n));if(!s){var i=Object.getOwnPropertyDescriptor(e,n);if(i&&i.get)return h.push("".concat(r,": ").concat(I("(...)"))),void t++}h.push("".concat(r,": ").concat(X(o[n],b))),t++}}try{d={}.toString.call(e)}catch(e){d="[object Object]"}var D,B="[object Array]"==d,F="[object Object]"==d,R="[object Number]"==d,L="[object RegExp]"==d,G="[object Symbol]"==d,P="[object Function]"==d,H="[object Boolean]"==d;if("[object String]"==d)u=I(ne(e));else if(L)D=ne(e.toString()),u=S+D+j;else if(P)u=I("ƒ");else if(B)if(A){u="[";var Y=e.length,$="";Y>100&&(Y=100,$=", …");for(var q=0;q0&&void 0!==arguments[0])||arguments[0],t=this.container.getBoundingClientRect(),n=t.width,o=t.height-1;this.height!==o&&(this.height=o,e||this.emit("updateHeight")),this.width!==n&&(this.width=n)}},{key:"html",value:function(){return this.container.outerHTML}},{key:"text",value:function(){return this.content.textContent||""}},{key:"select",value:function(){this.$container.addClass(this.console.c("selected"))}},{key:"deselect",value:function(){this.$container.rmClass(this.console.c("selected"))}},{key:"copy",value:function(){var e=this.args,t="";x()(e,(function(e,n){0!==n&&(t+=" "),ue()(e)?t+=Ze()(e):t+=v()(e)})),et()(t)}},{key:"bindEvent",value:function(){var e=this,t=this.console.c,n=this;this.resizeSensor.addListener(this.onResize),this.$container.on("click",t(".dom-viewer"),(function(e){return e.stopPropagation()})).on("click",t(".preview"),(function(e){if(e.stopPropagation(),!V(this)){var o=$e()(this).find(t(".preview-icon-container")).find(t(".icon")),r="caret-down";o.hasClass(t("icon-caret-down"))&&(r="caret-right"),o.rmAttr("class").addClass([t("icon"),t("icon-".concat(r))]),n.renderObjectViewer(this)}})).on("click",(function(){return e.click()}))}},{key:"renderEl",value:function(){var e=this.elements,t=this.console.c,n=this;this.$container.find(t(".dom-viewer")).each((function(){var t=$e()(this).data("id");new ae.A(this,{node:e[t],theme:n.console.getOption("theme")})}))}},{key:"renderObjectViewer",value:function(e){var t=this.console,n=this.unenumerable,o=this.accessGetter,r=this.lazyEvaluation,i=t.c,a=$e()(e),s=a.data("id");if(s){var c=this.objects[s],l=a.find(i(".json"));if(l.hasClass(i("hidden"))){if("true"!==l.data("init")){if(r){var u=new re.A(l.get(0),{unenumerable:n,accessGetter:o});u.setOption("theme",t.getOption("theme")),u.set(c)}else{var d=new re.j(l.get(0));d.setOption("theme",t.getOption("theme")),d.set(c)}l.data("init","true")}l.rmClass(i("hidden"))}else l.addClass(i("hidden"))}}},{key:"renderTable",value:function(e){var t=this,n="__LunaConsoleValue",o=this.columns,r=this.$container,i=this.console,a=i.c,s=r.find(a(".data-grid")),c=e[0],l=new ie.A(s.get(0),{columns:ke()([{id:"(index)",title:"(index)",sortable:!0}],z()(o,(function(e){return{id:e,title:e===n?"Value":e,sortable:!0}}))),theme:i.getOption("theme")});x()(c,(function(e,r){var i={"(index)":v()(r)};o.forEach((function(o){ue()(e)?i[o]=o===n?"":t.formatTableVal(e[o]):ge()(e)&&(i[o]=o===n?t.formatTableVal(e):"")})),l.append(i)}))}},{key:"extractObj",value:function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},n=arguments.length>2?arguments[2]:void 0,o=this.accessGetter,r=this.unenumerable;be()(t,{accessGetter:o,unenumerable:r,symbol:r,timeout:1e3}),function(e,t,n){var o=nt()(e,t);rt()((function(){return n(o)}))}(e,t,(function(e){return n(JSON.parse(e))}))}},{key:"click",value:function(){var e=this.type,t=this.$container,n=this.console,o=n.c;switch(e){case"log":case"warn":case"info":case"debug":case"output":case"table":case"dir":break;case"group":case"groupCollapsed":V(this.container)||n.toggleGroup(this);break;case"error":V(this.container)||t.find(o(".stack")).toggleClass(o("hidden"))}}},{key:"formatMsg",value:function(){var e=this.args,t=this.type,n=this.id,o=this.header,r=this.group,i=this.console.c;e=Re()(e);var a,s,c="";switch("group"!==t&&"groupCollapsed"!==t||0===e.length&&(e=["console.group"]),t){case"log":case"info":case"debug":c=this.formatCommon(e);break;case"dir":c=this.formatDir(e);break;case"warn":a="warn",c=this.formatCommon(e);break;case"error":he()(e[0])&&1!==e.length&&(e=this.substituteStr(e)),s=e[0],a="error",s=pe()(s)?s:new Error(this.formatCommon(e)),c=this.formatErr(s);break;case"table":c=this.formatTable(e);break;case"html":c=e[0];break;case"input":c=this.formatJs(e[0]),a="input";break;case"output":c=this.formatCommon(e),a="output";break;case"groupCollapsed":c=this.formatCommon(e),a="caret-right";break;case"group":c=this.formatCommon(e),a="caret-down"}if(m()(["log","debug","warn"],t)&&this.isSimple()&&(c=at()(c,(function(e){return'').concat(e,"")}))),c=this.render({msg:c,type:t,icon:a,id:n,header:o,group:r}),this.$container.addClass("".concat(i("log-container"))).html(c),"table"===t)E()(this.columns)||this.renderTable(e);E()(this.elements)||this.renderEl(),this.$content=this.$container.find(i(".log-content")),this.content=this.$content.get(0)}},{key:"render",value:function(e){var t=this.console.c,n="",r="";if(e.group)for(var i=e.group.indentLevel,a=0;a');e.header&&(n+=ht()(oe||(oe=(0,o.A)(['\n
\n ','\n
\n '," ","\n
\n
"])),t("header"),r,t("time-from-container"),e.header.time,e.header.from));var s="";return e.icon&&(s='
')),n+='\n
\n ').concat(r,"\n ").concat(s,'\n
\n
\n
\n
\n
').concat(e.msg,"
\n
\n
")}},{key:"formatTable",value:function(e){var t=e[0],n=e[1],o=[];return he()(n)&&(n=Me()(n)),ze()(n)||(n=null),ue()(t)?(x()(t,(function(e){ge()(e)?o.push("__LunaConsoleValue"):ue()(e)&&(o=o.concat(He()(e)))})),(o=Be()(o)).sort(),n&&(o=o.filter((function(e){return m()(n,e)}))),o.length>20&&(o=o.slice(0,20)),E()(o)?this.formatCommon(e):(this.columns=o,this.console.c('
')+this.formatPreview(t))):this.formatCommon(e)}},{key:"formatErr",value:function(e){var t=e.stack?e.stack.split("\n"):[],n=e.name?"".concat(e.name,": "):"";return n+="".concat(e.message||t[0],"
"),t=t.map((function(e){return f()(e)})),n+'
').concat(t.slice(1).join("
"),"
").replace(kt,(function(e){return'').concat(e,"")}))}},{key:"formatCommon",value:function(e){var t=this.console.c,n=he()(e[0])&&1!==e.length;n&&(e=this.substituteStr(e));for(var o=0,r=e.length;o').concat(f()(v()(i)),"
"):ue()(i)?e[o]=this.formatPreview(i):Te()(i)?e[o]='undefined'):Se()(i)?e[o]='null'):H()(i)?e[o]='').concat(v()(i),""):"bigint"==typeof i?e[o]='').concat(v()(i),"n"):bt()(i)?e[o]='').concat(v()(i),""):At()(i)?e[o]='').concat(f()(v()(i)),""):(i=v()(i),0===o&&n||(i=f()(i)),i.length>5e3&&(i=N()(i,5e3,{separator:" ",ellipsis:"…"})),e[o]=i)}return e.join(" ")}},{key:"formatDir",value:function(e){return ue()(e[0])?this.formatPreview(e[0]):this.formatCommon(e)}},{key:"formatTableVal",value:function(e){var t=this.console.c;return ue()(e)?"{…}":ge()(e)?pt()('
').concat(X(e),"
")):v()(e)}},{key:"formatPreview",value:function(e){var t=this,n=this.console.c,o=gt()();this.lazyEvaluation?this.objects[o]=e:this.extractObj(e,{},(function(e){t.objects[o]=e}));var r=m()(["dir","table"],this.type),i=M(e);return"Array"===i&&e.length>1?(i="(".concat(e.length,")"),r&&(i="Array".concat(i))):"RegExp"===i?i=v()(e):Ae()(e)&&(i=this.formatElName(e)),'
')+'
')+'
')+'')+'').concat(f()(i)," ")+'').concat(r?"":X(e,{getterVal:this.accessGetter,unenumerable:!1}),"")+"
"+'
')}},{key:"substituteStr",value:function(e){var t=f()(e[0]),n=!1,o="";e.shift();for(var r=0,i=t.length;r"),n=!0,o+='');break;default:r--,e.unshift(s),o+=a}}else o+=a}return n&&(o+=""),e.unshift(o),e}},{key:"formatJs",value:function(e){var t=ct()(e,"js",Ct);return t!==e&&(t=this.console.c(t)),'
').concat(t,"
")}},{key:"formatFn",value:function(e){return'
'.concat(this.formatJs(e.toString()),"
")}},{key:"formatElName",value:function(e){var t=e.id,n=e.className,o=e.tagName.toLowerCase();if(""!==t&&(o+="#".concat(t)),he()(n)){var r="";x()(n.split(/\s+/g),(function(e){""!==e.trim()&&(r+=".".concat(e))})),o+=r}return o}},{key:"formatEl",value:function(e){var t=gt()();return this.elements[t]=e,this.console.c('
'))}}])}(We());function Et(e){var t=(e=Ge()(e)).split(";"),n={};x()(t,(function(e){if(m()(e,":")){var t=i(e.split(":"),2),o=t[0],r=t[1];n[B()(o)]=B()(r)}})),n.display="inline-block",n["max-width"]="100%",delete n.width,delete n.height;var o="";return x()(n,(function(e,t){o+="".concat(t,":").concat(e,";")})),o}var Tt=n(5820),Nt=n.n(Tt),Ot=n(3981),jt=n.n(Ot),Mt=n(8105),It=n.n(Mt),zt=n(7005),Dt=n.n(zt),Bt=n(3497),Ft=n.n(Bt),Rt=n(5865),Lt=n.n(Rt),Gt=n(8862),Pt=n.n(Gt),Ht=n(7030),Yt=n.n(Ht),$t=n(961),qt=n.n($t),Qt=n(7e3),Ut=n.n(Qt);function Wt(){try{var e=!Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){})))}catch(e){}return(Wt=function(){return!!e})()}var Jt,Kt=function(e){function t(e,n){var o,r,i=n.compName,s=(arguments.length>2&&void 0!==arguments[2]?arguments[2]:{}).theme,u=void 0===s?"light":s;return(0,a.A)(this,t),o=function(e,t,n){return t=(0,l.A)(t),(0,c.A)(e,Wt()?Reflect.construct(t,n||[],(0,l.A)(e).constructor):t.apply(e,n))}(this,t),o.subComponents=[],o.theme="",o.onThemeChange=function(e){"auto"===o.options.theme&&o.setTheme(e)},o.compName=i,o.c=J(i),o.options={},o.container=e,o.$container=$e()(e),o.$container.addClass(["luna-".concat(i),o.c("platform-".concat((r=Q()(),"os x"===r?"mac":r)))]),o.on("changeOption",(function(e,t){if("theme"===e&&t){var n=t;"auto"===t&&(n=Ut().get()),o.setTheme(n),x()(o.subComponents,(function(e){return e.setOption("theme",t)}))}})),Ut().on("change",o.onThemeChange),o.setOption("theme",u),o}return(0,d.A)(t,e),(0,s.A)(t,[{key:"destroy",value:function(){var e=this;this.destroySubComponents();var t=this.$container,n=t.attr("class");x()(n.split(/\s+/),(function(n){y()(n,"luna-".concat(e.compName))&&t.rmClass(n)})),t.html(""),this.emit("destroy"),this.removeAllListeners(),Ut().off("change",this.onThemeChange)}},{key:"setOption",value:function(e,t){var n=this,o=this.options,r={};"string"==typeof e?r[e]=t:r=e,x()(r,(function(e,t){var r=o[t];o[t]=e,e!==r&&n.emit("changeOption",t,e,r)}))}},{key:"getOption",value:function(e){return this.options[e]}},{key:"addSubComponent",value:function(e){e.setOption("theme",this.options.theme),this.subComponents.push(e)}},{key:"removeSubComponent",value:function(e){qt()(this.subComponents,(function(t){return t===e}))}},{key:"destroySubComponents",value:function(){x()(this.subComponents,(function(e){return e.destroy()})),this.subComponents=[]}},{key:"initOptions",value:function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};be()(e,t),It()(this.options,e)}},{key:"find",value:function(e){return this.$container.find(this.c(e))}},{key:"setTheme",value:function(e){var t=this.c,n=this.$container;this.theme&&n.rmClass(t("theme-".concat(this.theme))),n.addClass(t("theme-".concat(e))),this.theme=e}}])}(We()),Vt=n(2228),Zt=n.n(Vt);function Xt(){try{var e=!Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){})))}catch(e){}return(Xt=function(){return!!e})()}e=n.hmd(e);var en=navigator.userAgent,tn=en.indexOf("Android")>-1||en.indexOf("Adr")>-1,nn=0,on=function(e){function t(e){var n,o,r,i,s=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};return(0,a.A)(this,t),o=this,r=t,i=[e,{compName:"console"},s],r=(0,l.A)(r),(n=(0,c.A)(o,Xt()?Reflect.construct(r,i||[],(0,l.A)(o).constructor):r.apply(o,i))).spaceHeight=0,n.topSpaceHeight=0,n.bottomSpaceHeight=0,n.lastScrollTop=0,n.lastTimestamp=0,n.speedToleranceFactor=100,n.maxSpeedTolerance=2e3,n.minSpeedTolerance=100,n.logs=[],n.displayLogs=[],n.timer={},n.counter={},n.asyncList=[],n.asyncTimer=null,n.isAtBottom=!0,n.groupStack=new(Dt()),n.selectedLog=null,n.onScroll=function(){var e=n.container,t=e.scrollHeight,o=e.offsetHeight,r=e.scrollTop;if(!(r<=0||o+r>t)){var i=!1;(t===o||Math.abs(t-o-r)<1)&&(i=!0),n.isAtBottom=i;var a=n.lastScrollTop,s=n.lastTimestamp,c=jt()(),l=c-s,u=r-a,d=Math.abs(u/l)*n.speedToleranceFactor;l>1e3&&(d=1e3),d>n.maxSpeedTolerance&&(d=n.maxSpeedTolerance),dr+o+f||n.renderViewport({topTolerance:2*h,bottomTolerance:2*f})}},n.initTpl(),n.initOptions(s,{maxNum:0,asyncRender:!0,showHeader:!1,filter:"",level:["verbose","info","warning","error"],accessGetter:!1,unenumerable:!0,lazyEvaluation:!0}),n.$el=n.find(".logs"),n.el=n.$el.get(0),n.$fakeEl=n.find(".fake-logs"),n.fakeEl=n.$fakeEl.get(0),n.$space=n.find(".logs-space"),n.space=n.$space.get(0),tn&&(n.speedToleranceFactor=800,n.maxSpeedTolerance=3e3,n.minSpeedTolerance=800),n.resizeSensor=new(ce())(e),n.renderViewport=Lt()((function(e){n._renderViewport(e)}),16),n.global={copy:function(e){he()(e)||(e=JSON.stringify(e,null,2)),et()(e)},$:function(e){return document.querySelector(e)},$$:function(e){return Me()(document.querySelectorAll(e))},$x:function(e){return Pt()(e)},clear:function(){n.clear()},dir:function(e){n.dir(e)},table:function(e,t){n.table(e,t)},keys:He()},n.bindEvent(),n}return(0,d.A)(t,e),(0,s.A)(t,[{key:"setGlobal",value:function(e,t){this.global[e]=t}},{key:"destroy",value:function(){var e,n,o,r,i;this.$container.off("scroll",this.onScroll),this.resizeSensor.destroy(),(e=t,n="destroy",o=this,r=3,i=(0,u.A)((0,l.A)(1&r?e.prototype:e),n,o),2&r&&"function"==typeof i?function(e){return i.apply(o,e)}:i)([])}},{key:"count",value:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"default",t=this.counter;Te()(t[e])?t[e]=1:t[e]++,this.info("".concat(e,": ").concat(t[e]))}},{key:"countReset",value:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"default";this.counter[e]=0}},{key:"assert",value:function(){for(var e=arguments.length,t=new Array(e),n=0;n0&&void 0!==arguments[0]?arguments[0]:"default";if(this.timer[e])return this.insert("warn",["Timer '".concat(e,"' already exists")]);this.timer[e]=Nt()()}},{key:"timeLog",value:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"default",t=this.timer[e];if(!t)return this.insert("warn",["Timer '".concat(e,"' does not exist")]);this.info("".concat(e,": ").concat(Nt()()-t,"ms"))}},{key:"timeEnd",value:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"default";this.timeLog(e),delete this.timer[e]}},{key:"clear",value:function(){var e=arguments.length>0&&void 0!==arguments[0]&&arguments[0];this.logs=[],this.displayLogs=[],this.selectLog(null),this.lastLog=void 0,this.counter={},this.timer={},this.groupStack=new(Dt()),this.asyncList=[],this.asyncTimer&&(clearTimeout(this.asyncTimer),this.asyncTimer=null),e?this.render():this.insert("log",["%cConsole was cleared","color:#808080;font-style:italic;"])}},{key:"info",value:function(){for(var e=arguments.length,t=new Array(e),n=0;n0&&(o.group=a.peek()),It()(o,{id:++nn,accessGetter:l,unenumerable:u,lazyEvaluation:d}),"group"===o.type||"groupCollapsed"===o.type){var h={id:gt()("group"),collapsed:!1,parent:a.peek(),indentLevel:a.size+1};"groupCollapsed"===o.type&&(h.collapsed=!0),o.targetGroup=h,a.push(h)}var f=new St(this,o);f.on("updateHeight",(function(){r.isAtBottom=!1,r.renderViewport()}));var p=this.lastLog;if(p&&!m()(["html","group","groupCollapsed"],f.type)&&p.type===f.type&&f.isSimple()&&p.text()===f.text()?(p.addCount(),f.header&&p.updateTime(f.header.time),f=p,this.detachLog(p)):(i.push(f),this.lastLog=f),0!==c&&i.length>c){var v=i[0];this.detachLog(v),i.shift()}this.attachLog(f),this.emit("insert",f)}},{key:"updateTopSpace",value:function(e){this.topSpaceHeight=e,this.el.style.top=e+"px"}},{key:"updateBottomSpace",value:function(e){this.bottomSpaceHeight=e}},{key:"updateSpace",value:function(e){this.spaceHeight!==e&&(this.spaceHeight=e,this.space.style.height=e+"px")}},{key:"detachLog",value:function(e){var t=this.displayLogs,n=t.indexOf(e);n>-1&&(t.splice(n,1),this.renderViewport())}},{key:"attachLog",value:function(e){if(this.filterLog(e)&&!e.collapsed){var t=this.displayLogs;if(0===t.length)return t.push(e),void this.renderViewport();var n=Ft()(t);if(e.id>n.id)return t.push(e),void this.renderViewport();for(var o,r=0,i=t.length-1,a=0;r<=i;){if((o=t[a=r+Math.floor((i-r)/2)]).id===e.id)return;o.id0&&void 0!==arguments[0]?arguments[0]:20,n=this.asyncList;this.asyncTimer||(this.asyncTimer=setTimeout((function(){e.asyncTimer=null;var t,o,r=!1,a=n.length;a<1e3?(o=200,t=400):a<5e3?(o=500,t=800):a<1e4?(o=800,t=1e3):a<25e3?(o=1e3,t=1200):a<5e4?(o=1500,t=1500):(o=2e3,t=2500),o>a&&(o=a,r=!0);for(var s=0;s0&&o.length>n&&(e.logs=o.slice(o.length-n),e.render());break;case"filter":e.render();break;case"level":e.options.level=Me()(n),e.render()}})),this.$container.on("scroll",this.onScroll)}},{key:"_renderViewport",value:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},t=e.topTolerance,n=void 0===t?500:t,o=e.bottomTolerance,r=void 0===o?500:o,i=this.el,a=this.container,s=this.space;if(!W()(a)){for(var c=a.scrollTop,l=a.offsetHeight,u=s.getBoundingClientRect().width,d=c-n,h=c+l+r,f=this.displayLogs,p=0,v=0,g=0,m=f.length,b=this.fakeEl,y=document.createDocumentFragment(),A=[],w=0;w0){b.appendChild(y);for(var k=0,C=A.length;kh?v+=O:g+O>d?S.appendChild(N):g\n
\n
\n \n '])))))}}])}(Kt),rn=function(){return Yt()("HH:MM:ss ")};function an(){for(var e=new Error,t="",n=e.stack?e.stack.split("\n"):"",o=0,r=n.length;o-1&&o0)&&!(o=i.next()).done;)a.push(o.value)}catch(e){r={error:e}}finally{try{o&&!o.done&&(n=i.return)&&n.call(i)}finally{if(r)throw r.error}}return a},o=this&&this.__values||function(e){var t="function"==typeof Symbol&&Symbol.iterator,n=t&&e[t],o=0;if(n)return n.call(e);if(e&&"number"==typeof e.length)return{next:function(){return e&&o>=e.length&&(e=void 0),{value:e&&e[o++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")};function r(e,t){var n=e[3];return[(1-n)*t[0]+n*e[0],(1-n)*t[1]+n*e[1],(1-n)*t[2]+n*e[2],n+t[3]*(1-n)]}function i(e){var t=n(e,3),o=t[0],r=t[1],i=t[2];return.2126*(o<=.03928?o/12.92:Math.pow((o+.055)/1.055,2.4))+.7152*(r<=.03928?r/12.92:Math.pow((r+.055)/1.055,2.4))+.0722*(i<=.03928?i/12.92:Math.pow((i+.055)/1.055,2.4))}Object.defineProperty(t,"__esModule",{value:!0}),t.getContrastThreshold=t.isLargeFont=t.getAPCAThreshold=t.desiredLuminanceAPCA=t.contrastRatioByLuminanceAPCA=t.contrastRatioAPCA=t.luminanceAPCA=t.contrastRatio=t.luminance=t.rgbaToHsla=t.blendColors=void 0,t.blendColors=r,t.rgbaToHsla=function(e){var t=n(e,4),o=t[0],r=t[1],i=t[2],a=t[3],s=Math.max(o,r,i),c=Math.min(o,r,i),l=s-c,u=s+c,d=.5*u;return[c===s?0:o===s?(1/6*(r-i)/l+1)%1:r===s?1/6*(i-o)/l+1/3:1/6*(o-r)/l+2/3,0===d||1===d?0:d<=.5?l/u:l/(2-u),d,a]},t.luminance=i,t.contrastRatio=function(e,t){var n=i(r(e,t)),o=i(t);return(Math.max(n,o)+.05)/(Math.min(n,o)+.05)};var a=12.82051282051282,s=.06;function c(e){var t=n(e,3),o=t[0],r=t[1],i=t[2];return.2126729*Math.pow(o,2.4)+.7151522*Math.pow(r,2.4)+.072175*Math.pow(i,2.4)}function l(e){return e>.03?e:e+Math.pow(.03-e,1.45)}function u(e,t){if(e=l(e),t=l(t),Math.abs(e-t)<5e-4)return 0;var n=0;return 100*(n=t>=e?(n=1.25*(Math.pow(t,.55)-Math.pow(e,.58)))<.001?0:n<.078?n-n*a*s:n-s:(n=1.25*(Math.pow(t,.62)-Math.pow(e,.57)))>-.001?0:n>-.078?n-n*a*s:n+s)}t.luminanceAPCA=c,t.contrastRatioAPCA=function(e,t){return u(c(e),c(t))},t.contrastRatioByLuminanceAPCA=u,t.desiredLuminanceAPCA=function(e,t,n){function o(){return n?Math.pow(Math.abs(Math.pow(e,.62)-(-t-s)/1.25),1/.57):Math.pow(Math.abs(Math.pow(e,.55)-(t+s)/1.25),1/.58)}e=l(e),t/=100;var r=o();return(r<0||r>1)&&(n=!n,r=o()),r};var d=[[12,-1,-1,-1,-1,100,90,80,-1,-1],[14,-1,-1,-1,100,90,80,60,60,-1],[16,-1,-1,100,90,80,60,55,50,50],[18,-1,-1,90,80,60,55,50,40,40],[24,-1,100,80,60,55,50,40,38,35],[30,-1,90,70,55,50,40,38,35,40],[36,-1,80,60,50,40,38,35,30,25],[48,100,70,55,40,38,35,30,25,20],[60,90,60,50,38,35,30,25,20,20],[72,80,55,40,35,30,25,20,20,20],[96,70,50,35,30,25,20,20,20,20],[120,60,40,30,25,20,20,20,20,20]];function h(e,t){var n=72*parseFloat(e.replace("px",""))/96;return-1!==["bold","bolder","600","700","800","900"].indexOf(t)?n>=14:n>=18}d.reverse(),t.getAPCAThreshold=function(e,t){var r,i,a,s,c=parseFloat(e.replace("px","")),l=parseFloat(t);try{for(var u=o(d),h=u.next();!h.done;h=u.next()){var f=n(h.value),p=f[0],v=f.slice(1);if(c>=p)try{for(var g=(a=void 0,o([900,800,700,600,500,400,300,200,100].entries())),m=g.next();!m.done;m=g.next()){var b=n(m.value,2),y=b[0];if(l>=b[1]){var A=v[v.length-1-y];return-1===A?null:A}}}catch(e){a={error:e}}finally{try{m&&!m.done&&(s=g.return)&&s.call(g)}finally{if(a)throw a.error}}}}catch(e){r={error:e}}finally{try{h&&!h.done&&(i=u.return)&&i.call(u)}finally{if(r)throw r.error}}return null},t.isLargeFont=h;var f={aa:3,aaa:4.5},p={aa:4.5,aaa:7};t.getContrastThreshold=function(e,t){return h(e,t)?f:p}},3722:function(e,t,n){var o=n(3145);t=function(e){return Object.freeze?Object.freeze(e):(o(e).forEach((function(t){Object.getOwnPropertyDescriptor(e,t).configurable&&Object.defineProperty(e,t,{writable:!1,configurable:!1})})),e)},e.exports=t},3737:function(e,t,n){var o=n(2263);t=o.extend({className:"MediaQuery",initialize:function(e){var t=this;this.callSuper(o,"initialize"),this._listener=function(){t.emit(t.isMatch()?"match":"unmatch")},this.setQuery(e)},setQuery:function(e){this._mql&&this._mql.removeListener(this._listener),this._mql=window.matchMedia(e),this._mql.addListener(this._listener)},isMatch:function(){return this._mql.matches}}),e.exports=t},3750:function(e,t,n){var o=n(3974);t=function(e){return"[object Blob]"===o(e)},e.exports=t},3752:function(e,t,n){var o=n(2510),r=n(769);t=o((function(e,t){return function(){var n=[];return n=(n=n.concat(t)).concat(r(arguments)),e.apply(this,n)}})),e.exports=t},3793:function(e,t,n){var o=n(8966),r=n(8971),i=n(3612),a=n(9100);function s(e){return function(t,n,s,c){t=i(t),r(c)&&(c=s,s=void 0),a(t,(function(t){o[e](t,n,s,c)}))}}t={on:s("add"),off:s("remove")},e.exports=t},3805:function(e,t,n){var o=n(2561);t=function(e){return o(e).toLocaleUpperCase()},e.exports=t},3915:function(e,t,n){var o=n(5693),r=n(3145),i=n(5793);t=function(e,t,n){t=o(t,n);for(var a=!i(e)&&r(e),s=(a||e).length,c=Array(s),l=0;l=e.length&&(e=void 0),{value:e&&e[o++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},r=this&&this.__read||function(e,t){var n="function"==typeof Symbol&&e[Symbol.iterator];if(!n)return e;var o,r,i=n.call(e),a=[];try{for(;(void 0===t||t-- >0)&&!(o=i.next()).done;)a.push(o.value)}catch(e){r={error:e}}finally{try{o&&!o.done&&(n=i.return)&&n.call(i)}finally{if(r)throw r.error}}return a},i=this&&this.__spreadArray||function(e,t,n){if(n||2===arguments.length)for(var o,r=0,i=t.length;r]*>/g.test(e))try{var t=Oe().parse(e);return De(t,(function(e){e.attrs&&e.attrs.class&&(e.attrs.class=Be(e.attrs.class))})),Oe().stringify(t)}catch(t){return Be(e)}return Be(e)}function De(e,t){for(var n=0,o=e.length;n3||Math.abs(a)>3)&&(r._isClick=!1);var s=r._oldX+i,c=r._oldY+a;s<0?s=0:s>n-t&&(s=n-t),c<0?c=0:c>o-t&&(c=o-t),r._$el.css({left:s,top:c})})),(0,c.A)(r,"_onDragEnd",(function(e){var t=r._$el;r._isClick&&r.emit("click"),r._onDragMove(e),Ge.off(pe()("move"),r._onDragMove),Ge.off(pe()("up"),r._onDragEnd);var n=r.config;n.get("rememberPos")&&n.set("pos",{x:Me(t.css("left")),y:Me(t.css("top"))}),t.rmClass("eruda-active")})),r._style=re(n(6717)),r._$container=e,r._initTpl(),r._bindEvent(),r._registerListener(),r}return(0,s.A)(t,e),(0,r.A)(t,[{key:"hide",value:function(){this._$el.hide()}},{key:"show",value:function(){this._$el.show()}},{key:"setPos",value:function(e){this._isOutOfRange(e)&&(e=this._getDefPos()),this._$el.css({left:e.x,top:e.y}),this.config.set("pos",e)}},{key:"getPos",value:function(){return this.config.get("pos")}},{key:"destroy",value:function(){re.remove(this._style),this._unregisterListener(),this._$el.remove()}},{key:"_isOutOfRange",value:function(e){e=e||this.config.get("pos");var t=this._getDefPos();return e.x>t.x+10||e.x<0||e.y<0||e.y>t.y+10}},{key:"_registerListener",value:function(){var e=this;this._scaleListener=function(){return ue()((function(){e._isOutOfRange()&&e._resetPos()}))},h.on(h.SCALE,this._scaleListener)}},{key:"_unregisterListener",value:function(){h.off(h.SCALE,this._scaleListener)}},{key:"_initTpl",value:function(){var e=this._$container;e.append(ze('
')),this._$el=e.find(".eruda-entry-btn")}},{key:"_resetPos",value:function(e){var t=this.config,n=t.get("pos"),o=this._getDefPos();t.get("rememberPos")&&!e||(n=o),this.setPos(n)}},{key:"_bindEvent",value:function(){var e=this;this._$el.on(pe()("down"),this._onDragStart),he().on("change",(function(){return e._resetPos(!0)})),window.addEventListener("resize",(function(){return e._resetPos()}))}},{key:"initCfg",value:function(e){var t=this.config=ce.createCfg("entry-button",{rememberPos:!0,pos:this._getDefPos()});e.switch(t,"rememberPos","Remember Entry Button Position"),this._resetPos()}},{key:"_getDefPos",value:function(){var e=this._$el.get(0).offsetWidth+10;return{x:window.innerWidth-e,y:window.innerHeight-e}}}])}(u()),He=n(5458),Ye=n(7622),$e=Le=new(n.n(Ye)())("[Eruda]","warn");Le.formatter=function(e,t){return t.unshift(this.name),t};var qe=n(6097),Qe=n.n(qe),Ue=n(7e3),We=n.n(Ue),Je=n(9931),Ke=n.n(Je),Ve=n(1009),Ze=n.n(Ve),Xe=n(5570),et=n.n(Xe),tt=n(2556),nt=n(1944),ot=n(8052);function rt(){try{var e=!Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){})))}catch(e){}return(rt=function(){return!!e})()}var it=function(e){function t(e){var r,s,l,u,d=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},h=d.defaults,f=void 0===h?{}:h,p=d.inline,v=void 0!==p&&p;return(0,o.A)(this,t),s=this,l=t,l=(0,a.A)(l),r=(0,i.A)(s,rt()?Reflect.construct(l,u||[],(0,a.A)(s).constructor):l.apply(s,u)),(0,c.A)(r,"_checkSafeArea",(function(){var e=r.$container;!function(){var e=!1,t=document.createElement("div");if(CSS.supports("padding-bottom: env(safe-area-inset-bottom)")?(t.style.paddingBottom="env(safe-area-inset-bottom)",e=!0):CSS.supports("padding-bottom: constant(safe-area-inset-bottom)")&&(t.style.paddingBottom="constant(safe-area-inset-bottom)",e=!0),e){document.body.appendChild(t);var n=parseInt(window.getComputedStyle(t).paddingBottom);if(document.body.removeChild(t),n>0)return!0}return!1}()?e.rmClass(ze("safe-area")):e.addClass(ze("safe-area"))})),(0,c.A)(r,"_updateTabHeight",(function(e){r._tab.setOption("height",40*e),ue()((function(){r._tab.updateSlider()}))})),r._defCfg=P()({transparency:1,displaySize:80,theme:"System preference"},f),r._style=re(n(541)),r.$container=e,r._isShow=!1,r._opacity=1,r._tools={},r._isResizing=!1,r._resizeTimer=null,r._resizeStartY=0,r._resizeStartSize=0,r._inline=v,r._initTpl(),r._initTab(),r._initNotification(),r._initModal(),et()((function(){return r._checkSafeArea()})),r._bindEvent(),r}return(0,s.A)(t,e),(0,r.A)(t,[{key:"show",value:function(){var e=this;return this._isShow=!0,this._$el.show(),this._tab.updateSlider(),setTimeout((function(){e._$el.css("opacity",e._opacity)}),50),this.emit("show"),this}},{key:"hide",value:function(){var e=this;if(!this._inline)return this._isShow=!1,this.emit("hide"),this._$el.css({opacity:0}),setTimeout((function(){return e._$el.hide()}),300),this}},{key:"toggle",value:function(){return this._isShow?this.hide():this.show()}},{key:"add",value:function(e){var t=this._tab;if(!(e instanceof v)){var n=new v,o=n.init,r=n.show,i=n.hide,a=n.destroy;L()(e,{init:o,show:r,hide:i,destroy:a})}var s=e.name;if(!s)return $e.error("You must specify a name for a tool");if(this._tools[s])return $e.warn("Tool ".concat(s," already exists"));var c=s.replace(/\s+/g,"-");return this._$tools.prepend('
')),e.init(this._$tools.find(".".concat(ze(c),".").concat(ze("tool"))),this),e.active=!1,this._tools[s]=e,"settings"===s?t.append({id:s,title:s}):t.insert(t.length-1,{id:s,title:s}),this}},{key:"remove",value:function(e){var t=this._tools;if(!t[e])return $e.warn("Tool ".concat(e," doesn't exist"));this._tab.remove(e);var n=t[e];if(delete t[e],n.active){var o=D()(t);o.length>0&&this.showTool(t[Ae()(o)].name)}return n.destroy(),this}},{key:"removeAll",value:function(){var e=this;return x()(this._tools,(function(t){return e.remove(t.name)})),this}},{key:"get",value:function(e){var t=this._tools[e];if(t)return t}},{key:"showTool",value:function(e){if(this._curTool===e)return this;this._curTool=e;var t=this._tools,n=t[e];if(n){var o={};return x()(t,(function(e){e.active&&(o=e,e.active=!1,e.hide())})),n.active=!0,n.show(),this._tab.select(e),this.emit("showTool",e,o),this}}},{key:"initCfg",value:function(e){var t=this,n=this.config=ce.createCfg("dev-tools",this._defCfg);this._setTransparency(n.get("transparency")),this._setDisplaySize(n.get("displaySize")),this._setTheme(n.get("theme")),n.on("change",(function(e,n){switch(e){case"transparency":return t._setTransparency(n);case"displaySize":return t._setDisplaySize(n);case"theme":return t._setTheme(n)}})),e.separator().select(n,"theme","Theme",["System preference"].concat((0,He.A)(D()(re.getThemes())))),this._inline||e.range(n,"transparency","Transparency",{min:.2,max:1,step:.01}).range(n,"displaySize","Display Size",{min:40,max:100,step:1}),e.button("Restore defaults and reload",(function(){var e=je("local"),t=JSON.parse(JSON.stringify(e));x()(t,(function(t,n){E()(t)&&Ze()(n,"eruda")&&e.removeItem(n)})),window.location.reload()})).separator()}},{key:"notify",value:function(e,t){this._notification.notify(e,t)}},{key:"destroy",value:function(){re.remove(this._style),this.removeAll(),this._tab.destroy(),this._$el.remove(),window.removeEventListener("resize",this._checkSafeArea),h.off(h.SCALE,this._updateTabHeight)}},{key:"_setTheme",value:function(e){var t=this.$container;"System preference"===e&&(e=Ke()(We().get())),K(e)?t.addClass(ze("dark")):t.rmClass(ze("dark")),re.setTheme(e)}},{key:"_setTransparency",value:function(e){Qe()(e)&&(this._opacity=e,this._isShow&&this._$el.css({opacity:e}))}},{key:"_setDisplaySize",value:function(e){this._inline&&(e=100),Qe()(e)&&this._$el.css({height:e+"%"})}},{key:"_initTpl",value:function(){var e=this.$container;e.append(ze('\n
\n
\n
\n
\n
\n \n
\n ')),this._$el=e.find(ze(".dev-tools")),this._$tools=this._$el.find(ze(".tools"))}},{key:"_initTab",value:function(){var e=this;this._tab=new ot.A(this._$el.find(ze(".tab")).get(0),{height:40}),this._tab.on("select",(function(t){return e.showTool(t)}))}},{key:"_initNotification",value:function(){this._notification=new tt.A(this._$el.find(ze(".notification")).get(0),{position:{x:"center",y:"top"}})}},{key:"_initModal",value:function(){nt.A.setContainer(this._$el.find(ze(".modal")).get(0))}},{key:"_bindEvent",value:function(){var e=this,t=this._$el.find(ze(".resizer")),n=this._$el.find(ze(".nav-bar")),o=m()(document);this._inline&&t.hide();var r=function(t){if(e._isResizing){t.preventDefault(),t.stopPropagation(),t=t.origEvent;var n=(e._resizeStartY-Fe("y",t))/window.innerHeight*100,o=e._resizeStartSize+n;o<40?o=40:o>100&&(o=100),e.config.set("displaySize",Se()(o.toFixed(2)))}},i=function(){clearTimeout(e._resizeTimer),e._isResizing=!1,t.css("height",10),o.off(pe()("move"),r),o.off(pe()("up"),i)};t.css("height",10),t.on(pe()("down"),(function(n){n.preventDefault(),n.stopPropagation(),n=n.origEvent,e._isResizing=!0,e._resizeStartSize=e.config.get("displaySize"),e._resizeStartY=Fe("y",n),t.css("height","100%"),o.on(pe()("move"),r),o.on(pe()("up"),i)})),n.on("contextmenu",(function(e){return e.preventDefault()})),this.$container.on("click",(function(e){return e.stopPropagation()})),window.addEventListener("resize",this._checkSafeArea),h.on(h.SCALE,this._updateTabHeight),We().on("change",(function(){var t=e.config.get("theme");"System preference"===t&&e._setTheme(t)}))}}])}(u()),at=n(9993),st=n.n(at),ct=n(3957),lt=n.n(ct),ut=n(1976),dt=n.n(ut),ht=n(6962),ft=n.n(ht),pt=n(8609),vt=n.n(pt),gt=n(4236),mt=n.n(gt),bt=n(3578);function yt(){try{var e=!Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){})))}catch(e){}return(yt=function(){return!!e})()}function At(e,t,n,o){var r=(0,f.A)((0,a.A)(1&o?e.prototype:e),t,n);return 2&o&&"function"==typeof r?function(e){return r.apply(n,e)}:r}ft().start();var wt=function(e){function t(){var e,n,r,s,l=(arguments.length>0&&void 0!==arguments[0]?arguments[0]:{}).name,d=void 0===l?"console":l;return(0,o.A)(this,t),n=this,r=t,r=(0,a.A)(r),e=(0,i.A)(n,yt()?Reflect.construct(r,s||[],(0,a.A)(n).constructor):r.apply(n,s)),(0,c.A)(e,"_handleShow",(function(){vt()(e._$el.get(0))||e._logger.renderViewport()})),(0,c.A)(e,"_handleErr",(function(t){e._logger.error(t)})),u().mixin(e),e.name=d,e._selectedLog=null,e}return(0,s.A)(t,e),(0,r.A)(t,[{key:"init",value:function(e,n){At(t,"init",this,3)([e]),this._container=n,this._appendTpl(),this._initCfg(),this._initLogger(),this._exposeLogger(),this._bindEvent()}},{key:"show",value:function(){At(t,"show",this,3)([]),this._handleShow()}},{key:"overrideConsole",value:function(){var e=this,t=this._origConsole={},n=window.console;return _t.forEach((function(o){var r=t[o]=st();n[o]&&(r=t[o]=n[o].bind(n)),n[o]=function(){e[o].apply(e,arguments),r.apply(void 0,arguments)}})),this}},{key:"setGlobal",value:function(e,t){this._logger.setGlobal(e,t)}},{key:"restoreConsole",value:function(){var e=this;return this._origConsole?(_t.forEach((function(t){return window.console[t]=e._origConsole[t]})),delete this._origConsole,this):this}},{key:"catchGlobalErr",value:function(){return ft().addListener(this._handleErr),this}},{key:"ignoreGlobalErr",value:function(){return ft().rmListener(this._handleErr),this}},{key:"filter",value:function(e){var t=this._$filterText,n=this._logger;E()(e)?(t.text(e),n.setOption("filter",Te()(e))):dt()(e)?(t.text(I()(e)),n.setOption("filter",e)):lt()(e)&&(t.text("ƒ"),n.setOption("filter",e))}},{key:"destroy",value:function(){this._logger.destroy(),At(t,"destroy",this,3)([]),this._container.off("show",this._handleShow),this._style&&re.remove(this._style),this.ignoreGlobalErr(),this.restoreConsole(),this._rmCfg()}},{key:"_enableJsExecution",value:function(e){var t=this._$el,n=t.find(ze(".js-input"));e?(n.show(),t.rmClass(ze("js-input-hidden"))):(n.hide(),t.addClass(ze("js-input-hidden")))}},{key:"_appendTpl",value:function(){var e=this._$el;this._style=re(n(6567)),e.append(ze('\n
\n \n All\n Info\n Warning\n Error\n \n \n \n
\n
\n
\n
\n
Cancel
\n
Execute
\n
\n \n \n
\n '));var t=e.find(ze(".js-input")),o=t.find("textarea"),r=t.find(ze(".buttons"));P()(this,{_$control:e.find(ze(".control")),_$logs:e.find(ze(".logs-container")),_$inputContainer:t,_$input:o,_$inputBtns:r,_$filterText:e.find(ze(".filter-text"))})}},{key:"_initLogger",value:function(){var e=this.config,t=e.get("maxLogNum");t="infinite"===t?0:+t;var n=this._$control.find(ze(".level")),o=new bt.A(this._$logs.get(0),{asyncRender:e.get("asyncRender"),maxNum:t,showHeader:e.get("displayExtraInfo"),unenumerable:e.get("displayUnenumerable"),accessGetter:e.get("displayGetterVal"),lazyEvaluation:e.get("lazyEvaluation")});o.on("optionChange",(function(e,t){if("level"===e)n.each((function(){var e=m()(this),n=e.data("level");e[n===t||"all"===n&&Y()(t)?"addClass":"rmClass"](ze("active"))}))})),e.get("overrideConsole")&&this.overrideConsole(),this._logger=o}},{key:"_exposeLogger",value:function(){var e=this,t=this._logger;["html"].concat(_t).forEach((function(n){return e[n]=function(){for(var o=arguments.length,r=new Array(o),i=0;i').concat(Tt()(e.data),""));var n="Empty";e.reqHeaders&&(n=_e()(e.reqHeaders,(function(e,t){return'\n ').concat(Tt()(t),"\n ").concat(Tt()(e),"\n ")})).join(""));var o="Empty";e.resHeaders&&(o=_e()(e.resHeaders,(function(e,t){return'\n ').concat(Tt()(t),"\n ").concat(Tt()(e),"\n ")})).join(""));var r="";if(e.resTxt){var i=e.resTxt;i.length>Ft&&(i=zt()(i,Ft)),r='
').concat(Tt()(i),"
")}var a='
\n \n \n ').concat(Tt()(e.url),'\n \n
\n
\n ').concat(t,'\n
\n

Response Headers

\n \n \n ').concat(o,'\n \n
\n
\n
\n

Request Headers

\n \n \n ').concat(n,"\n \n
\n
\n ").concat(r,"\n
");this._$container.html(a).show(),this._detailData=e}},{key:"hide",value:function(){this._$container.hide(),this.emit("hide")}},{key:"_bindEvent",value:function(){var e=this,t=this._devtools;this._$container.on("click",ze(".back"),(function(){return e.hide()})).on("click",ze(".copy-res"),this._copyRes).on("click",ze(".http .response"),(function(){var t=e._detailData,o=t.resTxt;if(Mt()(o))return n("object",o);switch(t.subType){case"css":return n("css",o);case"html":return n("html",o);case"javascript":return n("js",o);case"json":return n("object",o)}return"image"===t.type?n("img",t.url):void 0}));var n=function(e,n){var o=t.get("sources");o&&(o.set(e,n),t.showTool("sources"))}}}])}(u()),Ft=1e5,Rt=n(5865),Lt=n.n(Rt),Gt=n(6476),Pt=n.n(Gt),Ht=n(5334),Yt=n(2480),$t=n(6192),qt=n(5689);function Qt(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);t&&(o=o.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,o)}return n}function Ut(e){for(var t=1;t=300)&&(n.hasErr=!0),i&&(n.resHeaders=i,e._updateType(n)),n.render()}})),(0,c.A)(e,"_loadingFinished",(function(t){var n=e._requests[t.requestId];if(e._isRecording&&n){var o=1e3*t.timestamp;n.time=o-n.startTime,n.displayTime=kt()(n.time),n.size=t.encodedDataLength,n.done=!0,n.resTxt=Jt.domain("Network").getResponseBody({requestId:t.requestId}).body,n.render()}})),(0,c.A)(e,"_loadingFailed",(function(t){var n=e._requests[t.requestId];if(e._isRecording&&n){var o=1e3*t.timestamp;n.time=o-n.startTime,n.displayTime=kt()(n.time),n.hasErr=!0,n.status=0,n.done=!0,n.render()}})),(0,c.A)(e,"_copyCurl",(function(){var t=e._selectedRequest;Ot()(function(e){var t=nn()();"windows"===t&&(t="win");var n=[],o=rn()(["accept-encoding","host","method","path","scheme","version"]),r="win"===t?function(e){var t=/[\r\n]/.test(e)?'^"':'"';return t+e.replace(/\\/g,"\\\\").replace(/"/g,'\\"').replace(/[^a-zA-Z0-9\s_\-:=+~'/.',?;()*`&]/g,"^$&").replace(/%(?=[a-zA-Z0-9_])/g,"%^").replace(/\r?\n/g,"^\n\n")+t}:function(e){return/[\0-\x1F\x7F-\x9F!]|'/.test(e)?"$'"+e.replace(/\\/g,"\\\\").replace(/'/g,"\\'").replace(/\n/g,"\\n").replace(/\r/g,"\\r").replace(/[\0-\x1F\x7F-\x9F!]/g,(function(e){for(var t=e.charCodeAt(0).toString(16);t.length<4;)t="0"+t;return"\\u"+t}))+"'":"'"+e+"'"};n.push(r(e.url()).replace(/[[{}\]]/g,"\\$&"));var i="GET",a=[],s=e.requestFormData();s&&(a.push("--data-raw "+r(s)),o["content-length"]=!0,i="POST"),e.requestMethod!==i&&n.push("-X "+r(e.requestMethod));for(var c=e.requestHeaders(),l=0;l=3?"win"===t?" ^\n ":" \\\n ":" ")}({requestMethod:t.method,url:function(){return t.url},requestFormData:function(){return t.data},requestHeaders:function(){var e=t.reqHeaders||{};return P()(e,{"User-Agent":navigator.userAgent,Referer:location.href}),_e()(e,(function(e,t){return{name:t,value:e}}))}})),e._container.notify("Copied",{icon:"success"})})),(0,c.A)(e,"_toggleRecording",(function(){e._$control.find(ze(".record")).toggleClass(ze("recording")),e._isRecording=!e._isRecording})),(0,c.A)(e,"_showDetail",(function(){e._selectedRequest&&(e._splitMode&&e._$network.css("width","50%"),e._detail.show(e._selectedRequest))})),(0,c.A)(e,"_updateScale",(function(t){e._splitMediaQuery.setQuery("screen and (min-width: ".concat(680*t,"px)"))})),e._style=re(n(8239)),e.name="network",e._requests={},e._selectedRequest=null,e._isRecording=!0,e}return(0,s.A)(t,e),(0,r.A)(t,[{key:"init",value:function(e,n){sn(t,"init",this,3)([e]),this._container=n,this._initTpl(),this._detail=new Bt(this._$detail,n),this._splitMediaQuery=new(en())("screen and (min-width: 680px)"),this._splitMode=this._splitMediaQuery.isMatch(),this._requestDataGrid=new Kt.A(this._$requests.get(0),{columns:[{id:"name",title:"Name",sortable:!0,weight:30},{id:"method",title:"Method",sortable:!0,weight:14},{id:"status",title:"Status",sortable:!0,weight:14},{id:"type",title:"Type",sortable:!0,weight:14},{id:"size",title:"Size",sortable:!0,weight:14},{id:"time",title:"Time",sortable:!0,weight:14}]}),this._resizeSensor=new(Zt())(e.get(0)),this._bindEvent()}},{key:"show",value:function(){sn(t,"show",this,3)([]),this._updateDataGridHeight()}},{key:"clear",value:function(){this._requests={},this._requestDataGrid.clear()}},{key:"requests",value:function(){var e=[];return x()(this._requests,(function(t){e.push(t)})),e}},{key:"_updateDataGridHeight",value:function(){this._requestDataGrid.fit()}},{key:"_updateType",value:function(e){var t=function(e){if(!e)return"unknown";var t=e.split(";")[0].split("/");return{type:t[0],subType:Ae()(t)}}(e.resHeaders["content-type"]||""),n=t.type,o=t.subType;e.type=n,e.subType=o}},{key:"_updateButtons",value:function(){var e=this._$control,t=e.find(ze(".show-detail")),n=e.find(ze(".copy-curl")),o=ze("icon-disabled");t.addClass(o),n.addClass(o),this._selectedRequest&&(t.rmClass(o),n.rmClass(o))}},{key:"_bindEvent",value:function(){var e=this,t=this._$control,n=this._$filterText,o=this._requestDataGrid,r=this;t.on("click",ze(".clear-request"),(function(){return e.clear()})).on("click",ze(".show-detail"),this._showDetail).on("click",ze(".copy-curl"),this._copyCurl).on("click",ze(".record"),this._toggleRecording).on("click",ze(".filter"),(function(){nt.A.prompt("Filter").then((function(e){mt()(e)||(n.text(e),o.setOption("filter",Te()(e)))}))})),o.on("select",(function(t){var n=m()(t.container).data("id"),o=r._requests[n];e._selectedRequest=o,e._updateButtons(),e._splitMode&&e._showDetail()})),o.on("deselect",(function(){e._selectedRequest=null,e._updateButtons(),e._detail.hide()})),this._resizeSensor.addListener(Lt()((function(){return e._updateDataGridHeight()}),15)),this._splitMediaQuery.on("match",(function(){e._detail.hide(),e._splitMode=!0})),this._splitMediaQuery.on("unmatch",(function(){e._detail.hide(),e._splitMode=!1})),this._detail.on("hide",(function(){e._splitMode&&e._$network.css("width","100%")})),Jt.domain("Network").enable();var i=Jt.domain("Network");i.on("requestWillBeSent",this._reqWillBeSent),i.on("responseReceivedExtraInfo",this._resReceivedExtraInfo),i.on("responseReceived",this._resReceived),i.on("loadingFinished",this._loadingFinished),i.on("loadingFailed",this._loadingFailed),h.on(h.SCALE,this._updateScale)}},{key:"destroy",value:function(){sn(t,"destroy",this,3)([]),this._resizeSensor.destroy(),re.remove(this._style),this._splitMediaQuery.removeAllListeners();var e=Jt.domain("Network");e.off("requestWillBeSent",this._reqWillBeSent),e.off("responseReceivedExtraInfo",this._resReceivedExtraInfo),e.off("responseReceived",this._resReceived),e.off("loadingFinished",this._loadingFinished),h.off(h.SCALE,this._updateScale)}},{key:"_initTpl",value:function(){var e=this._$el;e.html(ze('
\n
\n \n \n \n \n \n \n
\n
\n
\n
')),this._$network=e.find(ze(".network")),this._$detail=e.find(ze(".detail")),this._$requests=e.find(ze(".requests")),this._$control=e.find(ze(".control")),this._$filterText=e.find(ze(".filter-text"))}}])}(v),ln=n(2708),un=n.n(ln),dn=n(1167),hn=n.n(dn),fn=n(7181),pn=n.n(fn),vn=n(5784),gn=n(96),mn=n.n(gn),bn=n(896),yn=n.n(bn),An=n(438),wn=n.n(An),_n=n(6493),xn=n.n(_n),kn=n(6186),Cn=n.n(kn),Sn=n(5241),En=n.n(Sn),Tn=n(2208),Nn=n.n(Tn),On=n(5145),jn=n.n(On);function Mn(e){for(var t={},n=0,o=e.length;no?o:n,i=0;io?1:n(t=Fn(t))?1:e1&&void 0!==arguments[1]?arguments[1]:{}).noAttr,n=void 0!==t&&t;if(e.nodeType===Node.TEXT_NODE)return'(text)');if(e.nodeType===Node.COMMENT_NODE)return'\x3c!--\x3e');if(pn()(e))return'#shadow-root');var o=e.id,r=e.className,i=e.attributes,a=''.concat(e.tagName.toLowerCase(),"");if(""!==o&&(a+='#'.concat(o,"")),E()(r)){var s="";x()(r.split(/\s+/g),(function(e){""!==e.trim()&&(s+=".".concat(e))})),a+=''.concat(s,"")}return n||x()(i,(function(e){var t=e.name;"id"!==t&&"class"!==t&&"style"!==t&&(a+=' '.concat(t,'="').concat(e.value,'"'))})),a}var Gn=function(){return(0,r.A)((function e(t,n){var r=this;(0,o.A)(this,e),(0,c.A)(this,"hide",(function(){r._$container.hide(),r._disableObserver(),Jt.domain("Overlay").hideHighlight()})),(0,c.A)(this,"_highlight",(function(e){var t=r._curEl,n={showInfo:!1};e&&"all"!==e?"margin"===e?n.marginColor="rgba(246, 178, 107, .66)":"border"===e?n.borderColor="rgba(255, 229, 153, .66)":"padding"===e?n.paddingColor="rgba(147, 196, 125, .55)":"content"===e&&(n.contentColor="rgba(111, 168, 220, .66)"):P()(n,{showInfo:!0,contentColor:"rgba(111, 168, 220, .66)",paddingColor:"rgba(147, 196, 125, .55)",borderColor:"rgba(255, 229, 153, .66)",marginColor:"rgba(246, 178, 107, .66)"});var o=Jt.domain("DOM").getNodeId({node:t}).nodeId;Jt.domain("Overlay").highlightNode({nodeId:o,highlightConfig:n})})),this._$container=t,this._devtools=n,this._curEl=document.documentElement,this._initObserver(),this._initCfg(),this._initTpl(),this._bindEvent()}),[{key:"show",value:function(e){this._curEl=e,this._rmDefComputedStyle=!0,this._computedStyleSearchKeyword="",this._enableObserver(),this._render(),this._highlight()}},{key:"destroy",value:function(){this._disableObserver(),this.restoreEventTarget(),this._rmCfg()}},{key:"overrideEventTarget",value:function(){var e=Jn(),t=this._origAddEvent=e.addEventListener,n=this._origRmEvent=e.removeEventListener;e.addEventListener=function(e,n,o){!function(e,t,n){var o=arguments.length>3&&void 0!==arguments[3]&&arguments[3];if(!un()(e)||!lt()(n)||!xn()(o))return;var r=e.erudaEvents=e.erudaEvents||{};r[t]=r[t]||[],r[t].push({listener:n,listenerStr:n.toString(),useCapture:o})}(this,e,n,o),t.apply(this,arguments)},e.removeEventListener=function(e,t,o){!function(e,t,n){var o=arguments.length>3&&void 0!==arguments[3]&&arguments[3];if(!un()(e)||!lt()(n)||!xn()(o))return;var r=e.erudaEvents;if(!r||!r[t])return;for(var i=r[t],a=0,s=i.length;a\n \n \n \n \n
\n
\n
\n
\n
\n
');e.html(t),this._$elementName=e.find(ze(".element-name")),this._$attributes=e.find(ze(".attributes")),this._$styles=e.find(ze(".styles")),this._$listeners=e.find(ze(".listeners")),this._$computedStyle=e.find(ze(".computed-style"));var n=En()("div");this._$boxModel=m()(n),this._boxModel=new Rn.A(n)}},{key:"_toggleAllComputedStyle",value:function(){this._rmDefComputedStyle=!this._rmDefComputedStyle,this._render()}},{key:"_render",value:function(){var e=this._getData(this._curEl),t=this._$attributes,n=this._$elementName,o=this._$styles,r=this._$computedStyle,i=this._$listeners;n.html(e.name);var a="Empty";St()(e.attributes)||(a=_e()(e.attributes,(function(e){var t=e.name,n=e.value;return'\n ').concat(Tt()(t),'\n ').concat(n,"\n ")})).join("")),a='

Attributes

\n
\n \n \n ').concat(a," \n \n
\n
"),t.html(a);var s="";if(St()(e.styles))o.hide();else{var c=_e()(e.styles,(function(e){var t=e.selectorText,n=e.style;return n=_e()(n,(function(e,t){return'
').concat(Tt()(t),": ").concat(e,";
")})).join(""),'
\n
').concat(Tt()(t)," {
\n ").concat(n,"\n
}
\n
")})).join("");s='

Styles

\n
\n ').concat(c,"\n
"),o.html(s).show()}var l="";if(e.computedStyle){var u=ze('
\n \n
');e.rmDefComputedStyle&&(u=ze('
\n \n
')),l="

\n Computed Style\n ".concat(u,'\n
\n \n
\n ').concat(e.computedStyleSearchKeyword?'
').concat(Tt()(e.computedStyleSearchKeyword),"
"):"",'\n

\n
\n
\n \n \n ').concat(_e()(e.computedStyle,(function(e,t){return'\n \n \n ")})).join(""),"\n \n
').concat(Tt()(t),"").concat(e,"
\n
"),r.html(l).show(),this._boxModel.setOption("element",this._curEl),r.find(ze(".box-model")).append(this._$boxModel.get(0))}else r.text("").hide();var d="";e.listeners?(d=_e()(e.listeners,(function(e,t){return e=_e()(e,(function(e){var t=e.useCapture,n=e.listenerStr;return"
  • ").concat(Tt()(n),"
  • ")})).join(""),'
    \n
    ').concat(Tt()(t),'
    \n
      \n ').concat(e,"\n
    \n
    ")})).join(""),d='

    Event Listeners

    \n
    \n ').concat(d," \n
    "),i.html(d).show()):i.hide(),this._$container.show()}},{key:"_getData",value:function(e){var t={},n=new Dn(e),o=e.className,r=e.id,i=e.attributes,a=e.tagName;t.computedStyleSearchKeyword=this._computedStyleSearchKeyword,t.attributes=Hn(i),t.name=Ln({tagName:a,id:r,className:o,attributes:i});var s=e.erudaEvents;if(s&&0!==D()(s).length&&(t.listeners=s),Un(a))return t;var c=n.getComputedStyle(),l=n.getMatchedCSSRules();l.unshift(function(e){for(var t={selectorText:"element.style",style:{}},n=0,o=e.length;n
    $&').replace($n,(function(e,t){return'url("'.concat(Wn(t),'")')}))}var Qn=["script","style","meta","title","link","head"],Un=function(e){Qn.indexOf(e.toLowerCase())},Wn=function(e){return'').concat(e,"")};var Jn=function(){return Cn()(window,"EventTarget.prototype")||window.Node.prototype};function Kn(){try{var e=!Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){})))}catch(e){}return(Kn=function(){return!!e})()}function Vn(e,t,n,o){var r=(0,f.A)((0,a.A)(1&o?e.prototype:e),t,n);return 2&o&&"function"==typeof r?function(e){return r.apply(n,e)}:r}var Zn=function(e){function t(){var e,r,s,l;return(0,o.A)(this,t),r=this,s=t,s=(0,a.A)(s),e=(0,i.A)(r,Kn()?Reflect.construct(s,l||[],(0,a.A)(r).constructor):s.apply(r,l)),(0,c.A)(e,"_showDetail",(function(){e._isShow&&e._curNode&&(e._curNode.nodeType===Node.ELEMENT_NODE?e._detail.show(e._curNode):e._detail.show(e._curNode.parentNode||e._curNode.host))})),(0,c.A)(e,"_back",(function(){if(e._curNode!==e._htmlEl){for(var t=e._curParentQueue,n=t.shift();!Xn(n);)n=t.shift();e.set(n)}})),(0,c.A)(e,"_updateScale",(function(t){e._splitMediaQuery.setQuery("screen and (min-width: ".concat(680*t,"px)"))})),(0,c.A)(e,"_deleteNode",(function(){var t=e._curNode;t.parentNode&&t.parentNode.removeChild(t)})),(0,c.A)(e,"_copyNode",(function(){var t=e._curNode;t.nodeType===Node.ELEMENT_NODE?Ot()(t.outerHTML):Ot()(t.nodeValue),e._container.notify("Copied",{icon:"success"})})),(0,c.A)(e,"_toggleSelect",(function(){e._$el.find(ze(".select")).toggleClass(ze("active")),e._selectElement=!e._selectElement,e._selectElement?(Jt.domain("Overlay").setInspectMode({mode:"searchForNode",highlightConfig:{showInfo:!hn()(),showRulers:!1,showAccessibilityInfo:!hn()(),showExtensionLines:!1,contrastAlgorithm:"aa",contentColor:"rgba(111, 168, 220, .66)",paddingColor:"rgba(147, 196, 125, .55)",borderColor:"rgba(255, 229, 153, .66)",marginColor:"rgba(246, 178, 107, .66)"}}),e._container.hide()):(Jt.domain("Overlay").setInspectMode({mode:"none"}),Jt.domain("Overlay").hideHighlight())})),(0,c.A)(e,"_inspectNodeRequested",(function(t){var n=t.backendNodeId;e._container.show(),e._toggleSelect();try{var o=Jt.domain("DOM").getNode({nodeId:n}).node;e.select(o)}catch(e){}})),(0,c.A)(e,"_setNode",(function(t){if(t!==e._curNode){e._curNode=t,e._renderCrumbs();for(var n=[],o=t.parentNode;o;)n.push(o),o=o.parentNode;e._curParentQueue=n,e._splitMode&&e._showDetail(),e._updateButtons(),e._updateHistory()}})),e._style=re(n(9111)),e.name="elements",e._selectElement=!1,e._observeElement=!0,e._history=[],u().mixin(e),e}return(0,s.A)(t,e),(0,r.A)(t,[{key:"init",value:function(e,n){var o=this;Vn(t,"init",this,3)([e]),this._container=n,this._initTpl(),this._htmlEl=document.documentElement,this._detail=new Gn(this._$detail,n),this.config=this._detail.config,this._splitMediaQuery=new(en())("screen and (min-width: 680px)"),this._splitMode=this._splitMediaQuery.isMatch(),this._domViewer=new vn.A(this._$domViewer.get(0),{node:this._htmlEl,ignore:function(e){return Ie(e)||function(e){for(;e;){var t="";if(e.getAttribute&&(t=e.getAttribute("class")||""),N()(t,"__chobitsu-hide__"))return!0;e=e.parentNode}return!1}(e)}}),this._domViewer.expand(),this._bindEvent(),Jt.domain("Overlay").enable(),ue()((function(){return o._updateHistory()}))}},{key:"show",value:function(){Vn(t,"show",this,3)([]),this._isShow=!0,this._curNode?this._splitMode&&this._showDetail():this.select(document.body)}},{key:"hide",value:function(){Vn(t,"hide",this,3)([]),this._isShow=!1,Jt.domain("Overlay").hideHighlight()}},{key:"select",value:function(e){return this._domViewer.select(e),this._setNode(e),this.emit("change",e),this}},{key:"destroy",value:function(){Vn(t,"destroy",this,3)([]),h.off(h.SCALE,this._updateScale),re.remove(this._style),this._detail.destroy(),Jt.domain("Overlay").off("inspectNodeRequested",this._inspectNodeRequested),Jt.domain("Overlay").disable(),this._splitMediaQuery.removeAllListeners()}},{key:"_updateButtons",value:function(){var e=this._$control,t=e.find(ze(".show-detail")),n=e.find(ze(".copy-node")),o=e.find(ze(".delete-node")),r=ze("icon-disabled");t.addClass(r),n.addClass(r),o.addClass(r);var i=this._curNode;i&&!pn()(i)&&(i!==document.documentElement&&i!==document.body&&o.rmClass(r),n.rmClass(r),i.nodeType===Node.ELEMENT_NODE&&t.rmClass(r))}},{key:"_initTpl",value:function(){var e=this._$el;e.html(ze('
    \n
    \n \n \n \n \n
    \n
    \n
    \n
    \n
    \n
    \n
    ')),this._$detail=e.find(ze(".detail")),this._$domViewer=e.find(ze(".dom-viewer")),this._$control=e.find(ze(".control")),this._$crumbs=e.find(ze(".crumbs"))}},{key:"_renderCrumbs",value:function(){var e=function(e){var t=[],n=0;for(;e;)t.push({text:Ln(e,{noAttr:!0}),idx:n++}),pn()(e)&&(e=e.host),e=!e.parentElement&&pn()(e.parentNode)?e.parentNode:e.parentElement;return t.reverse()}(this._curNode),t="";St()(e)||(t=_e()(e,(function(e){var t=e.text,n=e.idx;return'
  • ').concat(t,"
  • ")})).join("")),this._$crumbs.html(t)}},{key:"_bindEvent",value:function(){var e=this,t=this;this._$el.on("click",ze(".crumb"),(function(){for(var e=Se()(m()(this).data("idx")),n=t._curNode;e--&&n.parentElement;)n=n.parentElement;Xn(n)&&t.select(n)})),this._$control.on("click",ze(".select"),this._toggleSelect).on("click",ze(".show-detail"),this._showDetail).on("click",ze(".copy-node"),this._copyNode).on("click",ze(".delete-node"),this._deleteNode),this._domViewer.on("select",this._setNode).on("deselect",this._back),Jt.domain("Overlay").on("inspectNodeRequested",this._inspectNodeRequested),this._splitMediaQuery.on("match",(function(){e._splitMode=!0,e._showDetail()})),this._splitMediaQuery.on("unmatch",(function(){e._splitMode=!1,e._detail.hide()})),h.on(h.SCALE,this._updateScale)}},{key:"_updateHistory",value:function(){var e=this._container.get("console");if(e){var t=this._history;t.unshift(this._curNode),t.length>5&&t.pop();for(var n=0;n<5;n++)e.setGlobal("$".concat(n),t[n])}}}])}(v),Xn=function(e){return un()(e)&&e.parentNode};var eo=n(3981),to=n.n(eo),no=n(4866),oo=n.n(no),ro=null,io=[{name:"Border All",fn:function(){if(ro)return re.remove(ro),void(ro=null);ro=re("* { outline: 2px dashed #707d8b; outline-offset: -3px; }",document.head)},desc:"Add color borders to all elements"},{name:"Refresh Page",fn:function(){var e=new(ge());e.setQuery("timestamp",to()()),window.location.replace(e.toString())},desc:"Add timestamp to url and refresh"},{name:"Search Text",fn:function(){nt.A.prompt("Enter the text").then((function(e){var t,n,o;e&&""!==Te()(e)&&(t=e,n=document.body,o=new RegExp(t,"ig"),ao(n,(function(e){var t=m()(e);if(t.hasClass("eruda-search-highlight-block"))return document.createTextNode(t.text())})),ao(n,(function(e){if(3===e.nodeType){var t=e.nodeValue;if((t=t.replace(o,(function(e){return''.concat(e,"")})))!==e.nodeValue){var n=m()(document.createElement("div"));return n.html(t),n.addClass("eruda-search-highlight-block"),n.get(0)}}})))}))},desc:"Highlight given text on page"},{name:"Edit Page",fn:function(){var e=document.body;e.contentEditable="true"!==e.contentEditable},desc:"Toggle body contentEditable"},{name:"Fit Screen",fn:function(){var e=document.body,t=document.documentElement,n=m()(e);if(n.data("scaled"))window.scrollTo(0,+n.data("scaled")),n.rmAttr("data-scaled"),n.css("transform","none");else{var o=Math.max(e.scrollHeight,e.offsetHeight,t.clientHeight,t.scrollHeight,t.offsetHeight),r=Math.max(document.documentElement.clientHeight,window.innerHeight||0),i=r/o;n.css("transform","scale(".concat(i,")")),n.data("scaled",window.scrollY),window.scrollTo(0,o/2-r/2)}},desc:"Scale down the whole page to fit screen"},{name:"Load Vue Plugin",fn:function(){so("vue")},desc:"Vue devtools"},{name:"Load Monitor Plugin",fn:function(){so("monitor")},desc:"Display page fps, memory and dom nodes"},{name:"Load Features Plugin",fn:function(){so("features")},desc:"Browser feature detections"},{name:"Load Timing Plugin",fn:function(){so("timing")},desc:"Show performance and resource timing"},{name:"Load Code Plugin",fn:function(){so("code")},desc:"Edit and run JavaScript"},{name:"Load Benchmark Plugin",fn:function(){so("benchmark")},desc:"Run JavaScript benchmarks"},{name:"Load Geolocation Plugin",fn:function(){so("geolocation")},desc:"Test geolocation"},{name:"Load Orientation Plugin",fn:function(){so("orientation")},desc:"Test orientation api"},{name:"Load Touches Plugin",fn:function(){so("touches")},desc:"Visualize screen touches"}];function ao(e,t){var n=e.childNodes;if(!Ie(e)){for(var o=0,r=n.length;o\n

    ').concat(Tt()(e.name),'\n
    \n \n
    \n

    \n
    \n ').concat(Tt()(e.desc),"\n
    \n ")})).join("");this._renderHtml(e)}},{key:"_renderHtml",value:function(e){e!==this._lastHtml&&(this._lastHtml=e,this._$el.html(e))}}])}(v),vo=n(4497),go=n.n(vo),mo=n(311),bo=n.n(mo),yo=n(769),Ao=n.n(yo),wo=n(4069),_o=n.n(wo),xo=function(){return(0,r.A)((function e(t,n,r,i){var a=this;(0,o.A)(this,e),(0,c.A)(this,"_updateGridHeight",(function(e){a._dataGrid.setOption({minHeight:60*e,maxHeight:223*e})})),this._type=i,this._$container=t,this._devtools=n,this._resources=r,this._selectedItem=null,this._storeData=[],this._initTpl(),this._dataGrid=new Kt.A(this._$dataGrid.get(0),{columns:[{id:"key",title:"Key",weight:30},{id:"value",title:"Value",weight:90}],minHeight:60,maxHeight:223}),this._bindEvent()}),[{key:"destroy",value:function(){h.off(h.SCALE,this._updateGridHeight)}},{key:"refresh",value:function(){var e=this._dataGrid;this._refreshStorage(),e.clear(),x()(this._storeData,(function(t){var n=t.key,o=t.val;e.append({key:n,value:o},{selectable:!0})}))}},{key:"_refreshStorage",value:function(){var e=this._resources,t=je(this._type,!1);if(t){var n=[];t=JSON.parse(JSON.stringify(t)),x()(t,(function(t,o){E()(t)&&(e.config.get("hideErudaSetting")&&(Ze()(o,"eruda")||"active-eruda"===o)||n.push({key:o,val:zt()(t,200)}))})),this._storeData=n}}},{key:"_updateButtons",value:function(){var e=this._$container,t=e.find(ze(".show-detail")),n=e.find(ze(".delete-storage")),o=e.find(ze(".copy-storage")),r=ze("btn-disabled");t.addClass(r),n.addClass(r),o.addClass(r),this._selectedItem&&(t.rmClass(r),n.rmClass(r),o.rmClass(r))}},{key:"_initTpl",value:function(){var e=this._$container,t=this._type;e.html(ze('

    \n '.concat("local"===t?"Local":"Session",' Storage\n
    \n \n
    \n
    \n \n
    \n
    \n \n
    \n
    \n \n
    \n
    \n \n
    \n
    \n \n
    \n
    \n

    \n
    '))),this._$dataGrid=e.find(ze(".data-grid")),this._$filterText=e.find(ze(".filter-text"))}},{key:"_getVal",value:function(e){return"local"===this._type?localStorage.getItem(e):sessionStorage.getItem(e)}},{key:"_bindEvent",value:function(){var e=this,t=this._type,n=this._devtools;function o(e,t){var o=n.get("sources");if(o)return o.set(e,t),n.showTool("sources"),!0}this._$container.on("click",ze(".refresh-storage"),(function(){n.notify("Refreshed",{icon:"success"}),e.refresh()})).on("click",ze(".clear-storage"),(function(){x()(e._storeData,(function(e){"local"===t?localStorage.removeItem(e.key):sessionStorage.removeItem(e.key)})),e.refresh()})).on("click",ze(".show-detail"),(function(){var t=e._selectedItem,n=e._getVal(t);try{o("object",JSON.parse(n))}catch(e){o("raw",n)}})).on("click",ze(".copy-storage"),(function(){var t=e._selectedItem;Ot()(e._getVal(t)),n.notify("Copied",{icon:"success"})})).on("click",ze(".filter"),(function(){nt.A.prompt("Filter").then((function(t){mt()(t)||(t=Te()(t),e._$filterText.text(t),e._dataGrid.setOption("filter",t))}))})).on("click",ze(".delete-storage"),(function(){var n=e._selectedItem;"local"===t?localStorage.removeItem(n):sessionStorage.removeItem(n),e.refresh()})),this._dataGrid.on("select",(function(t){e._selectedItem=t.data.key,e._updateButtons()})).on("deselect",(function(){e._selectedItem=null,e._updateButtons()})),h.on(h.SCALE,this._updateGridHeight)}}])}();function ko(e,t){e.rmClass(ze("ok")).rmClass(ze("danger")).rmClass(ze("warn")).addClass(ze(t))}function Co(e,t){if(0===t)return"";var n=0,o=0;switch(e){case"cookie":n=30,o=60;break;case"script":n=5,o=10;break;case"stylesheet":n=4,o=8;break;case"image":n=50,o=100}return t>=o?"danger":t>=n?"warn":"ok"}var So=function(){return(0,r.A)((function e(t,n){(0,o.A)(this,e),this._$container=t,this._devtools=n,this._selectedItem=null,this._initTpl(),this._dataGrid=new Kt.A(this._$dataGrid.get(0),{columns:[{id:"key",title:"Key",weight:30},{id:"value",title:"Value",weight:90}],minHeight:60,maxHeight:223}),this._bindEvent()}),[{key:"refresh",value:function(){var e=this._$container,t=this._dataGrid,n=Jt.domain("Network").getCookies().cookies,o=_e()(n,(function(e){return{key:e.name,val:e.value}}));t.clear(),x()(o,(function(e){var n=e.key,o=e.val;t.append({key:n,value:o},{selectable:!0})})),ko(e,Co("cookie",o.length))}},{key:"_initTpl",value:function(){var e=this._$container;e.html(ze('

    \n Cookie\n \n
    \n \n
    \n \n \n \n
    \n \n
    \n
    \n

    \n
    ')),this._$dataGrid=e.find(ze(".data-grid")),this._$filterText=e.find(ze(".filter-text"))}},{key:"_updateButtons",value:function(){var e=this._$container,t=e.find(ze(".show-detail")),n=e.find(ze(".delete-cookie")),o=e.find(ze(".copy-cookie")),r=ze("btn-disabled");t.addClass(r),n.addClass(r),o.addClass(r),this._selectedItem&&(t.rmClass(r),n.rmClass(r),o.rmClass(r))}},{key:"_getVal",value:function(e){for(var t=Jt.domain("Network").getCookies().cookies,n=0,o=t.length;n=e.length?{done:!0}:{done:!1,value:e[o++]}},e:function(e){throw e},f:r}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var i,a=!0,s=!1;return{s:function(){n=n.call(e)},n:function(){var e=n.next();return a=e.done,e},e:function(e){s=!0,i=e},f:function(){try{a||null==n.return||n.return()}finally{if(s)throw i}}}}function To(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,o=Array(t);nEmpty";St()(e)||(n=_e()(e,(function(e){return e=Tt()(e),'
  • ').concat(e,"
  • ")})).join(""));var o='

    \n Script\n
    \n \n
    \n

    \n
      \n ').concat(n,"\n
    "),r=this._$script;return ko(r,t),r.html(o),this}},{key:"refreshStylesheet",value:function(){var e=[];m()("link").each((function(){"stylesheet"===this.rel&&e.push(this.href)}));var t=Co("stylesheet",(e=wn()(e)).length),n="
  • Empty
  • ";St()(e)||(n=_e()(e,(function(e){return e=Tt()(e),'
  • ').concat(e,"
  • ")})).join(""));var o='

    \n Stylesheet\n
    \n \n
    \n

    \n
      \n ').concat(n,"\n
    "),r=this._$stylesheet;return ko(r,t),r.html(o),this}},{key:"refreshIframe",value:function(){var e=[];m()("iframe").each((function(){var t=m()(this).attr("src");t&&e.push(t)})),e=wn()(e);var t="
  • Empty
  • ";St()(e)||(t=_e()(e,(function(e){return e=Tt()(e),'
  • ').concat(e,"
  • ")})).join(""));var n='

    \n Iframe\n
    \n \n
    \n

    \n
      \n ').concat(t,"\n
    ");return this._$iframe.html(n),this}},{key:"refreshLocalStorage",value:function(){return this._localStorage.refresh(),this}},{key:"refreshSessionStorage",value:function(){return this._sessionStorage.refresh(),this}},{key:"refreshCookie",value:function(){return this._cookie.refresh(),this}},{key:"refreshImage",value:function(){var e=[],t=this._performance=window.webkitPerformance||window.performance;t&&t.getEntries?this._performance.getEntries().forEach((function(t){if("img"===t.initiatorType||Io(t.name)){if(N()(t.name,"exclude=true"))return;e.push(t.name)}})):m()("img").each((function(){var t=m()(this),n=t.attr("src");"true"!==t.data("exclude")&&e.push(n)}));(e=wn()(e)).sort();var n=Co("image",e.length),o="
  • Empty
  • ";St()(e)||(o=_e()(e,(function(e){return'
  • \n \n
  • ')})).join(""));var r='

    \n Image\n
    \n \n
    \n

    \n
      \n ').concat(o,"\n
    "),i=this._$image;return ko(i,n),i.html(r),this}},{key:"show",value:function(){return Oo(t,"show",this,3)([]),this._observeElement&&this._enableObserver(),this.refresh()}},{key:"hide",value:function(){return this._disableObserver(),Oo(t,"hide",this,3)([])}},{key:"_initTpl",value:function(){var e=this._$el;e.html(ze('
    \n
    \n \n
    \n
    \n
    \n
    ')),this._$localStorage=e.find(ze(".local-storage")),this._$sessionStorage=e.find(ze(".session-storage")),this._$cookie=e.find(ze(".cookie")),this._$script=e.find(ze(".script")),this._$stylesheet=e.find(ze(".stylesheet")),this._$iframe=e.find(ze(".iframe")),this._$image=e.find(ze(".image"))}},{key:"_bindEvent",value:function(){var e=this,t=this._$el,n=this._container;function o(e,t){var o=n.get("sources");if(o)return o.set(e,t),n.showTool("sources"),!0}function r(e){return function(t){if(n.get("sources")){t.preventDefault();var r=m()(this).attr("href");"iframe"!==e&&go()(location.href,r)?bo()({url:r,success:function(t){o(e,t)},dataType:"raw"}):o("iframe",r)}}}t.on("click",".eruda-refresh-script",(function(){n.notify("Refreshed",{icon:"success"}),e.refreshScript()})).on("click",".eruda-refresh-stylesheet",(function(){n.notify("Refreshed",{icon:"success"}),e.refreshStylesheet()})).on("click",".eruda-refresh-iframe",(function(){n.notify("Refreshed",{icon:"success"}),e.refreshIframe()})).on("click",".eruda-refresh-image",(function(){n.notify("Refreshed",{icon:"success"}),e.refreshImage()})).on("click",".eruda-img-link",(function(){o("img",m()(this).attr("src"))})).on("click",".eruda-css-link",r("css")).on("click",".eruda-js-link",r("js")).on("click",".eruda-iframe-link",r("iframe"))}},{key:"_rmCfg",value:function(){var e=this.config,t=this._container.get("settings");t&&t.remove(e,"hideErudaSetting").remove(e,"observeElement").remove("Resources")}},{key:"_initCfg",value:function(){var e=this,t=this.config=ce.createCfg("resources",{hideErudaSetting:!0,observeElement:!0});t.get("hideErudaSetting")&&(this._hideErudaSetting=!0),t.get("observeElement")||(this._observeElement=!1),t.on("change",(function(t,n){switch(t){case"hideErudaSetting":return void(e._hideErudaSetting=n);case"observeElement":return e._observeElement=n,n?e._enableObserver():e._disableObserver()}})),this._container.get("settings").text("Resources").switch(t,"hideErudaSetting","Hide Eruda Setting").switch(t,"observeElement","Auto Refresh Elements").separator()}},{key:"_initObserver",value:function(){var e=this;this._observer=new(Nn())((function(t){x()(t,(function(t){e._handleMutation(t)}))}))}},{key:"_handleMutation",value:function(e){var t=this;if(!Ie(e.target)){var n=function(e){var n=function(e){return e.tagName?e.tagName.toLowerCase():""}(e);switch(n){case"script":t.refreshScript();break;case"img":t.refreshImage();break;case"link":t.refreshStylesheet()}};if("attributes"===e.type)n(e.target);else if("childList"===e.type){n(e.target);var o,r=Ao()(e.addedNodes),i=Eo(r=_o()(r,Ao()(e.removedNodes)));try{for(i.s();!(o=i.n()).done;){n(o.value)}}catch(e){i.e(e)}finally{i.f()}}}}},{key:"_enableObserver",value:function(){this._observer.observe(document.documentElement,{attributes:!0,childList:!0,subtree:!0})}},{key:"_disableObserver",value:function(){this._observer.disconnect()}}])}(v);var Mo=/\.(jpeg|jpg|gif|png)$/,Io=function(e){return Mo.test(e)},zo=n(6620),Do=n.n(zo),Bo=Do()(),Fo=[{name:"Location",val:function(){return Tt()(location.href)}},{name:"User Agent",val:navigator.userAgent},{name:"Device",val:["",'"),""),""),"
    screen'.concat(screen.width," * ").concat(screen.height,"
    viewport".concat(window.innerWidth," * ").concat(window.innerHeight,"
    pixel ratio".concat(window.devicePixelRatio,"
    "].join("")},{name:"System",val:["",'"),""),"
    os'.concat(nn()(),"
    browser".concat(Bo.name+" "+Bo.version,"
    "].join("")},{name:"Sponsor this Project",val:function(){return""+_e()([{name:"Open Collective",link:"https://opencollective.com/eruda"},{name:"Ko-fi",link:"https://ko-fi.com/surunzi"},{name:"Wechat Pay",link:"https://surunzi.com/wechatpay.html"}],(function(e){return"")})).join(" ")+"
    ".concat(e.name,'').concat(e.link.replace("https://",""),"
    "}},{name:"About",val:'Eruda v3.4.3'}],Ro=n(1034),Lo=n.n(Ro);function Go(){try{var e=!Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){})))}catch(e){}return(Go=function(){return!!e})()}function Po(e,t,n,o){var r=(0,f.A)((0,a.A)(1&o?e.prototype:e),t,n);return 2&o&&"function"==typeof r?function(e){return r.apply(n,e)}:r}var Ho=function(e){function t(){var e,r,s,c;return(0,o.A)(this,t),r=this,s=t,s=(0,a.A)(s),(e=(0,i.A)(r,Go()?Reflect.construct(s,c||[],(0,a.A)(r).constructor):s.apply(r,c)))._style=re(n(4657)),e.name="info",e._infos=[],e}return(0,s.A)(t,e),(0,r.A)(t,[{key:"init",value:function(e,n){Po(t,"init",this,3)([e]),this._container=n,this._addDefInfo(),this._bindEvent()}},{key:"destroy",value:function(){Po(t,"destroy",this,3)([]),re.remove(this._style)}},{key:"add",value:function(e,t){var n=this._infos,o=!1;return x()(n,(function(n){e===n.name&&(n.val=t,o=!0)})),o||n.push({name:e,val:t}),this._render(),this}},{key:"get",value:function(e){var t,n=this._infos;return be()(e)?Lo()(n):(x()(n,(function(n){e===n.name&&(t=n.val)})),t)}},{key:"remove",value:function(e){for(var t=this._infos,n=t.length-1;n>=0;n--)t[n].name===e&&t.splice(n,1);return this._render(),this}},{key:"clear",value:function(){return this._infos=[],this._render(),this}},{key:"_addDefInfo",value:function(){var e=this;x()(Fo,(function(t){return e.add(t.name,t.val)}))}},{key:"_render",value:function(){var e=[];x()(this._infos,(function(t){var n=t.name,o=t.val;lt()(o)&&(o=o()),e.push({name:n,val:o})}));var t="
      ".concat(_e()(e,(function(e){return'
    • ').concat(Tt()(e.name),'

      ').concat(e.val,"
    • ")})).join(""),"
    ");this._renderHtml(t)}},{key:"_bindEvent",value:function(){var e=this._container;this._$el.on("click",ze(".copy"),(function(){var t=m()(this).parent().parent(),n=t.find(ze(".title")).text(),o=t.find(ze(".content")).text();Ot()("".concat(n,": ").concat(o)),e.notify("Copied",{icon:"success"})}))}},{key:"_renderHtml",value:function(e){e!==this._lastHtml&&(this._lastHtml=e,this._$el.html(e))}}])}(v),Yo=n(6911),$o=n(894),qo=n.n($o),Qo=n(4249),Uo=n.n(Qo),Wo=n(1714);function Jo(){try{var e=!Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){})))}catch(e){}return(Jo=function(){return!!e})()}function Ko(e,t,n,o){var r=(0,f.A)((0,a.A)(1&o?e.prototype:e),t,n);return 2&o&&"function"==typeof r?function(e){return r.apply(n,e)}:r}var Vo=function(e){function t(){var e,r,s,c;return(0,o.A)(this,t),r=this,s=t,s=(0,a.A)(s),(e=(0,i.A)(r,Jo()?Reflect.construct(s,c||[],(0,a.A)(r).constructor):s.apply(r,c)))._style=re(n(4831)),e.name="sources",e._showLineNum=!0,e}return(0,s.A)(t,e),(0,r.A)(t,[{key:"init",value:function(e,n){Ko(t,"init",this,3)([e]),this._container=n,this._bindEvent(),this._initCfg()}},{key:"destroy",value:function(){Ko(t,"destroy",this,3)([]),re.remove(this._style),this._rmCfg()}},{key:"set",value:function(e,t){if("img"===e){this._isFetchingData=!0;var n=new Image,o=this;return n.onload=function(){o._isFetchingData=!1,o._data={type:"img",val:{width:this.width,height:this.height,src:t}},o._render()},n.onerror=function(){o._isFetchingData=!1},void(n.src=t)}return this._data={type:e,val:t},this._render(),this}},{key:"show",value:function(){return Ko(t,"show",this,3)([]),this._data||this._isFetchingData||this._renderDef(),this}},{key:"_renderDef",value:function(){var e=this;if(this._html)return this._data={type:"html",val:this._html},this._render();this._isGettingHtml||(this._isGettingHtml=!0,bo()({url:location.href,success:function(t){return e._html=t},error:function(){return e._html="Sorry, unable to fetch source code:("},complete:function(){e._isGettingHtml=!1,e._renderDef()},dataType:"raw"}))}},{key:"_bindEvent",value:function(){var e=this;this._container.on("showTool",(function(t,n){t!==e.name&&n.name===e.name&&delete e._data}))}},{key:"_rmCfg",value:function(){var e=this.config,t=this._container.get("settings");t&&t.remove(e,"showLineNum").remove("Sources")}},{key:"_initCfg",value:function(){var e=this,t=this.config=ce.createCfg("sources",{showLineNum:!0});t.get("showLineNum")||(this._showLineNum=!1),t.on("change",(function(t,n){"showLineNum"!==t||(e._showLineNum=n)})),this._container.get("settings").text("Sources").switch(t,"showLineNum","Show Line Numbers").separator()}},{key:"_render",value:function(){switch(this._isInit=!0,this._data.type){case"html":case"js":case"css":return this._renderCode();case"img":return this._renderImg();case"object":return this._renderObj();case"raw":return this._renderRaw();case"iframe":return this._renderIframe()}}},{key:"_renderImg",value:function(){var e=this._data.val,t=e.width,n=e.height,o=e.src;this._renderHtml('
    \n
    ').concat(Tt()(o),'
    \n
    \n \n
    \n
    ').concat(Tt()(t)," × ").concat(Tt()(n),"
    \n
    "))}},{key:"_renderCode",value:function(){var e=this._data;this._renderHtml('
    '),!1);var t=e.val,n=e.val.length;n>er&&(t=zt()(t,er)),n'),!1);var e=this._data.val;try{E()(e)&&(e=JSON.parse(e))}catch(e){}new Yo.A(this._$el.find(".eruda-json").get(0),{unenumerable:!0,accessGetter:!0,prototype:!1}).set(e)}},{key:"_renderRaw",value:function(){var e=this._data;this._renderHtml('
    \n
    \n
    '));var t=e.val,n=this._$el.find(ze(".raw")).get(0);t.length>er&&(t=zt()(t,er)),new Wo.A(n,{text:t,wrapLongLines:!0,showLineNumbers:t.length'))}},{key:"_renderHtml",value:function(e){var t=this;(!(arguments.length>1&&void 0!==arguments[1])||arguments[1])&&e===this._lastHtml||(this._lastHtml=e,this._$el.html(e),setTimeout((function(){return t._$el.get(0).scrollTop=0}),0))}}])}(v),Zo=3e4,Xo=8e4,er=1e5,tr=n(9760),nr=n.n(tr),or=n(1505),rr=n.n(or),ir=n(5701),ar=n.n(ir),sr={init:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},t=e.container,n=e.tool,o=e.autoScale,r=void 0===o||o,i=e.useShadowDom,a=void 0===i||i,s=e.inline,c=void 0!==s&&s,l=e.defaults,u=void 0===l?{}:l;this._isInit||(this._isInit=!0,this._scale=1,this._initContainer(t,a),this._initStyle(),this._initDevTools(u,c),this._initEntryBtn(),this._initSettings(),this._initTools(n),this._registerListener(),r&&this._autoScale(),c&&(this._entryBtn.hide(),this._$el.addClass("eruda-inline"),this.show()))},_isInit:!1,version:"3.4.3",util:{isErudaEl:Ie,evalCss:re,isDarkTheme:function(e){return e||(e=this.getTheme()),K(e)},getTheme:function(){var e=re.getCurTheme(),t="Light";return x()(V,(function(n,o){ar()(n,e)&&(t=o)})),t}},chobitsu:Jt,Tool:v,Console:wt,Elements:Zn,Network:cn,Sources:Vo,Resources:jo,Info:Ho,Snippets:po,Settings:ce,get:function(e){if(this._checkInit()){if("entryBtn"===e)return this._entryBtn;var t=this._devTools;return e?t.get(e):t}},add:function(e){if(this._checkInit())return lt()(e)&&(e=e(this)),this._devTools.add(e),this},remove:function(e){return this._devTools.remove(e),this},show:function(e){if(this._checkInit()){var t=this._devTools;return e?t.showTool(e):t.show(),this}},hide:function(){if(this._checkInit())return this._devTools.hide(),this},destroy:function(){this._devTools.destroy(),delete this._devTools,this._entryBtn.destroy(),delete this._entryBtn,this._unregisterListener(),m()(this._container).remove(),re.clear(),this._isInit=!1,this._container=null,this._shadowRoot=null},scale:function(e){return Qe()(e)?(this._scale=e,h.emit(h.SCALE,e),this):this._scale},position:function(e){var t=this._entryBtn;return nr()(e)?(t.setPos(e),this):t.getPos()},_autoScale:function(){hn()()&&this.scale(1/rr()())},_registerListener:function(){var e=this;this._addListener=function(){return e.add.apply(e,arguments)},this._showListener=function(){return e.show.apply(e,arguments)},h.on(h.ADD,this._addListener),h.on(h.SHOW,this._showListener),h.on(h.SCALE,re.setScale)},_unregisterListener:function(){h.off(h.ADD,this._addListener),h.off(h.SHOW,this._showListener),h.off(h.SCALE,re.setScale)},_checkInit:function(){return this._isInit||$e.error('Please call "eruda.init()" first'),this._isInit},_initContainer:function(e,t){var o,r;e||(e=document.createElement("div"),document.documentElement.appendChild(e)),e.id="eruda",e.style.all="initial",this._container=e,t&&(e.attachShadow?o=e.attachShadow({mode:"open"}):e.createShadowRoot&&(o=e.createShadowRoot()),o&&(re.container=document.head,re(n(6793)+n(7853)+n(9907)+n(879)+n(1107)+n(5259)),r=document.createElement("div"),o.appendChild(r),this._shadowRoot=o)),this._shadowRoot||(r=document.createElement("div"),e.appendChild(r)),P()(r,{className:"eruda-container __chobitsu-hide__",contentEditable:!1}),"ios"===Do()().name&&r.setAttribute("ontouchstart",""),this._$el=m()(r)},_initDevTools:function(e,t){this._devTools=new it(this._$el,{defaults:e,inline:t})},_initStyle:function(){var e="eruda-style-container",t=this._$el;this._shadowRoot?(re.container=this._shadowRoot,re(":host { all: initial }")):(t.append('
    ')),re.container=t.find(".".concat(e)).get(0)),re(n(2693)+n(9907)+n(7853)+n(5259)+n(3277)+n(879)+n(4393)+n(7253)+n(1107)+n(187)+n(1277)+n(8687)+n(6793))},_initEntryBtn:function(){var e=this;this._entryBtn=new Pe(this._$el),this._entryBtn.on("click",(function(){return e._devTools.toggle()}))},_initSettings:function(){var e=this._devTools,t=new ce;e.add(t),this._entryBtn.initCfg(t),e.initCfg(t)},_initTools:function(){var e=this,t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:["console","elements","network","resources","sources","info","snippets"];t=Ao()(t);var n=this._devTools;t.forEach((function(t){var o=e[Ke()(t)];try{o&&n.add(new o)}catch(e){ue()((function(){$e.error("Something wrong when initializing tool ".concat(t,":"),e.message)}))}})),n.showTool(t[0]||"settings")}}},4236:function(e,t){t=function(e){return null===e},e.exports=t},4249:function(e,t,n){var o=n(9100),r=n(5651);t=function(e){var n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"js",s=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{};r(s,i),e=e.replace(//g,">"),n=a[n];var c=0,l=[];o(n,(function(n){n.language&&(e=e.replace(n.re,(function(e,o){return o?(l[c++]=t(o,n.language,s),e.replace(o,"___subtmpl"+(c-1)+"___")):e})))})),o(n,(function(t,n){a[t.language]||(e=e.replace(t.re,"___"+n+"___$1___end"+n+"___"))}));var u=[];return e=e.replace(/___(?!subtmpl)\w+?___/g,(function(e){var t="end"===e.substr(3,3),o=(t?e.substr(6):e.substr(3)).replace(/_/g,""),r=u.length>0?u[u.length-1]:null;return!t&&(null==r||o==r||null!=r&&n[r]&&null!=n[r].embed&&n[r].embed.indexOf(o)>-1)?(u.push(o),e):t&&o==r?(u.pop(),e):""})),o(n,(function(t,n){var o=s[t.style]?' style="'.concat(s[t.style],'"'):"";e=e.replace(new RegExp("___end"+n+"___","g"),"
    ").replace(new RegExp("___"+n+"___","g"),'"))})),o(n,(function(t){t.language&&(e=e.replace(/___subtmpl\d+___/g,(function(e){var t=parseInt(e.replace(/___subtmpl(\d+)___/,"$1"),10);return l[t]})))})),e};var i={comment:"color:#63a35c;",string:"color:#183691;",number:"color:#0086b3;",keyword:"color:#a71d5d;",operator:"color:#994500;"},a={js:{comment:{re:/(\/\/.*|\/\*([\s\S]*?)\*\/)/g,style:"comment"},string:{re:/(('.*?')|(".*?"))/g,style:"string"},numbers:{re:/(-?(\d+|\d+\.\d+|\.\d+))/g,style:"number"},keywords:{re:/(?:\b)(function|for|foreach|while|if|else|elseif|switch|break|as|return|this|class|self|default|var|const|let|false|true|null|undefined)(?:\b)/gi,style:"keyword"},operator:{re:/(\+|-|\/|\*|%|=|<|>|\||\?|\.)/g,style:"operator"}}};a.html={comment:{re:/(<!--([\s\S]*?)-->)/g,style:"comment"},tag:{re:/(<\/?\w(.|\n)*?\/?>)/g,style:"keyword",embed:["string"]},string:a.js.string,css:{re:/(?:<style.*?>)([\s\S]*)?(?:<\/style>)/gi,language:"css"},script:{re:/(?:<script.*?>)([\s\S]*?)(?:<\/script>)/gi,language:"js"}},a.css={comment:a.js.comment,string:a.js.string,numbers:{re:/((-?(\d+|\d+\.\d+|\.\d+)(%|px|em|pt|in)?)|#[0-9a-fA-F]{3}[0-9a-fA-F]{3})/g,style:"number"},keywords:{re:/(@\w+|:?:\w+|[a-z-]+:)/g,style:"keyword"}},e.exports=t},4279:function(e,t,n){(t=n(6314)(!1)).push([e.id,"#_settings{overflow-y:auto;-webkit-overflow-scrolling:touch}._safe-area #_settings{padding-bottom:calc(0px + env(safe-area-inset-bottom))}",""]),e.exports=t},4307:function(e,t,n){var o=n(2263),r=n(2767),i=n(9100),a=n(438),s=n(9405),c=n(3915),l=n(3957);t={on:function(e,t,n){l(t)&&(n=t,t={}),e=e.split(f),i(e,(function(e){if(e=h(e),t.element){var o=t.element,r=o._hotkeyListeners||{};o._hotkeyListeners=r,r[e]=r[e]||[];var i=function(t){e===d(t)&&n(t)};r[e].push({listener:i,origin:n}),o.addEventListener("keydown",i)}else u.on(e,n)}))},off:function(e,t,n){l(t)&&(n=t,t={}),e=e.split(f),i(e,(function(e){if(e=h(e),t.element){var o=t.element,r=o._hotkeyListeners;if(r&&r[e]){for(var i,a=r[e],s=0,c=a.length;s]*>/g.test(e))try{var t=s.default.parse(e);return f(t,(function(e){e.attrs&&e.attrs.class&&(e.attrs.class=n(e.attrs.class))})),s.default.stringify(t)}catch(t){return n(e)}return n(e)}};var p,v="ontouchstart"in a.default,g={start:"touchstart",move:"touchmove",end:"touchend"},m={start:"mousedown",move:"mousemove",end:"mouseup"};t.drag=function(e){return v?g[e]:m[e]},t.eventClient=function(e,t){var n="x"===e?"clientX":"clientY";return t[n]?t[n]:t.changedTouches?t.changedTouches[0][n]:0},t.eventPage=function(e,t){var n="x"===e?"pageX":"pageY";return t[n]?t[n]:t.changedTouches?t.changedTouches[0][n]:0},t.measuredScrollbarWidth=function(){if((0,c.default)(p))return p;if(!document)return 16;var e=document.createElement("div"),t=document.createElement("div");return e.setAttribute("style","display: block; width: 100px; height: 100px; overflow: scroll;"),t.setAttribute("style","height: 200px"),e.appendChild(t),document.body.appendChild(e),p=e.offsetWidth-e.clientWidth,document.body.removeChild(e),p},t.hasVerticalScrollbar=function(e){return e.scrollHeight>e.offsetHeight},t.executeAfterTransition=function(e,t){if((0,h.default)(e))return t();var n=function(o){o.target===e&&(e.removeEventListener("transitionend",n),t())};e.addEventListener("transitionend",n)},t.pxToNum=function(e){return(0,u.default)(e.replace("px",""))},t.getPlatform=function(){var e=(0,d.default)();return"os x"===e?"mac":e},t.resetCanvasSize=function(e){e.width=Math.round(e.offsetWidth*window.devicePixelRatio),e.height=Math.round(e.offsetHeight*window.devicePixelRatio)}},4801:function(e,t,n){var o=n(4950),r=n(2989),i=n(2561),a=n(1580),s=n(1168),c=n(3145),l=n(9100),u=n(2717),d=n(5427),h=n(466),f=n(8105),p=n(8796),v=n(2571),g=n(3981),m=n(7514),b=n(3249),y=n(9760),A=n(4460),w=n(6513),_=n(1009),x=n(2806),k=n(4151),C=n(896),S=n(5793);function E(e,n,o,r){var a=[];return l(n,(function(e){var n,s=Object.getOwnPropertyDescriptor(o,e),c=s&&s.get,l=s&&s.set;if(!r.accessGetter&&c)n="(...)";else try{if(n=o[e],b(r.ignore,n))return;p(n)&&n.catch((function(){}))}catch(e){n=e.message}a.push("".concat(T(e),":").concat(t(n,r))),c&&a.push("".concat(T("get "+i(e)),":").concat(t(s.get,r))),l&&a.push("".concat(T("set "+i(e)),":").concat(t(s.set,r)))})),'"'.concat(e,'":{')+a.join(",")+"}"}function T(e){return'"'.concat(O(e),'"')}function N(e){return'"'.concat(O(i(e)),'"')}function O(e){return o(e).replace(/\\'/g,"'").replace(/\t/g,"\\t")}t=function(e){var n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},o=n.self,l=n.startTime,u=void 0===l?g():l,p=n.timeout,y=void 0===p?0:p,A=n.depth,w=void 0===A?0:A,_=n.curDepth,x=void 0===_?1:_,k=n.visitor,C=void 0===k?new j:k,S=n.unenumerable,T=void 0!==S&&S,O=n.symbol,M=void 0!==O&&O,I=n.accessGetter,z=void 0!==I&&I,D=n.ignore,B=void 0===D?[]:D,F="",R={visitor:C,unenumerable:T,symbol:M,accessGetter:z,depth:w,curDepth:x+1,timeout:y,startTime:u,ignore:B},L=r(e,!1);if("String"===L)F=N(e);else if("Number"===L)F=i(e),a(F,"Infinity")&&(F='{"value":"'.concat(F,'","type":"Number"}'));else if("NaN"===L)F='{"value":"NaN","type":"Number"}';else if("Boolean"===L)F=e?"true":"false";else if("Null"===L)F="null";else if("Undefined"===L)F='{"type":"Undefined"}';else if("Symbol"===L){var G="Symbol";try{G=i(e)}catch(e){}F='{"value":'.concat(N(G),',"type":"Symbol"}')}else{if(y&&g()-u>y)return N("Timeout");if(w&&x>w)return N("{...}");F="{";var P,H=[],Y=C.get(e);if(Y?(P=Y.id,H.push('"reference":'.concat(P))):(P=C.set(e),H.push('"id":'.concat(P))),H.push('"type":"'.concat(L,'"')),a(L,"Function")?H.push('"value":'.concat(N(s(e)))):"RegExp"===L&&H.push('"value":'.concat(N(e))),!Y){var $=c(e);if($.length&&H.push(E("enumerable",$,o||e,R)),T){var q=h(m(e,{prototype:!1,unenumerable:!0}),$);q.length&&H.push(E("unenumerable",q,o||e,R))}if(M){var Q=v(m(e,{prototype:!1,symbol:!0}),(function(e){return"symbol"==typeof e}));Q.length&&H.push(E("symbol",Q,o||e,R))}var U=d(e);if(U&&!b(B,U)){var W='"proto":'.concat(t(U,f(R,{self:o||e})));H.push(W)}}F+=H.join(",")+"}"}return F};var j=u({initialize:function(){this.id=1,this.visited=[]},set:function(e){var t=this.visited,n=this.id,o={id:n,val:e};return t.push(o),this.id++,n},get:function(e){for(var t=this.visited,n=0,o=t.length;n>6*t)+n);t>0;){o+=h(128|63&e>>6*(t-1)),t--}return o}function p(e){for(;;){if(i>=a&&l){if(e)return v();throw new Error("Invalid byte index")}if(i===a)return!1;var t=r[i];if(i++,l){if(td){if(e)return i--,v();throw new Error("Invalid continuation byte")}if(u=128,d=191,s=s<<6|63&t,++c===l){var n=s;return s=0,l=0,c=0,n}}else{if(!(128&t))return t;if(192==(224&t))l=1,s=31&t;else if(224==(240&t))224===t&&(u=160),237===t&&(d=159),l=2,s=15&t;else{if(240!=(248&t)){if(e)return v();throw new Error("Invalid UTF-8 detected")}240===t&&(u=144),244===t&&(d=143),l=3,s=7&t}}}}function v(){var e=i-c-1;return i=e+1,s=0,l=0,c=0,u=128,d=191,r[e]}e.exports=t},4983:function(e,t){t=function(e){try{return JSON.parse(e),!0}catch(e){return!1}},e.exports=t},4992:function(e,t,n){var o=n(3974);t=function(e){return"[object ArrayBuffer]"===o(e)},e.exports=t},4994:function(e,t,n){var o=n(2510);t=o((function(e,t,n){return o((function(o){return e.apply(t,n.concat(o))}))})),e.exports=t},5004:function(e,t,n){var o=n(1909),r=n(621);t=function(e){function t(t){return e.indexOf(t)>-1}if(!e&&o&&(e=navigator.userAgent),e){if(e=e.toLowerCase(),t("windows phone"))return"windows phone";if(t("win"))return"windows";if(t("android"))return"android";if(t("ipad")||t("iphone")||t("ipod"))return"ios";if(t("mac"))return"os x";if(t("linux"))return"linux"}else if(r){var n=process,i=n.platform,a=n.env;if("win32"===i||"cygwin"===a.OSTYPE||"msys"===a.OSTYPE)return"windows";if("darwin"===i)return"os x";if("linux"===i)return"linux"}return"unknown"},e.exports=t},5021:function(e,t,n){var o=n(7744),r=n(1931),i=n(9464),a=n(8032),s=n(5651),c=n(9760),l=r("local");t=o.extend({initialize:function(e,t){this._name=e,t=t||{};var n=l.getItem(e);try{n=JSON.parse(n)}catch(e){n={}}c(n)||(n={}),t=s(n,t),this.callSuper(o,"initialize",[t])},save:function(e){if(i(e))return l.removeItem(this._name);l.setItem(this._name,a(e))}}),e.exports=t},5119:function(e,t,n){var o=n(9100);t=function(e){var t=[];return o(e,(function(e){t.push(e)})),t},e.exports=t},5132:function(e,t,n){var o=n(3974);t=function(e){return"[object Arguments]"===o(e)},e.exports=t},5145:function(e,t,n){var o=n(9756),r=n(5651),i=n(3145),a=n(6214),s=n(9760);t=function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};r(t,c);var n=t.deep,o=t.comparator,l=[],u=[];return function e(t){var r,c=l.indexOf(t);if(c>-1)return u[c];if(a(t)){r=[],l.push(t),u.push(r);for(var d=0,h=t.length;d=55296&&r<=56319&&n2?n-2:0),d=2;d=a?"":e.substr(i,a)},e.exports=t},5334:function(e,t,n){"use strict";var o=this&&this.__awaiter||function(e,t,n,o){return new(n||(n=Promise))((function(r,i){function a(e){try{c(o.next(e))}catch(e){i(e)}}function s(e){try{c(o.throw(e))}catch(e){i(e)}}function c(e){var t;e.done?r(e.value):(t=e.value,t instanceof n?t:new n((function(e){e(t)}))).then(a,s)}c((o=o.apply(e,t||[])).next())}))},r=this&&this.__generator||function(e,t){var n,o,r,i={label:0,sent:function(){if(1&r[0])throw r[1];return r[1]},trys:[],ops:[]},a=Object.create(("function"==typeof Iterator?Iterator:Object).prototype);return a.next=s(0),a.throw=s(1),a.return=s(2),"function"==typeof Symbol&&(a[Symbol.iterator]=function(){return this}),a;function s(s){return function(c){return function(s){if(n)throw new TypeError("Generator is already executing.");for(;a&&(a=0,s[0]&&(i=0)),i;)try{if(n=1,o&&(r=2&s[0]?o.return:s[0]?o.throw||((r=o.return)&&r.call(o),0):o.next)&&!(r=r.call(o,s[1])).done)return r;switch(o=0,r&&(s=[2&s[0],r.value]),s[0]){case 0:case 1:r=s;break;case 4:return i.label++,{value:s[1],done:!1};case 5:i.label++,o=s[1],s=[0];continue;case 7:s=i.ops.pop(),i.trys.pop();continue;default:if(!(r=i.trys,(r=r.length>0&&r[r.length-1])||6!==s[0]&&2!==s[0])){i=0;continue}if(3===s[0]&&(!r||s[1]>r[0]&&s[1]e.length)&&(t=e.length);for(var n=0,o=Array(t);n1?n-1:0),u=1;u0?i.apply(null,p):0;return s(a(f,(function(e){return" "===e[0]?e.slice(b):e})).join("\n"))};var c=/^(\s+)\S+/;e.exports=t},5784:function(e,t,n){"use strict";n.d(t,{A:function(){return _e}});var o=n(7528),r=n(4467),i=n(3029),a=n(2901),s=n(388),c=n(3954),l=n(5361),u=n(2263),d=n.n(u),h=n(3693),f=n.n(h),p=n(3915),v=n.n(p),g=n(9405),m=n.n(g),b=n(5169),y=n.n(b),A=n(9548),w=n.n(A),_=(n(6097),n(3249)),x=n.n(_),k=(n(6030),n(5004)),C=n.n(k);n(9410),n(8609);function S(e){var t="luna-".concat(e,"-");function n(e){return v()(m()(e).split(/\s+/),(function(e){return x()(e,t)?e:e.replace(/[\w-]+/,(function(e){return"".concat(t).concat(e)}))})).join(" ")}return function(e){if(/<[^>]*>/g.test(e))try{var t=w().parse(e);return E(t,(function(e){e.attrs&&e.attrs.class&&(e.attrs.class=n(e.attrs.class))})),w().stringify(t)}catch(t){return n(e)}return n(e)}}function E(e,t){for(var n=0,o=e.length;n2&&void 0!==arguments[2]?arguments[2]:{}).theme,l=void 0===a?"light":a;return(0,i.A)(this,t),o=function(e,t,n){return t=(0,c.A)(t),(0,s.A)(e,H()?Reflect.construct(t,n||[],(0,c.A)(e).constructor):t.apply(e,n))}(this,t),o.subComponents=[],o.theme="",o.onThemeChange=function(e){"auto"===o.options.theme&&o.setTheme(e)},o.compName=r,o.c=S(r),o.options={},o.container=e,o.$container=f()(e),o.$container.addClass(["luna-".concat(r),o.c("platform-".concat(N()))]),o.on("changeOption",(function(e,t){if("theme"===e&&t){var n=t;"auto"===t&&(n=L().get()),o.setTheme(n),j()(o.subComponents,(function(e){return e.setOption("theme",t)}))}})),L().on("change",o.onThemeChange),o.setOption("theme",l),o}return(0,l.A)(t,e),(0,a.A)(t,[{key:"destroy",value:function(){var e=this;this.destroySubComponents();var t=this.$container,n=t.attr("class");j()(n.split(/\s+/),(function(n){P()(n,"luna-".concat(e.compName))&&t.rmClass(n)})),t.html(""),this.emit("destroy"),this.removeAllListeners(),L().off("change",this.onThemeChange)}},{key:"setOption",value:function(e,t){var n=this,o=this.options,r={};"string"==typeof e?r[e]=t:r=e,j()(r,(function(e,t){var r=o[t];o[t]=e,e!==r&&n.emit("changeOption",t,e,r)}))}},{key:"getOption",value:function(e){return this.options[e]}},{key:"addSubComponent",value:function(e){e.setOption("theme",this.options.theme),this.subComponents.push(e)}},{key:"removeSubComponent",value:function(e){F()(this.subComponents,(function(t){return t===e}))}},{key:"destroySubComponents",value:function(){j()(this.subComponents,(function(e){return e.destroy()})),this.subComponents=[]}},{key:"initOptions",value:function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};D()(e,t),I()(this.options,e)}},{key:"find",value:function(e){return this.$container.find(this.c(e))}},{key:"setTheme",value:function(e){var t=this.c,n=this.$container;this.theme&&n.rmClass(t("theme-".concat(this.theme))),n.addClass(t("theme-".concat(e))),this.theme=e}}])}(d()),Q=n(5241),U=n.n(Q),W=n(2571),J=n.n(W),K=n(7181),V=n.n(K),Z=n(5773),X=n.n(Z),ee=n(769),te=n.n(ee),ne=n(2208),oe=n.n(ne),re=n(4249),ie=n.n(re),ae=n(15),se=n.n(ae),ce=n(3497),le=n.n(ce),ue=n(5902),de=n.n(ue),he=n(8098),fe=n.n(he),pe=n(4307),ve=n.n(pe),ge=n(96),me=n.n(ge);function be(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);t&&(o=o.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,o)}return n}function ye(e){for(var t=1;t1&&void 0!==arguments[1]?arguments[1]:{};return(0,i.A)(this,t),o=this,r=t,a=[e,{compName:"dom-viewer"},l],r=(0,c.A)(r),(n=(0,s.A)(o,Ae()?Reflect.construct(r,a||[],(0,c.A)(o).constructor):r.apply(o,a))).isExpanded=!1,n.childNodes=[],n.childNodeDomViewers=[],n.expand=function(){var e=arguments.length>0&&void 0!==arguments[0]&&arguments[0];n.isExpandable()&&(n.isExpanded||(n.isExpanded=!0,n.renderExpandTag(),n.renderChildNodes()),e&&j()(n.childNodeDomViewers,(function(e){e.expand(!0)})))},n.collapse=function(){var e=arguments.length>0&&void 0!==arguments[0]&&arguments[0];n.isExpandable()&&(n.isExpanded&&(n.isExpanded=!1,n.renderCollapseTag()),e&&j()(n.childNodeDomViewers,(function(e){e.collapse(!0)})))},n.toggle=function(){n.isExpanded?n.collapse():n.expand()},n.onKeyRight=function(){n.isExpanded?n.childNodeDomViewers[0].select():n.expand()},n.onKeyLeft=function(){var e;n.isExpanded?n.collapse():null===(e=n.options.parent)||void 0===e||e.select()},n.onKeyDown=function(){var e=n.options;if(n.isExpanded)n.childNodeDomViewers[0].select();else{var t=e.parent;if(t)if(e.isEndTag){if(!(t=t.getOption("parent")))return;var o=t,r=o.childNodes,i=o.childNodeDomViewers,a=o.endTagDomViewer,s=r.indexOf(e.node);r[s+1]?i[s+1].select():a&&a.select()}else{var c=t,l=c.childNodeDomViewers,u=c.endTagDomViewer,d=l.indexOf(n);l[d+1]?l[d+1].select():u&&u.select()}}},n.onKeyUp=function(){var e=n.options,t=e.parent;if(t){var o,r;if(e.isEndTag)o=le()(t.childNodeDomViewers);else{var i=t.childNodeDomViewers.indexOf(n);i<1?t.select():o=t.childNodeDomViewers[i-1]}if(o)if(o.isExpanded)null===(r=o.endTagDomViewer)||void 0===r||r.select();else o.select()}},n.initOptions(l,{node:document.documentElement,parent:null,isEndTag:!1,observe:!0,rootContainer:e,rootDomViewer:n,ignore:function(){return!1},ignoreAttr:function(){return!1},lowerCaseTagName:!0,hotkey:!0}),n.isShadowRoot=V()(n.options.node),n.initTpl(),n.bindEvent(),!n.options.isEndTag&&n.options.observe&&n.initObserver(),n}return(0,l.A)(t,e),(0,a.A)(t,[{key:"select",value:function(e){var t=this.c,n=this.options;if(!e||e&&n.node===e){if(this.$tag.hasClass(t("selected")))return;return f()(this.options.rootContainer).find(t(".selected")).rmClass(t("selected")).rmAttr("tabindex"),this.$tag.attr("tabindex","0").get(0).focus(),this.$tag.addClass(t("selected")),void n.rootDomViewer.emit("select",n.node)}if(e.nodeType===Node.ELEMENT_NODE)for(var o=e,r=e.parentElement;r;){if(r===n.node){this.expand(),this.childNodeDomViewers[this.childNodes.indexOf(o)].select(e);break}o=r,r=r.parentElement}}},{key:"attach",value:function(){this.container.appendChild(this.$tag.get(0)),this.$children&&this.container.appendChild(this.$children.get(0))}},{key:"isAttached",value:function(){return!!this.$tag.get(0).parentNode}},{key:"detach",value:function(){this.$tag.remove(),this.$children&&this.$children.remove()}},{key:"destroy",value:function(){var e=this.c;this.$tag.hasClass(e("selected"))&&this.options.rootDomViewer.emit("deselect"),this.detach(),this.observer&&this.observer.disconnect(),this.destroySubComponents(),this.options.rootDomViewer===this&&this.$container.rmClass("luna-dom-viewer").rmClass(e("platform-".concat(N()))).rmClass(e("theme-".concat(this.options.theme))),this.emit("destroy"),this.removeAllListeners()}},{key:"renderExpandTag",value:function(){var e=this.$tag,t=this.c,n=this.options.node;this.isShadowRoot||e.html(this.renderHtmlTag(ye(ye({},xe(n)),{},{hasTail:!1,hasToggleButton:!0}))),e.addClass(t("expanded")),this.$children.rmClass(t("hidden"))}},{key:"renderCollapseTag",value:function(){var e=this.$tag,t=this.c,n=this.options.node;this.$children.addClass(t("hidden")),this.isShadowRoot||this.$tag.html(this.renderHtmlTag(ye(ye({},xe(n)),{},{hasTail:!0,hasToggleButton:!0}))),e.rmClass(t("expanded"))}},{key:"initObserver",value:function(){var e=this;this.observer=new(oe())((function(t){j()(t,(function(t){e.handleMutation(t)}))})),this.observer.observe(this.options.node,{attributes:!0,childList:!0,characterData:!0})}},{key:"handleMutation",value:function(e){var t=this.$tag,n=this.c,o=this.options,r=o.node,i=o.ignore;if(x()(["attributes","childList"],e.type)){if("childList"===e.type){if(fe()(e.addedNodes,i)&&fe()(e.removedNodes,i))return;this.renderChildNodes()}this.isExpandable()?this.isExpanded?this.renderExpandTag():this.renderCollapseTag():(this.$children.addClass(n("hidden")),this.isExpanded=!1,this.isShadowRoot?t.html(this.renderShadowRoot(!1)):t.html(this.renderHtmlTag(ye(ye({},xe(r)),{},{hasTail:!1}))))}else"characterData"===e.type&&(r.nodeType===Node.TEXT_NODE?t.html(this.renderTextNode(r)):r.nodeType===Node.COMMENT_NODE&&t.html(this.renderHtmlComment(r.nodeValue)))}},{key:"bindEvent",value:function(){var e=this,t=this.c,n=this.$tag;if((this.options.node.nodeType===Node.ELEMENT_NODE||this.isShadowRoot)&&n.on("click",t(".toggle"),(function(t){t.stopPropagation(),e.toggle()})),T?n.on("click",(function(){return e.select()})):n.on("mousedown",(function(){return e.select()})),this.options.hotkey){var o={element:n.get(0)};ve().on("right",o,this.onKeyRight),ve().on("left",o,this.onKeyLeft),ve().on("down",o,this.onKeyDown),ve().on("up",o,this.onKeyUp)}}},{key:"isExpandable",value:function(){return!(this.options.node.nodeType!==Node.ELEMENT_NODE&&!this.isShadowRoot)&&this.getChildNodes().length>0}},{key:"getChildNodes",value:function(){var e=this.options,t=e.rootContainer,n=e.ignore,o=this.options.node,r=te()(o.childNodes);return r=J()(r,(function(e){if(e.nodeType===Node.TEXT_NODE||e.nodeType===Node.COMMENT_NODE){var o=e.nodeValue;if(""===m()(o))return!1}return e!==t&&!n(e)})),o.shadowRoot?r.unshift(o.shadowRoot):o.chobitsuShadowRoot&&r.unshift(o.chobitsuShadowRoot),r}},{key:"initTpl",value:function(){var e=this.container,t=this.c,n=this.options,o=n.node,r=n.isEndTag,i=n.lowerCaseTagName,a=f()(U()("li"));if(a.addClass(t("tree-item")),this.$tag=a,r){var s=o.tagName;i&&(s=me()(s)),a.html(t('</'.concat(s,'>')))}else if(o.nodeType===Node.ELEMENT_NODE){var c=this.isExpandable(),l=ye(ye({},xe(o)),{},{hasTail:c,hasToggleButton:c});a.html(this.renderHtmlTag(l))}else if(V()(o)){var u=this.isExpandable();a.html(this.renderShadowRoot(u))}else if(o.nodeType===Node.TEXT_NODE)a.html(this.renderTextNode(o));else{if(o.nodeType!==Node.COMMENT_NODE)return;var d=o.nodeValue;if(""===d.trim())return;a.html(this.renderHtmlComment(d))}if(e.appendChild(a.get(0)),o.nodeType===o.ELEMENT_NODE||this.isShadowRoot){var h=f()(U()("ul"));h.addClass([t("children"),t("hidden")]),e.appendChild(h.get(0)),this.$children=h}}},{key:"renderChildNodes",value:function(){var e=this,n=this.options.node,o=this.options,r=o.rootContainer,i=o.ignore,a=o.ignoreAttr,s=o.rootDomViewer,c=o.observe,l=o.lowerCaseTagName,u=this.$children.get(0),d=this.childNodes,h=this.childNodeDomViewers;j()(h,(function(t){t.detach(),e.removeSubComponent(t)})),this.endTagDomViewer&&this.endTagDomViewer.detach();var f=this.getChildNodes();this.childNodes=f;var p=[];this.childNodeDomViewers=p,j()(f,(function(n,o){var f,v=d.indexOf(n);(f=v>-1?h[v]:new t(u,{node:n,observe:c,parent:e,rootContainer:r,rootDomViewer:s,ignore:i,ignoreAttr:a,lowerCaseTagName:l})).attach(),p[o]=f,e.addSubComponent(f)})),j()(h,(function(e){e.isAttached()||e.destroy()})),n&&!this.isShadowRoot&&(this.endTagDomViewer?this.endTagDomViewer.attach():(this.endTagDomViewer=new t(u,{node:n,parent:this,isEndTag:!0,lowerCaseTagName:l,rootContainer:r,rootDomViewer:s,ignore:i}),this.addSubComponent(this.endTagDomViewer)))}},{key:"renderHtmlTag",value:function(e){var t=this,n=this.options.lowerCaseTagName;e.attributes=J()(e.attributes,(function(n){return!t.options.ignoreAttr(e.el,n.name,n.value)}));var r=v()(e.attributes,(function(e){var t=e.name,n=e.value,o=e.isLink;return'\n '.concat(de()(t),"").concat(n?'="').concat(de()(n),'"'):"","")})).join(""),i="",a=e.tagName;return n&&(a=me()(a)),e.hasTail?i="".concat(e.hasTail?"…":"",'</').concat(a,">"):this.isExpandable()||(i='</'.concat(a,">")),this.c(X()(Y||(Y=(0,o.A)(["\n ",'\n <',"",">",'\n '])),e.hasToggleButton?this.renderToggle():"",a,r,i))}},{key:"renderTextNode",value:function(e){var t=this.c,n=e.nodeValue,o=e.parentElement,r='',i='';if(o&&n.length<1e4){if("STYLE"===o.tagName)return t("".concat(r).concat(ie()(n,"css",we)).concat(i));if("SCRIPT"===o.tagName)return t("".concat(r).concat(ie()(n,"js",we)).concat(i))}return t('"'.concat(r).concat(de()(se()(n,1e4,{separator:" ",ellipsis:"…"}))).concat(i,'"'))}},{key:"renderHtmlComment",value:function(e){return this.c('<!-- '.concat(de()(e),' -->'))}},{key:"renderShadowRoot",value:function(e){var t=this.options.node;return this.c(X()($||($=(0,o.A)(["\n ",'\n #shadow-root (',')\n '])),e?this.renderToggle():"",t.mode))}},{key:"renderToggle",value:function(){return'
    '}}])}(q);function xe(e){var t={el:e,tagName:"",attributes:[]};t.tagName=e.tagName;var n=[];return j()(e.attributes,(function(t){var o=t.name,r=t.value;n.push({name:o,value:r,isLink:ke(e,o)})})),t.attributes=n,t}function ke(e,t){var n=e.tagName;return("SCRIPT"===n||"IMAGE"===n||"VIDEO"===n||"AUDIO"===n)&&"src"===t||"LINK"===n&&"href"===t}(function(e,t){try{e.exports=t,e.exports.default=t}catch(e){}})(e,_e)},5793:function(e,t,n){var o=n(6097),r=n(3957),i=Math.pow(2,53)-1;t=function(e){if(!e)return!1;var t=e.length;return o(t)&&t>=0&&t<=i&&!r(e)},e.exports=t},5820:function(e,t,n){var o,r=n(3981),i=n(5169),a=i.performance,s=i.process;if(a&&a.now)t=function(){return a.now()};else if(s&&s.hrtime){var c=function(){var e=s.hrtime();return 1e9*e[0]+e[1]};o=c()-1e9*s.uptime(),t=function(){return(c()-o)/1e6}}else o=r(),t=function(){return r()-o};e.exports=t},5865:function(e,t,n){var o=n(4534);t=function(e,t){return o(e,t,!0)},e.exports=t},5869:function(e,t,n){var o=n(365);t=function(e,t){var n=function(r){var i=n.cache,a=""+(t?t.apply(this,arguments):r);return o(i,a)||(i[a]=e.apply(this,arguments)),i[a]};return n.cache={},n},e.exports=t},5902:function(e,t,n){var o=n(3145),r=(t=function(e){return a.test(e)?e.replace(s,c):e}).map={"&":"&","<":"<",">":">",'"':""","'":"'","`":"`"},i="(?:"+o(r).join("|")+")",a=new RegExp(i),s=new RegExp(i,"g"),c=function(e){return r[e]};e.exports=t},5945:function(e,t,n){var o=n(3974);t=function(e){return"[object Set]"===o(e)},e.exports=t},5957:function(e,t,n){var o=n(1738),r=n(2990),i=n(4992),a=n(6214),s=n(3159),c=n(2989),l=n(96);(t=function(e,t){var n;if(t=l(t),o(e))n=new Uint8Array(r.decode(e));else if(i(e))e=e.slice(0),n=new Uint8Array(e);else if(a(e))n=new Uint8Array(e);else if("uint8array"===c(e))n=e.slice(0);else if(s(e)){n=new Uint8Array(e.length);for(var u=0;un?n:e},e.exports=t},6030:function(e,t,n){var o=n(6097),r=n(9760),i=n(3957),a=n(1738);t=function(e){if(o(e))return e;if(r(e)){var t=i(e.valueOf)?e.valueOf():e;e=r(t)?t+"":t}return a(e)?+e:0===e?e:+e},e.exports=t},6032:function(e,t,n){var o=n(2717),r=n(8105),i=n(9405),a=n(7257),s=n(9464),c=n(9100),l=n(6214),u=n(769),d=n(1909),h=n(9760),f=n(2561);t=o({className:"Url",initialize:function(e){!e&&d&&(e=window.location.href),r(this,t.parse(e||""))},setQuery:function(e,t){var n=this.query;return h(e)?c(e,(function(e,t){n[t]=f(e)})):n[e]=f(t),this},rmQuery:function(e){var t=this.query;return l(e)||(e=u(e)),c(e,(function(e){delete t[e]})),this},toString:function(){return t.stringify(this)}},{parse:function(e){var t={protocol:"",auth:"",hostname:"",hash:"",query:{},port:"",pathname:"",slashes:!1},n=i(e),o=!1,r=n.match(p);if(r&&(r=r[0],t.protocol=r.toLowerCase(),n=n.substr(r.length)),r&&(o="//"===n.substr(0,2))&&(n=n.slice(2),t.slashes=!0),o){for(var s=n,c=-1,l=0,u=g.length;l-1&&(s=n.slice(0,c),n=n.slice(c));var h=s.lastIndexOf("@");-1!==h&&(t.auth=decodeURIComponent(s.slice(0,h)),s=s.slice(h+1)),t.hostname=s;var f=s.match(v);f&&(":"!==(f=f[0])&&(t.port=f.substr(1)),t.hostname=s.substr(0,s.length-f.length))}var m=n.indexOf("#");-1!==m&&(t.hash=n.substr(m),n=n.slice(0,m));var b=n.indexOf("?");return-1!==b&&(t.query=a.parse(n.substr(b+1)),n=n.slice(0,b)),t.pathname=n||"/",t},stringify:function(e){var t=e.protocol+(e.slashes?"//":"")+(e.auth?encodeURIComponent(e.auth)+"@":"")+e.hostname+(e.port?":"+e.port:"")+e.pathname;return s(e.query)||(t+="?"+a.stringify(e.query)),e.hash&&(t+=e.hash),t}});var p=/^([a-z0-9.+-]+:)/i,v=/:[0-9]*$/,g=["/","?","#"];e.exports=t},6097:function(e,t,n){var o=n(3974);t=function(e){return"[object Number]"===o(e)},e.exports=t},6167:function(e,t,n){var o=n(5693);t=function(e,t,n,r){r=r||1,t=o(t,n);for(var i=e.length,a=r>0?0:i-1;a>=0&&a"),t))return void n.push(e);var i=[];(0,A.default)(e.attributes,(function(e){var t=e.name,n=e.value;return i.push(t,n)}));for(var a=0,s=i.length;a"),v.default.parse(i)[0].attrs));var i},t.setAttributeValue=function(e){var t=e.nodeId,n=e.name,o=e.value;(0,l.getNode)(t).setAttribute(n,o)},t.setInspectedNode=function(e){var t=(0,l.getNode)(e.nodeId);O.unshift(t),O.length>5&&O.pop();for(var n=0;n<5;n++)(0,k.setGlobal)("$".concat(n),O[n])},t.setNodeValue=function(e){var t=e.nodeId,n=e.value;(0,l.getNode)(t).nodeValue=n},t.setOuterHTML=function(e){var t=e.nodeId,n=e.outerHTML;(0,l.getNode)(t).outerHTML=n},t.getDOMNodeId=function(e){var t=e.node;return{nodeId:c.getOrCreateNodeId(t)}},t.getDOMNode=function(e){var t=e.nodeId;return{node:(0,l.getNode)(t)}},t.getTopLayerElements=function(){return{nodeIds:[]}},t.getNodesForSubtreeByStyle=function(){return{nodeIds:[]}};var s=a(n(8665)),c=i(n(9893)),l=n(9893),u=i(n(2484)),d=a(n(8757)),h=a(n(3693)),f=a(n(4236)),p=a(n(9464)),v=a(n(9548)),g=a(n(3915)),m=a(n(438)),b=a(n(3249)),y=a(n(96)),A=a(n(9100)),w=a(n(769)),_=a(n(8862)),x=a(n(4069)),k=n(2627),C=n(916);var S,E=!1;(S=Element.prototype.attachShadow)&&(Element.prototype.attachShadow=function(e){var t=S.apply(this,[e]);if(!c.isValidNode(this))return t;if(this.chobitsuShadowRoot=t,E){d.default.observe(t);var n=(0,l.getNodeId)(this);n&&s.default.trigger("DOM.shadowRootPushed",{hostId:n,root:c.wrap(t,{depth:1})})}return t});var T=new Map;function N(e){for(var t=[e],n=e.parentNode;n;){if(t.push(n),r=(0,l.getNodeId)(n))break;n=n.parentNode}for(;t.length;){var o=t.pop(),r=(0,l.getNodeId)(o);s.default.trigger("DOM.setChildNodes",{parentId:r,nodes:c.getChildNodes(o,1)})}return(0,l.getNodeId)(e)}var O=[];function j(e,t){for(var n=c.filterNodes(e.childNodes),o=0,r=n.length;o0&&r[r.length-1])||6!==s[0]&&2!==s[0])){i=0;continue}if(3===s[0]&&(!r||s[1]>r[0]&&s[1]-1)return r(e.substring(n+t.length,e.indexOf(".",n)))}e.exports=t},6631:function(e,t,n){var o=n(6030);t=function(e){return e?(e=o(e))-e%1:0===e?e:0},e.exports=t},6717:function(e,t,n){(t=n(6314)(!1)).push([e.id,"._container ._entry-btn{touch-action:none;width:40px;height:40px;display:flex;background:#000;opacity:.3;border-radius:10px;position:relative;z-index:1000;transition:opacity .3s;color:#fff;font-size:25px;align-items:center;justify-content:center}._container ._entry-btn._active,._container ._entry-btn:active{opacity:.8}",""]),e.exports=t},6741:function(e,t,n){var o=n(2717),r=n(6186),i=n(8105),a=n(2508),s=n(1738),c=n(3957);t=o({initialize:function(e,t){this._locale=e,this._langs=t},set:function(e,t){this._langs[e]?i(this._langs[e],t):this._langs[e]=t},t:function(e,t){var n="",o=this._langs[this._locale];return o?(n=r(o,e),t&&(s(n)?n=a(n,t):c(n)&&(n=n(t))),n||""):""},locale:function(e){this._locale=e}}),e.exports=t},6774:function(e,t){t=function(e,t,n){null==t&&(t=e,e=0);var o=Math.random();return n||e%1||t%1?Math.min(e+o*(t-e+parseFloat("1e-"+((o+"").length-1))),t):e+Math.floor(o*(t-e+1))},e.exports=t},6793:function(e,t,n){(t=n(6314)(!1)).push([e.id,"@font-face{font-family:eruda-icon;src:url('data:application/x-font-woff;charset=utf-8;base64,d09GRgABAAAAAA6UAAsAAAAAGvAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAABHU1VCAAABCAAAARoAAAHeLjoycE9TLzIAAAIkAAAAPwAAAFZWm1KoY21hcAAAAmQAAAFdAAADwhPu1O9nbHlmAAADxAAAB+wAAA9I7RPQpGhlYWQAAAuwAAAAMQAAADZ26MSyaGhlYQAAC+QAAAAdAAAAJAgEBC9obXR4AAAMBAAAAB0AAACwXAv//GxvY2EAAAwkAAAAOwAAAFpuVmoybWF4cAAADGAAAAAfAAAAIAE9AQ1uYW1lAAAMgAAAASkAAAIWm5e+CnBvc3QAAA2sAAAA5QAAAU4VMmUJeJxNkD1Ow0AQhb9NHGISCH9RiB0cErCNHRrqFFSIyqKiQHSpEFJERUnBCTgPZ+AEHIe34wDe1f69efPezOKAHldc07q5re4ZrFevL8QE1MPHm3e3fn5aEf6+FAvsDHHuTUoxd7zzwSdffLulq9wjLbaYau8TacZMONE554xzZsrtNfBEzFOhbSmOyTmga0ikvRR/37RSsSMyDukYPjWdgGOtsSK55Y/k0Bf/ksK0MrbFr70idsVZKNPnDcSay3umd2TISCvWTJSxI78lFQ/C+qbv/Zo9tNXDP55ZL7k0Q90u5F5XX0qrYx16btccCtXg/ULrKzGFuqY9rUTMhf3fkCNj+MxUnsM/frr5Qx+ZbH4vVQ0F5Q/ZQBvxAAB4nGNgZJJgnMDAysDA1Mt0hoGBoR9CM75mMGLkAIoysDIzYAUBaa4pDAcYdD+KsIC4MSxMDIxAGoQZALgnCOUAeJy1011SGlEQhuF3BFHxD5UUyr8gIJIsiiKJsSqJlrHKsJssKFeuxF6Bfj3dF96aqhzqoZnDzJyG8w2wCVTko1SheKLAx1/NFuV8hXo5X+WPjht6+fmfWHLDHQ+srfnykjMrvnPPoxXlzNtRlFc26HLBZblal1N9ntBnwIgx5/SYMaWt78+YM6TDgitduaEVq+q0xhbb7KifPQ441N2OOOaEJh9oaYka7xvdd57vQz1P+oPR+Bx6s2lbrc6H0Flc/cO9/sfY87fiOY8u8X0J/muX6VRW6UI+p4l8SX35mgZynUbyLY3lJukf0e6HnvxIM/mZpnKb2nKXvM/7dCa/0lwe0lAeU0d+p4Wsk3bBiuDptY2A10rw9Fo1eOJtM/iTYLWA162A1+2A152A13rwJ8R2g++AJaUU2w/KK3YQlFzsMCjDWCMozdhRUK6x46CEYydBWceagdYraihRngAAAHic7RdbbBxX9Z57Z2d2d2ZndryzM7ve9ax3NztjO/bann0lTuW16zoBJSWJ7Zg83NiUJCQ1Ik2ikKQJNC9FFQqVEG0RVLQoSpEKH2klqgpEIyWAUMRTNBJC/PUDhETgiwhQd8y5s1s7oqr624/srO6ce89zzjn3nHsJEPwxyn5GVEJKBTcCdc80pAiYhkjfNWL+NnhLdTKqfxVOqJlxFX6E84wb86/6X4+5GRLw0/vsOgkREoFGBFx62P/uFviBP78FWrC02d/r79vcpmMl+k2uBwwJxIILTrVeyXsmK8krRLb5YGqUaCb9ksYnMuBqMtnRcY6V1nidml6texaY9CxSRm3TtKNIjcxrUjhEWKD3OnuNJEgPKSG/I6nUpo06fxwXH8lmEoyDFQIVyrROs7254z990rj0u2PLez47WqG1yu69V7ZdfDxU9He4C6P+v+HN+vlnD9Uou0Zp+NnfvveT/XL0kbGFxT/u37tx7CTdeuGlKfiibcMr/gt9qfyu05e4+YEdb7A3iEVG0ArdEAvDIPHBqTbB7bgCDA0sdH0x3/nEHDT4YFJi9siz74iaOBkK3ZyRTRXwE+FGG15BeA0Pf14hqinP3AyFJnHhnVm5xzThmNSBNFjDdvwzw75GFJIlvWhZ1UHlYlI3zIputa3CSduiRF7P09e9on+jODpanPOKsJMDOPV2wU7/BqsVPcQ2ix41X/8ARKpbfhPVtHNgik1hXAhIlmQ1rIbbcCVIzN/7+65794KRTc13IBwJXVkhRACBkAEyhVyiBqJbRn81YRjKUDfRN9xHpoVBt0xJRZ+iS4ehZFg2utJrjCO2GrAUAizcj+c3pXpiXVQwThZmdNrbrx+hAjtjbhSF5FPyKSsqmGraWKYCbfl97vMLi79fXHje7XsAhBsoo0P35fyMPpCj+lM0FDptJexuYzl82upRufxlKgrTh/+fOwBXc+Jt9jZJBTnxUbH/yGT5j4jRT2pB9O1oO/oi3FyD2/ggU14LY/j5RuHTJIZf5LR/WVmbaB2CT6xdQa4KwJZIHPfyMFoWRNSmQZDLlJVpdRw8GwwVWEGlScOGijdOq2VKyfHDB7/d1/+d37zXeT/dXG42l7/Kh2a20pd0JpxsxTVNt8KWyuu/94Ujr+7uvFpvQXP5PCfEAU4l+6pZZ9Ix3eqGqmsGrvok28V+zi6TKEYyi/Udt0MNavkkJC1e+vQA1tGqil6EV93j/UBbY0AXm/2Vku+z53x/8MDT5879U9Nb4Cqq/yf/WEjReiECfS9+C2f/6umFS/77q3t7kp0nGu8DTrFTQrwG1KtsoHVXlnXL0qMKHTRpGbaJlt7aoVsSbO3aQFb5L7MTJElIwrBMvnWxQteCEl2QREn8Ci/Ef9i7u1IT6tX5Pb/ePV+rUXKEL3DMkUPzc6OeNzo3/6C8K2QdrzVlKAYyHhBcxGgUyoCRqXimJZXYwYO1y1tWxQWKLkyfunpqevrU5vJs4SQ02JUDw94qMlC6maORJpc9AR/Sm7C4cK7S4MoL/FNqFYy+Nw5VbpIoWaWXP0atf+fj1Lb36w12h6SxShIouuNQw+TCVDNsWvHqDStpNUoFnobUs6mhUvpmn+r2VxaeuXjmCc974vSjm44OxfytrXeH5iaKxYm5fXMThcLEHLwcGzq66dHTnObMxWcWKv2u2tfa1ipMzu7rEM5OFshqLfsFu4R9thszrVjAUoHFgH98DxRreb3CK74rMTh/bWmJTq9Pd0nCZOvsbfrYrVsTty9cOPc5Or2U6spq8rXbrbNAL9yeuHWLYuEnEiErK0JIAPIN8kNyl9wn/yUt7mioN6GGTi1jDQrypNPRxQ+8zREatnUsVtgbcDHAaZA0rc6TxOIWLPFVXLDbvYRT45CDSnBOqFhee4aTcWw8gapGnS+Z+EYrOuqh825jrY5WSVwPDSewh/OWqYueCJQFEjhELTdgcdEODjUCo5yge7lcAlJxRSgceyZyu5LFfqnaeldKlsyunnK6N6LEaUSqTSndgpZK7jC7NZaR7LGcGhXwgMNC+WFt0MxEomZcECQ9EY4JkgAQDilSNKnGuxXJ0u2hdG9YUZkiZcfWpaOWkUv0G6IaCseVVH81o0dEEClKGokassX0hKSk44PxBGOS4E8cmNk+OMSY5+2cXfz8zI4hrG4jI9tnFpW/hqKx7PCnH1O7wpFkqeANT4IUVhopPTUwnNJxzSlUzLASV+4YfUIkpoQFTYvoMUFkJgtJ/Z6VEIyymx4usdCW5CuDc9s+dZDm6GeiejTl1jN6VFKUdMHMlUIWzaQEOdyrKHIsL0VZJB0TE1rUlLvCo71yPKya3dW+ONBQRBajUdPuKoXFsBAOiYoUdx7JtSXlU3ZJNAW1O+4ktBCFqBjLJhMW97JgyonISE5kVIJQJJ6tO6nueCJj1TV/D6uMzu06tH/H44NlRr3RnbNPLu7cXh75sWOklURzi5ZI9dgqG6tuEAf0bkWX0/0j6S6+RjfaYiQsbkKHhuNdms6kUExWZNGSlJgzkjIGjPK61KjLxOvGc/1/27r9KOQe7omHe+LhnvjQnmArLTyHMYHiPbGbFLEL4Q1BxOsiHrfy2HIBz67BXQbPsVbB4TNDZP/wF4x63cAxUl/PRtbXI61f2QM2/iuZUqleKr3ABp1Mxnn/rjvpOJN0b9K2k/73+Xi/VHOcGl4qyf8AzjWNo3icY2BkYGAA4uhnXafj+W2+MnCzgASiOB/va4DR///+/8/CysIElOBgAJEMAHS2DWQAAAB4nGNgZGBgYQABFtb/f///ZWFlYGRABToAW+YEPQAAAHicY2BgYGAhiP//J6wGCbNCMcP/vwxUBgDl4QRhAAAAeJxjYAACBQYThiCGAoYtjAyMZowBjPuYuJjCmBYxvWNWYXZhzmFewfyIRYUliPUOexr7EmIhAF3rF0sAeJxjYGRgYNBhZGRgZwABJiDmAkIGhv9gPgMADcIBTAB4nGWQPW7CQBSEx2BIAlKCFCkps1UKIpmfkgNAT0GXwpi1MbK91npBossJcoQcIaeIcoIcKGPzaGAtP38zb97uygAG+IWHenm4bWq9WrihOnGb9CDsk5+FO+jjRbhLfyjcwxumwn084p07eP4dnQFK4Rbu8SHcpv8p7JO/hDt4wrdwl/6PcA8r/An38eoN08gUsSncUif7LLRnef6utK1SU6hJMD5bC11oGzq9Ueujqg7J1LlYxdbkas6uzjKjSmt2OnLB1rlyNhrF4geRyZEigkGBuKkOS2gk2CNDCHvVvdQrpi0q+rVWmCDA+Cq1YKpokiGVxobJNY6sFQ48bUrXMa34Ws7kpLnMat4kIyv+77q3oxPRD7BtpkrMMOITX+SD5g75Pz0RXqgAAAB4nG2MyW6DQBiD+RKYpKT7vqf7Gg55pNHwEyJNGDSMRHj70nKtD7Zly45G0YA0+h8LRoyJSVBMmLJDyoxd9tjngEOOOOaEU84454JLrrjmhlvuuGfOA4888cwLr7zxzgeffPHNgixKtfeuzawUYTZYv16VITXaS8hy11azwf7FibGi/dS4Te2laWLj6k7lYiVIIv3aK9nWusqng2TLsXR900m2VMXaBvFxbXWnvBjn84mXor8pk54kqKa/NmUvVkyIg3NW/VK2jFvtKzQeR0uGRSgIrFlRYsip2FDT0LGNoh/MCkh9AAAA') format('woff')}[class*=' _icon-'],[class^='_icon-']{display:inline-block;font-family:eruda-icon!important;font-size:16px;font-style:normal;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}._icon-arrow-left:before{content:'\\f101'}._icon-arrow-right:before{content:'\\f102'}._icon-caret-down:before{content:'\\f103'}._icon-caret-right:before{content:'\\f104'}._icon-clear:before{content:'\\f105'}._icon-compress:before{content:'\\f106'}._icon-copy:before{content:'\\f107'}._icon-delete:before{content:'\\f108'}._icon-error:before{content:'\\f109'}._icon-expand:before{content:'\\f10a'}._icon-eye:before{content:'\\f10b'}._icon-filter:before{content:'\\f10c'}._icon-play:before{content:'\\f10d'}._icon-record:before{content:'\\f10e'}._icon-refresh:before{content:'\\f10f'}._icon-reset:before{content:'\\f110'}._icon-search:before{content:'\\f111'}._icon-select:before{content:'\\f112'}._icon-tool:before{content:'\\f113'}._icon-warn:before{content:'\\f114'}",""]),e.exports=t},6833:function(e,t){var n=/([A-Z])/g,o=/[_.\- ]+/g,r=/(^-)|(-$)/g;t=function(e){return(e=e.replace(n,"-$1").toLowerCase().replace(o,"-").replace(r,"")).split("-")},e.exports=t},6866:function(e,t,n){var o=n(1738),r=n(9760),i=n(7604),a=n(8971),s=n(3249),c=n(6097),l=n(3612),u=n(6969),d=n(9100);t=function(e,t,n){if(e=l(e),a(n)&&o(t))return function(e,t){return e.style[u(t)]||getComputedStyle(e,"").getPropertyValue(t)}(e[0],t);var f=t;r(f)||((f={})[t]=n),function(e,t){d(e,(function(e){var n=";";d(t,(function(e,t){t=u.dash(t),n+=t+":"+function(e,t){var n=c(t)&&!s(h,i(e));return n?t+"px":t}(t,e)+";"})),e.style.cssText+=n}))}(e,f)};var h=["column-count","columns","font-weight","line-weight","opacity","z-index","zoom"];e.exports=t},6911:function(e,t,n){"use strict";n.d(t,{j:function(){return Pe},A:function(){return $e}});var o=n(2284),r=n(3029),i=n(2901),a=n(388),s=n(3954),c=n(991),l=n(5361),u=n(5427),d=n.n(u),h=n(6097),f=n.n(h),p=n(6493),v=n.n(p),g=n(96),m=n.n(g),b=n(9760),y=n.n(b),A=n(6214),w=n.n(A),_=n(9931),x=n.n(_),k=n(3145),C=n.n(k),S=n(9100),E=n.n(S),T=n(1168),N=n.n(T),O=n(8796),j=n.n(O),M=n(2989),I=n.n(M),z=n(3693),D=n.n(z),B=n(466),F=n.n(B),R=n(15),L=n.n(R),G=n(1738),P=n.n(G),H=n(7514),Y=n.n(H),$=n(2571),q=n.n($),Q=n(7140),U=n.n(Q),W=n(2561),J=n.n(W),K=n(9993),V=n.n(K),Z=n(1532),X=n.n(Z),ee=n(8105),te=n.n(ee),ne=function(){return(0,i.A)((function e(){(0,r.A)(this,e),this.id=0,this.visited=[]}),[{key:"set",value:function(e,t){var n=this.visited,o=this.id,r={id:o,val:e};return te()(r,t),n.push(r),this.id++,o}},{key:"get",value:function(e){for(var t=this.visited,n=0,o=t.length;n500&&(e=e.slice(0,500)+"..."),"ƒ "+re()(function(e){var t=e.match(le);return t?t[0]:e}(e).replace("function",""))}var le=/function(.*?)\((.*?)\)/;var ue=n(1009),de=n.n(ue),he=n(5630),fe=n.n(he),pe=n(6030),ve=n.n(pe),ge=n(1932),me=n.n(ge),be=n(2263),ye=n.n(be),Ae=n(3915),we=n.n(Ae),_e=n(5169),xe=n.n(_e),ke=n(9548),Ce=n.n(ke),Se=n(3249),Ee=n.n(Se),Te=n(5004),Ne=n.n(Te);n(9410),n(8609);function Oe(e){var t="luna-".concat(e,"-");function n(e){return we()(re()(e).split(/\s+/),(function(e){return Ee()(e,t)?e:e.replace(/[\w-]+/,(function(e){return"".concat(t).concat(e)}))})).join(" ")}return function(e){if(/<[^>]*>/g.test(e))try{var t=Ce().parse(e);return je(t,(function(e){e.attrs&&e.attrs.class&&(e.attrs.class=n(e.attrs.class))})),Ce().stringify(t)}catch(t){return n(e)}return n(e)}}function je(e,t){for(var n=0,o=e.length;n2&&void 0!==arguments[2]?arguments[2]:{}).theme,u=void 0===l?"light":l;return(0,r.A)(this,t),o=function(e,t,n){return t=(0,s.A)(t),(0,a.A)(e,Re()?Reflect.construct(t,n||[],(0,s.A)(e).constructor):t.apply(e,n))}(this,t),o.subComponents=[],o.theme="",o.onThemeChange=function(e){"auto"===o.options.theme&&o.setTheme(e)},o.compName=c,o.c=Oe(c),o.options={},o.container=e,o.$container=D()(e),o.$container.addClass(["luna-".concat(c),o.c("platform-".concat((i=Ne()(),"os x"===i?"mac":i)))]),o.on("changeOption",(function(e,t){if("theme"===e&&t){var n=t;"auto"===t&&(n=Fe().get()),o.setTheme(n),E()(o.subComponents,(function(e){return e.setOption("theme",t)}))}})),Fe().on("change",o.onThemeChange),o.setOption("theme",u),o}return(0,l.A)(t,e),(0,i.A)(t,[{key:"destroy",value:function(){var e=this;this.destroySubComponents();var t=this.$container,n=t.attr("class");E()(n.split(/\s+/),(function(n){de()(n,"luna-".concat(e.compName))&&t.rmClass(n)})),t.html(""),this.emit("destroy"),this.removeAllListeners(),Fe().off("change",this.onThemeChange)}},{key:"setOption",value:function(e,t){var n=this,o=this.options,r={};"string"==typeof e?r[e]=t:r=e,E()(r,(function(e,t){var r=o[t];o[t]=e,e!==r&&n.emit("changeOption",t,e,r)}))}},{key:"getOption",value:function(e){return this.options[e]}},{key:"addSubComponent",value:function(e){e.setOption("theme",this.options.theme),this.subComponents.push(e)}},{key:"removeSubComponent",value:function(e){De()(this.subComponents,(function(t){return t===e}))}},{key:"destroySubComponents",value:function(){E()(this.subComponents,(function(e){return e.destroy()})),this.subComponents=[]}},{key:"initOptions",value:function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};Ie()(e,t),te()(this.options,e)}},{key:"find",value:function(e){return this.$container.find(this.c(e))}},{key:"setTheme",value:function(e){var t=this.c,n=this.$container;this.theme&&n.rmClass(t("theme-".concat(this.theme))),n.addClass(t("theme-".concat(e))),this.theme=e}}])}(ye());function Ge(){try{var e=!Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){})))}catch(e){}return(Ge=function(){return!!e})()}var Pe=function(e){function t(e){var n,o,i,c;return(0,r.A)(this,t),o=this,i=t,c=[e,{compName:"object-viewer"}],i=(0,s.A)(i),(n=(0,a.A)(o,Ge()?Reflect.construct(i,c||[],(0,s.A)(o).constructor):i.apply(o,c))).onItemClick=function(e){var t=n,o=t.map,r=t.c,i=D()(e.curTarget),a=i.data("object-id"),s=i.find("span").eq(0);if(!i.data("first-level")&&(a&&(i.find("ul").html(n.objToHtml(o[a],!1)),i.rmAttr("data-object-id")),e.stopImmediatePropagation(),s.hasClass(r("expanded")))){var c=i.find("ul").eq(0);s.hasClass(r("collapsed"))?(s.rmClass(r("collapsed")),c.show()):(s.addClass(r("collapsed")),c.hide()),n.emit("change")}},n.bindEvent(),n}return(0,l.A)(t,e),(0,i.A)(t,[{key:"set",value:function(e){P()(e)&&(e=JSON.parse(e)),this.data={id:fe()("json"),enumerable:{0:e}},this.map={},He(this.map,this.data),this.render()}},{key:"destroy",value:function(){var e,n,o,r,i;(e=t,n="destroy",o=this,r=3,i=(0,c.A)((0,s.A)(1&r?e.prototype:e),n,o),2&r&&"function"==typeof i?function(e){return i.apply(o,e)}:i)([]),this.$container.off("click","li",this.onItemClick)}},{key:"objToHtml",value:function(e,t){var n=this,o="";return E()(["enumerable","unenumerable","symbol"],(function(r){if(e[r]){var i=C()(e[r]);X()(i);for(var a=0,s=i.length;a3&&void 0!==arguments[3]&&arguments[3],i=this.c,a=(0,o.A)(t);if(null===t)return"
  • ".concat(h(e),'null
  • ');if(f()(t)||v()(t))return"
  • ".concat(h(e),'').concat(se(t),"
  • ");if("RegExp"===t.type&&(a="regexp"),"Number"===t.type&&(a="number"),"Number"===t.type||"RegExp"===t.type)return"
  • ".concat(h(e),'').concat(se(t.value),"
  • ");if("Undefined"===t.type||"Symbol"===t.type)return"
  • ".concat(h(e),'').concat(m()(t.type),"
  • ");if("(...)"===t)return"
  • ".concat(h(e),'').concat(t,"
  • ");if(y()(t)){var s=t.id,c=t.reference,l=function(e){var t=e.type,n=e.value;if(!t)return;if("Function"===t)return ce(n);if("Array"===t&&e.unenumerable)return"Array(".concat(e.unenumerable.length,")");return e.type}(t)||x()(a),u=r?"":''),d="
  • ").concat(u).concat(h(e),'').concat(r?"":l,'
      ");return r&&(d+=this.objToHtml(this.map[s])),d+'
  • ')}function h(e){if(r)return"";if(y()(t)&&t.jsonSplitArr)return"";var o=i("key");return"unenumerable"===n||"symbol"===n?o=i("key-lighter"):"proto"===n&&(o=i("key-special")),'').concat(se(e),": ")}return P()(t)&&t.length>1e4&&(t=L()(t,50,{separator:" ",ellipsis:"…"})),"
  • ".concat(h(e),'"').concat(se(t),'"
  • ')}},{key:"render",value:function(){var e=this.map[this.data.id];this.$container.html(this.objToHtml(e,!0))}},{key:"bindEvent",value:function(){this.$container.on("click","li",this.onItemClick)}}])}(Le);function He(e,t){var n=t.id;if(n||0===n){if(t.type&&de()(t.type,"Array")&&t.enumerable){var o=function(e,t,n){var o=[],r={};E()(e.enumerable,(function(e,t){var n=ve()(t);me()(n)?r[t]=e:o[n]=e})),o.enumerable=r,o.type=n,o.id=t,e.unenumerable&&(o.unenumerable=e.unenumerable);e.symbol&&(o.symbol=e.symbol);e.proto&&(o.proto=e.proto);return o}(t,n,t.type);o.length>100&&(t=function(e){var t=0,n={};E()(U()(e,100),(function(e){var o={},r=t;o.type="["+r,o.enumerable={},E()(e,(function(e){o.enumerable[t]=e,t+=1}));var i=t-1;o.type+=(i-r>0?" … "+i:"")+"]",o.id=fe()("json"),o.jsonSplitArr=!0,n[t]=o}));var o={};o.enumerable=n,o.id=e.id,o.type=e.type,e.unenumerable&&(o.unenumerable=e.unenumerable);e.symbol&&(o.symbol=e.symbol);e.proto&&(o.proto=e.proto);return o}(o))}e[n]=t;var r=[];E()(["enumerable","unenumerable","symbol"],(function(e){if(t[e])for(var n in t[e])r.push(t[e][n])})),t.proto&&r.push(t.proto);for(var i=0,a=r.length;i1&&void 0!==arguments[1]?arguments[1]:{};return(0,r.A)(this,t),o=this,i=t,c=[e,{compName:"object-viewer"}],i=(0,s.A)(i),(n=(0,a.A)(o,Ye()?Reflect.construct(i,c||[],(0,s.A)(o).constructor):i.apply(o,c))).onItemClick=function(e){var t=n,o=t.map,r=t.c;if(!function(e){var t=window.getSelection();if(!t||"Range"!==t.type||""===t.toString())return!1;var n=t.anchorNode,o=t.focusNode;return t.containsNode(e,!0)||n&&e.contains(n)||o&&e.contains(o)}(e.curTarget)){var i=D()(e.curTarget),a=i.data("object-id"),s=i.find("span").eq(0);if(!i.data("first-level")&&(a&&(i.find("ul").html(n.objToHtml(o[a],!1)),i.rmAttr("data-object-id")),e.stopImmediatePropagation(),s.hasClass(r("expanded")))){var c=i.find("ul").eq(0);s.hasClass(r("collapsed"))?(s.rmClass(r("collapsed")),c.show()):(s.addClass(r("collapsed")),c.hide()),n.emit("change")}}},n.initOptions(l,{prototype:!0,unenumerable:!1,accessGetter:!1}),n.bindEvent(),n.options.object&&n.set(n.options.object),n}return(0,l.A)(t,e),(0,i.A)(t,[{key:"set",value:function(e){this.data=[e],this.visitor=new ne,this.map={},this.render()}},{key:"destroy",value:function(){var e,n,o,r,i;(e=t,n="destroy",o=this,r=3,i=(0,c.A)((0,s.A)(1&r?e.prototype:e),n,o),2&r&&"function"==typeof i?function(e){return i.apply(o,e)}:i)([]),this.$container.off("click","li",this.onItemClick)}},{key:"objToHtml",value:function(e,t){var n=this,r=this.visitor,i=e,a=!1,s=r.get(e);s&&s.self&&(i=s.self);var c="",l=["enumerable"],u=C()(e),h=[],f=[],p=[],v={};if(this.options.unenumerable&&!t&&(l.push("unenumerable"),l.push("symbol"),h=F()(Y()(e,{prototype:!1,unenumerable:!0}),u),f=q()(Y()(e,{prototype:!1,symbol:!0}),(function(e){return"symbol"===(0,o.A)(e)}))),w()(e)&&e.length>100){l.unshift("virtual"),a=!0;var g=0,m={};E()(U()(e,100),(function(e){var t=Object.create(null),n=g,o="["+n;E()(e,(function(e){t[g]=e,m[g]=!0,g++}));var r=g-1;v[o+=(r-n>0?" … "+r:"")+"]"]=t})),p=C()(v),u=q()(u,(function(e){return!m[e]}))}if(E()(l,(function(o){var r=[];r="symbol"===o?f:"unenumerable"===o?h:"virtual"===o?p:u,a||X()(r);for(var s=0,l=r.length;s4&&void 0!==arguments[4]&&arguments[4],a=this.visitor,s=this.c,c=(0,o.A)(n),l=I()(n,!1);if("virtual"===r&&(l=e),null===n)return"
  • ".concat(A(e),'null
  • ');if(f()(n)||v()(n))return"
  • ".concat(A(e),'').concat(se(n),"
  • ");if("RegExp"===l&&(c="regexp"),"Number"===l&&(c="number"),"Undefined"===l||"Symbol"===l)return"
  • ".concat(A(e),'').concat(m()(l),"
  • ");if("(...)"===n)return"
  • ".concat(A(e),'').concat(n,"
  • ");if(y()(n)){var u,d=a.get(n);if(d)u=d.id;else{var h={};"proto"===r&&(h.self=t),u=a.set(n,h),this.map[u]=n}var p="Object";p="regexp"===c?'').concat(se(n)):se(function(e,t){if(!t)return;if("Function"===t)return ce(N()(e));if("Array"===t)return"Array(".concat(e.length,")");return t}(n,l)||x()(c));var g=i?"":''),b="
  • ").concat(g).concat(A(e),'').concat(i?"":p,'
      ");return i&&(b+=this.objToHtml(n)),b+'
  • ')}function A(e){if(i)return"";if(y()(n)&&"virtual"===r)return"";var t=s("key");return"unenumerable"===r||"symbol"===r?t=s("key-lighter"):"proto"===r&&(t=s("key-special")),'').concat(se(e),": ")}return P()(n)&&n.length>1e4&&(n=L()(n,50,{separator:" ",ellipsis:"…"})),"
  • ".concat(A(e),'"').concat(se(n),'"
  • ')}},{key:"render",value:function(){this.$container.html(this.objToHtml(this.data,!0))}},{key:"bindEvent",value:function(){var e=this;this.$container.on("click","li",this.onItemClick),this.on("changeOption",(function(t,n){switch(t){case"object":e.set(n);break;case"unenumerable":case"prototype":case"accessGetter":e.render()}}))}}])}(Le);$e.Static=Pe,function(e,t){try{e.exports=t,e.exports.default=t}catch(e){}}(e,$e)},6948:function(e,t){var n=/<[^>]*>/g;t=function(e){return e.replace(n,"")},e.exports=t},6949:function(e,t,n){var o=n(365),r=n(6214);t=function(e,t){if(r(e))return e;if(t&&o(t,e))return[e];var n=[];return e.replace(i,(function(e,t,o,r){n.push(o?r.replace(a,"$1"):t||e)})),n};var i=/[^.[\]]+|\[(?:(-?\d+(?:\.\d+)?)|(["'])((?:(?!\2)[^\\]|\\.)*?)\2)\]|(?=(?:\.|\[\])(?:\.|\[\]|$))/g,a=/\\(\\)?/g;e.exports=t},6962:function(e,t,n){var o=n(1909),r=n(1023),i=!1;function a(e){i&&t.emit(e)}t={start:function(){i=!0},stop:function(){i=!1}},r.mixin(t),o?(window.addEventListener("error",(function(e){if(e.error)a(e.error);else if(e.message){var t=new Error(e.message);t.stack="Error: ".concat(e.message," \n at ").concat(e.filename,":").concat(e.lineno,":").concat(e.colno),a(t)}})),window.addEventListener("unhandledrejection",(function(e){a(e.reason)}))):(process.on("uncaughtException",a),process.on("unhandledRejection",a)),e.exports=t},6969:function(e,t,n){var o=n(5869),r=n(387),i=n(9931),a=n(365),s=n(7604);(t=o((function(e){if(e=e.replace(l,""),e=r(e),a(u,e))return e;for(var t=c.length;t--;){var n=c[t]+i(e);if(a(u,n))return n}return e}))).dash=o((function(e){var n=t(e);return(l.test(n)?"-":"")+s(n)}));var c=["O","ms","Moz","Webkit"],l=/^(O)|(ms)|(Moz)|(Webkit)|(-o-)|(-ms-)|(-moz-)|(-webkit-)/g,u=document.createElement("p").style;e.exports=t},7e3:function(e,t,n){var o=n(2263),r=new(n(3737))("(prefers-color-scheme: dark)");t={get:function(){return r.isMatch()?"dark":"light"}},o.mixin(t),r.on("match",(function(){return t.emit("change","dark")})),r.on("unmatch",(function(){return t.emit("change","light")})),e.exports=t},7005:function(e,t,n){var o=n(2717),r=n(5395);t=o({initialize:function(){this.clear()},clear:function(){this._items=[],this.size=0},push:function(e){return this._items.push(e),++this.size},pop:function(){if(this.size)return this.size--,this._items.pop()},peek:function(){return this._items[this.size-1]},forEach:function(e,t){t=arguments.length>1?t:this;for(var n=this._items,o=this.size-1,r=0;o>=0;o--,r++)e.call(t,n[o],r,this)},toArr:function(){return r(this._items)}}),e.exports=t},7030:function(e,t,n){var o=n(1738),r=n(2517),i=n(2561),a=n(6392);t=function(e,n,a,h){1===arguments.length&&o(e)&&!u.test(e)&&(n=e,e=void 0),e=e||new Date,r(e)||(e=new Date(e));var f=(n=i(t.masks[n]||n||t.masks.default)).slice(0,4);"UTC:"!==f&&"GMT:"!==f||(n=n.slice(4),a=!0,"GMT:"===f&&(h=!0));var p=a?"getUTC":"get",v=e[p+"Date"](),g=e[p+"Day"](),m=e[p+"Month"](),b=e[p+"FullYear"](),y=e[p+"Hours"](),A=e[p+"Minutes"](),w=e[p+"Seconds"](),_=e[p+"Milliseconds"](),x=a?0:e.getTimezoneOffset(),k={d:v,dd:s(v),ddd:t.i18n.dayNames[g],dddd:t.i18n.dayNames[g+7],m:m+1,mm:s(m+1),mmm:t.i18n.monthNames[m],mmmm:t.i18n.monthNames[m+12],yy:i(b).slice(2),yyyy:b,h:y%12||12,hh:s(y%12||12),H:y,HH:s(y),M:A,MM:s(A),s:w,ss:s(w),l:s(_,3),L:s(Math.round(_/10)),t:y<12?"a":"p",tt:y<12?"am":"pm",T:y<12?"A":"P",TT:y<12?"AM":"PM",Z:h?"GMT":a?"UTC":(i(e).match(l)||[""]).pop().replace(d,""),o:(x>0?"-":"+")+s(100*Math.floor(Math.abs(x)/60)+Math.abs(x)%60,4),S:["th","st","nd","rd"][v%10>3?0:(v%100-v%10!=10)*v%10]};return n.replace(c,(function(e){return e in k?k[e]:e.slice(1,e.length-1)}))};var s=function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:2;return a(i(e),t,"0")},c=/d{1,4}|m{1,4}|yy(?:yy)?|([HhMsTt])\1?|[LloSZWN]|"[^"]*"|'[^']*'/g,l=/\b(?:[PMCEA][SDP]T|(?:Pacific|Mountain|Central|Eastern|Atlantic) (?:Standard|Daylight|Prevailing) Time|(?:GMT|UTC)(?:[-+]\d{4})?)\b/g,u=/\d/,d=/[^-+\dA-Z]/g;t.masks={default:"ddd mmm dd yyyy HH:MM:ss",shortDate:"m/d/yy",mediumDate:"mmm d, yyyy",longDate:"mmmm d, yyyy",fullDate:"dddd, mmmm d, yyyy",shortTime:"h:MM TT",mediumTime:"h:MM:ss TT",longTime:"h:MM:ss TT Z",isoDate:"yyyy-mm-dd",isoTime:"HH:MM:ss",isoDateTime:"yyyy-mm-dd'T'HH:MM:sso",isoUtcDateTime:"UTC:yyyy-mm-dd'T'HH:MM:ss'Z'",expiresHeaderFormat:"ddd, dd mmm yyyy HH:MM:ss Z"},t.i18n={dayNames:["Sun","Mon","Tue","Wed","Thu","Fri","Sat","Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],monthNames:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec","January","February","March","April","May","June","July","August","September","October","November","December"]},e.exports=t},7119:function(e){e.exports='.luna-dom-highlighter{position:fixed;left:0;top:0;width:100%;height:100%;z-index:100000;pointer-events:none;font-size:13px}.luna-dom-highlighter-fill{position:absolute;top:0;right:0;bottom:0;left:0}.luna-dom-highlighter-platform-linux{font-family:Roboto,Ubuntu,Arial,sans-serif}.luna-dom-highlighter-platform-mac{color:#303942;font-family:\'.SFNSDisplay-Regular\',\'Helvetica Neue\',\'Lucida Grande\',sans-serif}.luna-dom-highlighter-platform-windows{font-family:\'Segoe UI\',Tahoma,sans-serif}.luna-dom-highlighter-px{color:gray}#luna-dom-highlighter-element-title{position:absolute;z-index:10}.luna-dom-highlighter-tooltip-content{position:absolute;-webkit-user-select:none;-moz-user-select:none;user-select:none;background-color:#fff;padding:5px 8px;border:1px solid #fff;border-radius:3px;box-sizing:border-box;min-width:100px;max-width:min(300px,100% - 4px);z-index:2;background-clip:padding-box;will-change:transform;text-rendering:optimizeLegibility;pointer-events:none;filter:drop-shadow(0 2px 4px rgba(0,0,0,.35))}.luna-dom-highlighter-tooltip-content .luna-dom-highlighter-tooltip-arrow{background:#fff;width:15px;height:8px;position:absolute}.luna-dom-highlighter-element-info-section{margin-top:12px;margin-bottom:6px}.luna-dom-highlighter-section-name{color:#333;font-weight:500;font-size:10px;text-transform:uppercase;letter-spacing:.05em;line-height:12px}.luna-dom-highlighter-element-info{display:flex;flex-direction:column}.luna-dom-highlighter-element-info-header{display:flex;align-items:center}.luna-dom-highlighter-element-info-body{display:flex;flex-direction:column;padding-top:2px;margin-top:2px}.luna-dom-highlighter-element-info-row{display:flex;line-height:19px}.luna-dom-highlighter-separator-container{display:flex;align-items:center;flex:auto;margin-left:7px}.luna-dom-highlighter-separator{border-top:1px solid #ddd;width:100%}.luna-dom-highlighter-element-info-name{flex-shrink:0;color:#666}.luna-dom-highlighter-element-info-gap{flex:auto}.luna-dom-highlighter-element-info-value-color{display:flex;color:#303942;margin-left:10px;align-items:baseline}.luna-dom-highlighter-a11y-icon{width:16px;height:16px;background-repeat:no-repeat;display:inline-block}.luna-dom-highlighter-element-info-value-contrast{display:flex;align-items:center;text-align:right;color:#303942;margin-left:10px}.luna-dom-highlighter-element-info-value-contrast .luna-dom-highlighter-a11y-icon{margin-left:8px}.luna-dom-highlighter-element-info-value-icon{display:flex;align-items:center}.luna-dom-highlighter-element-info-value-text{text-align:right;color:#303942;margin-left:10px;align-items:baseline;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.luna-dom-highlighter-color-swatch{display:flex;margin-right:2px;width:10px;height:10px;background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMCAIAAADZF8uwAAAAGUlEQVQYV2M4gwH+YwCGIasIUwhT25BVBADtzYNYrHvv4gAAAABJRU5ErkJggg==);line-height:10px}.luna-dom-highlighter-color-swatch-inner{flex:auto;border:1px solid #808002}.luna-dom-highlighter-element-layout-type{margin-right:10px;width:16px;height:16px}.luna-dom-highlighter-element-layout-type.luna-dom-highlighter-grid{background-image:url(\'data:image/svg+xml,\')}.luna-dom-highlighter-element-layout-type.luna-dom-highlighter-flex{background-image:url(\'data:image/svg+xml,\')}.luna-dom-highlighter-element-description{flex:1 1;font-weight:700;word-wrap:break-word;word-break:break-all}.luna-dom-highlighter-dimensions{color:#737373;text-align:right;margin-left:10px}.luna-dom-highlighter-material-node-width{margin-right:2px}.luna-dom-highlighter-material-node-height{margin-left:2px}.luna-dom-highlighter-material-tag-name{color:#881280}.luna-dom-highlighter-material-class-name,.luna-dom-highlighter-material-node-id{color:#1a1aa6}.luna-dom-highlighter-contrast-text{width:16px;height:16px;text-align:center;line-height:16px;margin-right:8px;border:1px solid #000;padding:0 1px}.luna-dom-highlighter-a11y-icon-not-ok{background-image:url(\'data:image/svg+xml,\')}.luna-dom-highlighter-a11y-icon-warning{background-image:url(\'data:image/svg+xml,\')}.luna-dom-highlighter-a11y-icon-ok{background-image:url(\'data:image/svg+xml,\')}@media (forced-colors:active){:root,body{background-color:transparent;forced-color-adjust:none}.luna-dom-highlighter-tooltip-content{border-color:Highlight;background-color:canvas;color:text;forced-color-adjust:none}.luna-dom-highlighter-tooltip-content::after{background-color:Highlight}.luna-dom-highlighter-color-swatch-inner,.luna-dom-highlighter-contrast-text,.luna-dom-highlighter-separator{border-color:Highlight}.luna-dom-highlighter-section-name{color:Highlight}.luna-dom-highlighter-dimensions,.luna-dom-highlighter-element-info-name,.luna-dom-highlighter-element-info-value-color,.luna-dom-highlighter-element-info-value-contrast,.luna-dom-highlighter-element-info-value-icon,.luna-dom-highlighter-element-info-value-text,.luna-dom-highlighter-material-class-name,.luna-dom-highlighter-material-node-id,.luna-dom-highlighter-material-tag-name{color:canvastext}}\n\n/*# sourceMappingURL=luna-dom-highlighter.css.map*/'},7140:function(e,t){t=function(e,t){var n=[];t=t||1;for(var o=0,r=Math.ceil(e.length/t);o0?n.join("="):null;o=decodeURIComponent(o),r=decodeURIComponent(r),i(t[o])?t[o]=r:a(t[o])?t[o].push(r):t[o]=[t[o],r]})),t},stringify:function(e,n){return l(s(e,(function(e,o){return u(e)&&c(e)?"":a(e)?t.stringify(e,o):(n?encodeURIComponent(n):encodeURIComponent(o))+"="+encodeURIComponent(e)})),(function(e){return e.length>0})).join("&")}};var d=/^(\?|#|&)/g;e.exports=t},7308:function(e,t,n){var o=n(9100),r=n(1738),i=n(8971),a=n(3249),s=n(6214),c=n(9760),l=n(769);(t=function(e,t){if(i(e))return n={},d((function(e,t){n[e]=t})),n;var n;if(r(e)&&i(t)||s(e))return function(e){if(!r(e)){var t={};return d((function(n,o){a(e,n)&&(t[n]=o)})),t}var n=h(e);if(n)return n.getAttribute("content")}(e);var l=e;c(l)||((l={})[e]=t),function(e){o(e,(function(e,t){var n=h(t);if(n)return n.setAttribute("content",e);(n=u.createElement("meta")).setAttribute("name",t),n.setAttribute("content",e),u.head.appendChild(n)}))}(l)}).remove=function(e){e=l(e),o(e,(function(e){var t=h(e);t&&u.head.removeChild(t)}))};var u=document;function d(e){var t=u.querySelectorAll("meta");o(t,(function(t){var n=t.getAttribute("name"),o=t.getAttribute("content");n&&o&&e(n,o)}))}function h(e){return u.querySelector('meta[name="'+e+'"]')}e.exports=t},7375:function(e,t){t=function(e,t,n){return Array.prototype.indexOf.call(e,t,n)},e.exports=t},7468:function(e,t,n){var o=n(3145);t=function(e,t){var n=o(t),r=n.length;if(null==e)return!r;e=Object(e);for(var i=0;i1&&void 0!==arguments[1]?arguments[1]:{},n=t.prototype,c=void 0===n||n,l=t.unenumerable,u=void 0!==l&&l,d=t.symbol,h=void 0!==d&&d,f=[];if((u||h)&&a){var p=o;u&&a&&(p=a);do{f=f.concat(p(e)),h&&s&&(f=f.concat(s(e)))}while(c&&(e=r(e))&&e!==Object.prototype);f=i(f)}else if(c)for(var v in e)f.push(v);else f=o(e);return f},e.exports=t},7528:function(e,t,n){"use strict";function o(e,t){return t||(t=e.slice(0)),Object.freeze(Object.defineProperties(e,{raw:{value:Object.freeze(t)}}))}n.d(t,{A:function(){return o}})},7542:function(e,t){t=function(e,t){var n;return function(){return--e>0&&(n=t.apply(this,arguments)),e<=1&&(t=null),n}},e.exports=t},7571:function(e,t,n){var o=n(2263),r=n(6186),i=window.screen;t={get:function(){if(i){var e=r(i,"orientation.type");if(e)return e.split("-").shift()}return window.innerWidth>window.innerHeight?"landscape":"portrait"}},o.mixin(t),window.addEventListener("orientationchange",(function(){setTimeout((function(){t.emit("change",t.get())}),200)}),!1),e.exports=t},7604:function(e,t,n){var o=n(6833);t=function(e){return o(e).join("-")},e.exports=t},7622:function(e,t,n){var o=n(2263),r=n(2192),i=n(769),a=n(8971),s=n(8420),c=n(1738),l=n(6097);t=o.extend({initialize:function(e,n){this.name=e,this.setLevel(a(n)?t.level.DEBUG:n),this.callSuper(o,"initialize",arguments)},setLevel:function(e){return c(e)?((e=t.level[e.toUpperCase()])&&(this._level=e),this):(l(e)&&(this._level=e),this)},getLevel:function(){return this._level},formatter:function(e,t){return t},trace:function(){return this._log("trace",arguments)},debug:function(){return this._log("debug",arguments)},info:function(){return this._log("info",arguments)},warn:function(){return this._log("warn",arguments)},error:function(){return this._log("error",arguments)},_log:function(e,n){return 0===(n=i(n)).length?this:(this.emit("all",e,s(n)),t.level[e.toUpperCase()]*{vertical-align:top}.luna-console-log-item .luna-console-log-content .luna-console-null,.luna-console-log-item .luna-console-log-content .luna-console-undefined{color:#5e5e5e}.luna-console-log-item .luna-console-log-content .luna-console-number{color:#1c00cf}.luna-console-log-item .luna-console-log-content .luna-console-boolean{color:#0d22aa}.luna-console-log-item .luna-console-log-content .luna-console-regexp,.luna-console-log-item .luna-console-log-content .luna-console-symbol{color:#881391}.luna-console-log-item .luna-console-data-grid,.luna-console-log-item .luna-console-dom-viewer{white-space:initial}.luna-console-log-item.luna-console-error{z-index:50;background:#fff0f0;color:red;border-top:1px solid #ffd6d6;border-bottom:1px solid #ffd6d6}.luna-console-log-item.luna-console-error .luna-console-stack{padding-left:1.2em;white-space:nowrap}.luna-console-log-item.luna-console-error .luna-console-count{background:red}.luna-console-log-item.luna-console-debug{z-index:20}.luna-console-log-item.luna-console-input{border-bottom-color:transparent}.luna-console-log-item.luna-console-warn{z-index:40;color:#5c5c00;background:#fffbe5;border-top:1px solid #fff5c2;border-bottom:1px solid #fff5c2}.luna-console-log-item.luna-console-warn .luna-console-count{background:#e8a400}.luna-console-log-item.luna-console-info{z-index:30}.luna-console-log-item.luna-console-group,.luna-console-log-item.luna-console-groupCollapsed{font-weight:700}.luna-console-preview{display:inline-block}.luna-console-preview .luna-console-preview-container{display:flex;align-items:center}.luna-console-preview .luna-console-json{overflow-x:auto;-webkit-overflow-scrolling:touch;padding-left:12px}.luna-console-preview .luna-console-preview-icon-container{display:block}.luna-console-preview .luna-console-preview-icon-container .luna-console-icon{position:relative;font-size:12px}.luna-console-preview .luna-console-preview-icon-container .luna-console-icon-caret-down{top:2px}.luna-console-preview .luna-console-preview-icon-container .luna-console-icon-caret-right{top:1px}.luna-console-preview .luna-console-preview-content-container{word-break:break-all}.luna-console-preview .luna-console-descriptor,.luna-console-preview .luna-console-object-preview{font-style:italic}.luna-console-preview .luna-console-key{color:#881391}.luna-console-preview .luna-console-number{color:#1c00cf}.luna-console-preview .luna-console-null{color:#5e5e5e}.luna-console-preview .luna-console-string{color:#c41a16}.luna-console-preview .luna-console-boolean{color:#0d22aa}.luna-console-preview .luna-console-special{color:#5e5e5e}.luna-console-theme-dark{color-scheme:dark}.luna-console-theme-dark .luna-console-log-container.luna-console-selected .luna-console-log-item{background:#29323d}.luna-console-theme-dark .luna-console-log-container.luna-console-selected .luna-console-log-item:not(.luna-console-error):not(.luna-console-warn){border-color:#4173b4}.luna-console-theme-dark .luna-console-log-item{color:#a5a5a5;border-bottom-color:#3d3d3d}.luna-console-theme-dark .luna-console-log-item .luna-console-code .luna-console-keyword{color:#e36eec}.luna-console-theme-dark .luna-console-log-item .luna-console-code .luna-console-number{color:#9980ff}.luna-console-theme-dark .luna-console-log-item .luna-console-code .luna-console-operator{color:#7f7f7f}.luna-console-theme-dark .luna-console-log-item .luna-console-code .luna-console-comment{color:#747474}.luna-console-theme-dark .luna-console-log-item .luna-console-code .luna-console-string{color:#f29766}.luna-console-theme-dark .luna-console-log-item.luna-console-error{background:#290000;color:#ff8080;border-top-color:#5c0000;border-bottom-color:#5c0000}.luna-console-theme-dark .luna-console-log-item.luna-console-error .luna-console-count{background:#ff8080}.luna-console-theme-dark .luna-console-log-item.luna-console-warn{color:#ffcb6b;background:#332a00;border-top-color:#650;border-bottom-color:#650}.luna-console-theme-dark .luna-console-log-item .luna-console-count{background:#42597f;color:#949494}.luna-console-theme-dark .luna-console-log-item .luna-console-log-content .luna-console-null,.luna-console-theme-dark .luna-console-log-item .luna-console-log-content .luna-console-undefined{color:#7f7f7f}.luna-console-theme-dark .luna-console-log-item .luna-console-log-content .luna-console-boolean,.luna-console-theme-dark .luna-console-log-item .luna-console-log-content .luna-console-number{color:#9980ff}.luna-console-theme-dark .luna-console-log-item .luna-console-log-content .luna-console-regexp,.luna-console-theme-dark .luna-console-log-item .luna-console-log-content .luna-console-symbol{color:#e36eec}.luna-console-theme-dark .luna-console-icon-container .luna-console-icon-caret-down,.luna-console-theme-dark .luna-console-icon-container .luna-console-icon-caret-right{color:#9aa0a6}.luna-console-theme-dark .luna-console-header{border-bottom-color:#3d3d3d}.luna-console-theme-dark .luna-console-nesting-level{border-right-color:#3d3d3d}.luna-console-theme-dark .luna-console-nesting-level::before{border-bottom-color:#3d3d3d}.luna-console-theme-dark .luna-console-preview .luna-console-key{color:#e36eec}.luna-console-theme-dark .luna-console-preview .luna-console-number{color:#9980ff}.luna-console-theme-dark .luna-console-preview .luna-console-null{color:#7f7f7f}.luna-console-theme-dark .luna-console-preview .luna-console-string{color:#f29766}.luna-console-theme-dark .luna-console-preview .luna-console-boolean{color:#9980ff}.luna-console-theme-dark .luna-console-preview .luna-console-special{color:#7f7f7f}",""]),e.exports=t},8009:function(e,t,n){var o=n(6513);t=function(e,t){e.prototype=o(t.prototype)},e.exports=t},8032:function(e,t,n){var o=n(2989),r=n(9931),i=n(2561),a=n(8971),s=n(3957),c=n(1976);t=function(e,t){return JSON.stringify(e,(n=[],l=[],function(e,t){if(n.length>0){var u=n.indexOf(this);u>-1?(n.splice(u+1),l.splice(u,1/0,e)):(n.push(this),l.push(e));var d=n.indexOf(t);d>-1&&(t=n[0]===t?"[Circular ~]":"[Circular ~."+l.slice(0,d).join(".")+"]")}else n.push(t);return c(t)||s(t)?t="["+r(o(t))+" "+i(t)+"]":a(t)&&(t=null),t}),t);var n,l},e.exports=t},8046:function(e,t,n){var o=n(7731);t=function(){var e=o(16);return e[6]=15&e[6]|64,e[8]=63&e[8]|128,r[e[0]]+r[e[1]]+r[e[2]]+r[e[3]]+"-"+r[e[4]]+r[e[5]]+"-"+r[e[6]]+r[e[7]]+"-"+r[e[8]]+r[e[9]]+"-"+r[e[10]]+r[e[11]]+r[e[12]]+r[e[13]]+r[e[14]]+r[e[15]]};for(var r=[],i=0;i<256;i++)r[i]=(i+256).toString(16).substr(1);e.exports=t},8052:function(e,t,n){"use strict";n.d(t,{A:function(){return K}});var o=n(7528),r=n(3029),i=n(2901),a=n(388),s=n(3954),c=n(5361),l=n(2263),u=n.n(l),d=n(3693),h=n.n(d),f=n(3915),p=n.n(f),v=n(9405),g=n.n(v),m=n(5169),b=n.n(m),y=n(9548),A=n.n(y),w=n(6097),_=n.n(w),x=n(3249),k=n.n(x),C=(n(6030),n(5004)),S=n.n(C);n(9410),n(8609);function E(e){var t="luna-".concat(e,"-");function n(e){return p()(g()(e).split(/\s+/),(function(e){return k()(e,t)?e:e.replace(/[\w-]+/,(function(e){return"".concat(t).concat(e)}))})).join(" ")}return function(e){if(/<[^>]*>/g.test(e))try{var t=A().parse(e);return T(t,(function(e){e.attrs&&e.attrs.class&&(e.attrs.class=n(e.attrs.class))})),A().stringify(t)}catch(t){return n(e)}return n(e)}}function T(e,t){for(var n=0,o=e.length;n2&&void 0!==arguments[2]?arguments[2]:{}).theme,u=void 0===l?"light":l;return(0,r.A)(this,t),o=function(e,t,n){return t=(0,s.A)(t),(0,a.A)(e,H()?Reflect.construct(t,n||[],(0,s.A)(e).constructor):t.apply(e,n))}(this,t),o.subComponents=[],o.theme="",o.onThemeChange=function(e){"auto"===o.options.theme&&o.setTheme(e)},o.compName=c,o.c=E(c),o.options={},o.container=e,o.$container=h()(e),o.$container.addClass(["luna-".concat(c),o.c("platform-".concat((i=S()(),"os x"===i?"mac":i)))]),o.on("changeOption",(function(e,t){if("theme"===e&&t){var n=t;"auto"===t&&(n=L().get()),o.setTheme(n),j()(o.subComponents,(function(e){return e.setOption("theme",t)}))}})),L().on("change",o.onThemeChange),o.setOption("theme",u),o}return(0,c.A)(t,e),(0,i.A)(t,[{key:"destroy",value:function(){var e=this;this.destroySubComponents();var t=this.$container,n=t.attr("class");j()(n.split(/\s+/),(function(n){P()(n,"luna-".concat(e.compName))&&t.rmClass(n)})),t.html(""),this.emit("destroy"),this.removeAllListeners(),L().off("change",this.onThemeChange)}},{key:"setOption",value:function(e,t){var n=this,o=this.options,r={};"string"==typeof e?r[e]=t:r=e,j()(r,(function(e,t){var r=o[t];o[t]=e,e!==r&&n.emit("changeOption",t,e,r)}))}},{key:"getOption",value:function(e){return this.options[e]}},{key:"addSubComponent",value:function(e){e.setOption("theme",this.options.theme),this.subComponents.push(e)}},{key:"removeSubComponent",value:function(e){F()(this.subComponents,(function(t){return t===e}))}},{key:"destroySubComponents",value:function(){j()(this.subComponents,(function(e){return e.destroy()})),this.subComponents=[]}},{key:"initOptions",value:function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};D()(e,t),I()(this.options,e)}},{key:"find",value:function(e){return this.$container.find(this.c(e))}},{key:"setTheme",value:function(e){var t=this.c,n=this.$container;this.theme&&n.rmClass(t("theme-".concat(this.theme))),n.addClass(t("theme-".concat(e))),this.theme=e}}])}(u()),q=n(5773),Q=n.n(q),U=n(5902),W=n.n(U);function J(){try{var e=!Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){})))}catch(e){}return(J=function(){return!!e})()}e=n.hmd(e);var K=function(e){function t(e){var n,o,i,c,l=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};return(0,r.A)(this,t),o=this,i=t,c=[e,{compName:"tab"},l],i=(0,s.A)(i),(n=(0,a.A)(o,J()?Reflect.construct(i,c||[],(0,s.A)(o).constructor):i.apply(o,c))).initOptions(l,{height:30}),n.initTpl(),n.$tabs=n.find(".tabs"),n.tabs=n.$tabs.get(0),n.$slider=n.find(".slider"),n.bindEvent(),n.updateHeight(),n}return(0,c.A)(t,e),(0,i.A)(t,[{key:"length",get:function(){return this.$tabs.find(this.c(".item")).length}},{key:"insert",value:function(e,t){var n=this.c,o=this.$tabs,r=this.options.height-1,i=o.find(n(".item")),a=i.length,s='
    ').concat(W()(t.title),"").concat(t.closeable?'
    '):"","
    ");e>a-1?o.append(s):i.eq(e).before(s),this.updateSlider()}},{key:"append",value:function(e){this.insert(this.length,e)}},{key:"remove",value:function(e){var t=this.c;if(1!==this.length){var n=this;this.$tabs.find(t(".item")).each((function(o){var r=h()(this);if(r.data("id")===e){if(r.remove(),r.hasClass(t("selected")))if(n.length>0){var i=o===n.length?o-1:o,a=n.$tabs.find(t(".item")).eq(i).data("id");n.select(a)}else n.emit("deselect");r.data("closeable")&&n.emit("close",e)}})),this.updateSlider()}}},{key:"select",value:function(e){var t=this.c,n=this;this.$tabs.find(t(".item")).each((function(){var o=h()(this);o.data("id")===e?(o.addClass(t("selected")),n.updateSlider(),n.scrollToSelected(),n.emit("select",e)):o.rmClass(t("selected"))}))}},{key:"deselect",value:function(){var e=this.c;this.$tabs.find(e(".item")).each((function(){h()(this).rmClass(e("selected"))})),this.emit("deselect"),this.updateSlider()}},{key:"scrollToSelected",value:function(){var e,t=this.$tabs,n=this.tabs,o=this.c,r=t.find(o(".selected")).get(0),i=r.offsetLeft,a=r.offsetWidth,s=n.offsetWidth,c=n.scrollLeft;is+c&&(e=i+a-s),_()(e)&&(n.scrollLeft=e)}},{key:"hideScrollbar",value:function(){var e=this.$tabs;if("none"!==getComputedStyle(this.tabs,"::-webkit-scrollbar").display){var t=function(){if(_()(N))return N;if(!document)return 16;var e=document.createElement("div"),t=document.createElement("div");e.setAttribute("style","display: block; width: 100px; height: 100px; overflow: scroll;"),t.setAttribute("style","height: 200px"),e.appendChild(t);var n=document.body||document.documentElement;return n.appendChild(e),N=e.offsetWidth-e.clientWidth,n.removeChild(e),N}();e.css("height",this.options.height-1+t+"px")}}},{key:"updateSlider",value:function(){var e=this.$slider,t=this.$tabs,n=this.c,o=t.find(n(".selected")).get(0);o?e.css({width:o.offsetWidth,left:o.offsetLeft-t.get(0).scrollLeft}):e.css({width:0})}},{key:"updateHeight",value:function(){var e=this.options.height,t=e-1;this.find(".tabs-container").css("height",e+"px"),this.find(".item").css({height:t,lineHeight:t}),this.hideScrollbar()}},{key:"bindEvent",value:function(){var e=this,t=this.tabs,n=this.c;this.on("changeOption",(function(t){if("height"===t)e.updateHeight()}));var o=this;this.$tabs.on("wheel",(function(e){e.preventDefault(),t.scrollLeft+=e.origEvent.deltaY})).on("click",n(".item"),(function(){var e=h()(this);o.select(e.data("id"))})).on("click",n(".close"),(function(e){e.stopPropagation();var t=h()(this).parent().parent();o.remove(t.data("id"))})).on("scroll",(function(){e.updateSlider()}))}},{key:"initTpl",value:function(){this.$container.html(this.c(Q()(Y||(Y=(0,o.A)(['\n
    \n
    \n
    \n
    \n '])))))}}])}($);(function(e,t){try{e.exports=t,e.exports.default=t}catch(e){}})(e,K)},8091:function(e,t,n){var o=n(282),r=n(9100),i=n(5207);function a(e){return''+e+""}t=function(e,t){t=t||a;var n=o(e);return r(n,(function(n){e=e.replace(new RegExp(i(n),"g"),t)})),e},e.exports=t},8098:function(e,t,n){var o=n(5693),r=n(5793),i=n(3145);t=function(e,t,n){t=o(t,n);for(var a=!r(e)&&i(e),s=(a||e).length,c=0;ct.right||e.bottomt.bottom}t=function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},n=t.display,o=void 0===n||n,s=t.visibility,c=void 0!==s&&s,l=t.opacity,u=void 0!==l&&l,d=t.size,h=void 0!==d&&d,f=t.viewport,p=void 0!==f&&f,v=t.overflow,g=void 0!==v&&v,m=r(e);if(o){var b=e.tagName;if("BODY"===b||"HTML"===b||"fixed"===m.position){if("none"===m.display)return!0;for(var y=e;y=y.parentElement;){if("none"===r(y).display)return!0}}else if(null===e.offsetParent)return!0}if(c&&"hidden"===m.visibility)return!0;if(u){if("0"===m.opacity)return!0;for(var A=e;A=A.parentElement;){if("0"===r(A).opacity)return!0}}var w=e.getBoundingClientRect();if(h&&(0===w.width||0===w.height))return!0;if(p)return a(w,{top:0,left:0,right:i.documentElement.clientWidth,bottom:i.documentElement.clientHeight});if(g)for(var _=e;_=_.parentElement;){var x=r(_).overflow;if("scroll"===x||"hidden"===x)if(a(w,_.getBoundingClientRect()))return!0}return!1},e.exports=t},8665:function(e,t,n){"use strict";var o,r=this&&this.__extends||(o=function(e,t){return o=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var n in t)Object.prototype.hasOwnProperty.call(t,n)&&(e[n]=t[n])},o(e,t)},function(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Class extends value "+String(t)+" is not a constructor or null");function n(){this.constructor=e}o(e,t),e.prototype=null===t?Object.create(t):(n.prototype=t.prototype,new n)}),i=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var a=function(e){function t(){return null!==e&&e.apply(this,arguments)||this}return r(t,e),t.prototype.trigger=function(e,t){this.emit("message",JSON.stringify({method:e,params:t}))},t}(i(n(2263)).default);t.default=new a},8687:function(e,t,n){(t=n(6314)(!1)).push([e.id,'._container .luna-console{background:var(--background)}._container .luna-console-header{color:var(--link-color);border-bottom-color:var(--border)}._container .luna-console-nesting-level{border-right-color:var(--border)}._container .luna-console-nesting-level::before{border-bottom-color:var(--border)}._container .luna-console-log-container.luna-console-selected .luna-console-log-item{background:var(--contrast)}._container .luna-console-log-container.luna-console-selected .luna-console-log-item:not(.luna-console-error):not(.luna-console-warn){border-color:var(--border)}._container .luna-console-log-item{border-bottom-color:var(--border);color:var(--foreground)}._container .luna-console-log-item a{color:var(--link-color)!important}._container .luna-console-log-item .luna-console-icon-container .luna-console-icon{color:var(--foreground)}._container .luna-console-log-item .luna-console-icon-container .luna-console-icon-error{color:#ef3842}._container .luna-console-log-item .luna-console-icon-container .luna-console-icon-warn{color:#e8a400}._container .luna-console-log-item .luna-console-count{color:var(--select-foreground);background:var(--highlight)}._container .luna-console-log-item.luna-console-warn{color:var(--console-warn-foreground);background:var(--console-warn-background);border-color:var(--console-warn-border)}._container .luna-console-log-item.luna-console-error{background:var(--console-error-background);color:var(--console-error-foreground);border-color:var(--console-error-border)}._container .luna-console-log-item.luna-console-error .luna-console-count{background:var(--console-error-foreground)}._container .luna-console-log-item .luna-console-code .luna-console-key{color:var(--var-color)}._container .luna-console-log-item .luna-console-code .luna-console-number{color:var(--number-color)}._container .luna-console-log-item .luna-console-code .luna-console-null{color:var(--operator-color)}._container .luna-console-log-item .luna-console-code .luna-console-string{color:var(--string-color)}._container .luna-console-log-item .luna-console-code .luna-console-boolean{color:var(--keyword-color)}._container .luna-console-log-item .luna-console-code .luna-console-special{color:var(--operator-color)}._container .luna-console-log-item .luna-console-code .luna-console-keyword{color:var(--keyword-color)}._container .luna-console-log-item .luna-console-code .luna-console-operator{color:var(--operator-color)}._container .luna-console-log-item .luna-console-code .luna-console-comment{color:var(--comment-color)}._container .luna-console-log-item .luna-console-log-content .luna-console-null,._container .luna-console-log-item .luna-console-log-content .luna-console-undefined{color:var(--operator-color)}._container .luna-console-log-item .luna-console-log-content .luna-console-number{color:var(--number-color)}._container .luna-console-log-item .luna-console-log-content .luna-console-boolean{color:var(--keyword-color)}._container .luna-console-log-item .luna-console-log-content .luna-console-regexp,._container .luna-console-log-item .luna-console-log-content .luna-console-symbol{color:var(--var-color)}._container .luna-console-preview .luna-console-key{color:var(--var-color)}._container .luna-console-preview .luna-console-number{color:var(--number-color)}._container .luna-console-preview .luna-console-null{color:var(--operator-color)}._container .luna-console-preview .luna-console-string{color:var(--string-color)}._container .luna-console-preview .luna-console-boolean{color:var(--keyword-color)}._container .luna-console-preview .luna-console-special{color:var(--operator-color)}._container .luna-console-preview .luna-console-keyword{color:var(--keyword-color)}._container .luna-console-preview .luna-console-operator{color:var(--operator-color)}._container .luna-console-preview .luna-console-comment{color:var(--comment-color)}._container .luna-object-viewer{color:var(--primary);font-size:12px!important}._container .luna-object-viewer-null{color:var(--operator-color)}._container .luna-object-viewer-regexp,._container .luna-object-viewer-string{color:var(--string-color)}._container .luna-object-viewer-number{color:var(--number-color)}._container .luna-object-viewer-boolean{color:var(--keyword-color)}._container .luna-object-viewer-special{color:var(--operator-color)}._container .luna-object-viewer-key,._container .luna-object-viewer-key-lighter{color:var(--var-color)}._container .luna-object-viewer-expanded:before{border-color:transparent;border-top-color:var(--foreground)}._container .luna-object-viewer-collapsed:before{border-top-color:transparent;border-left-color:var(--foreground)}._container .luna-notification{pointer-events:none!important;padding:10px;z-index:1000}._container .luna-notification-item{z-index:500;color:var(--foreground);background:var(--background);box-shadow:none;padding:5px 10px;border:1px solid var(--border)}._container .luna-notification-upper{margin-bottom:10px}._container .luna-notification-lower{margin-top:10px}._container .luna-data-grid{color:var(--foreground);background:var(--background);border-color:var(--border)}._container .luna-data-grid td,._container .luna-data-grid th{border-color:var(--border)}._container .luna-data-grid th{background:var(--darker-background)}._container .luna-data-grid th.luna-data-grid-sortable:active,._container .luna-data-grid th.luna-data-grid-sortable:hover{color:var(--select-foreground);background:var(--highlight)}._container .luna-data-grid .luna-data-grid-data-container .luna-data-grid-node.luna-data-grid-selectable:hover,._container .luna-data-grid .luna-data-grid-data-container .luna-data-grid-node.luna-data-grid-selected{background:var(--highlight)}._container .luna-data-grid .luna-data-grid-data-container tr:nth-child(even){background:var(--contrast)}._container .luna-data-grid:focus .luna-data-grid-data-container .luna-data-grid-node.luna-data-grid-selected{background:var(--accent)}._container .luna-dom-viewer{color:var(--foreground)}._container .luna-dom-viewer .luna-dom-viewer-html-tag,._container .luna-dom-viewer .luna-dom-viewer-tag-name{color:var(--tag-name-color)}._container .luna-dom-viewer .luna-dom-viewer-attribute-name{color:var(--attribute-name-color)}._container .luna-dom-viewer .luna-dom-viewer-attribute-value{color:var(--string-color)}._container .luna-dom-viewer .luna-dom-viewer-html-comment{color:var(--comment-color)}._container .luna-dom-viewer .luna-dom-viewer-tree-item:hover .luna-dom-viewer-selection{background:var(--contrast)}._container .luna-dom-viewer .luna-dom-viewer-tree-item.luna-dom-viewer-selected .luna-dom-viewer-selection{background:var(--highlight)}._container .luna-dom-viewer .luna-dom-viewer-tree-item.luna-dom-viewer-selected:focus .luna-dom-viewer-selection{background:var(--accent);opacity:.2}._container .luna-dom-viewer .luna-dom-viewer-text-node .luna-dom-viewer-key{color:var(--var-color)}._container .luna-dom-viewer .luna-dom-viewer-text-node .luna-dom-viewer-number{color:var(--number-color)}._container .luna-dom-viewer .luna-dom-viewer-text-node .luna-dom-viewer-null{color:var(--operator-color)}._container .luna-dom-viewer .luna-dom-viewer-text-node .luna-dom-viewer-string{color:var(--string-color)}._container .luna-dom-viewer .luna-dom-viewer-text-node .luna-dom-viewer-boolean{color:var(--keyword-color)}._container .luna-dom-viewer .luna-dom-viewer-text-node .luna-dom-viewer-special{color:var(--operator-color)}._container .luna-dom-viewer .luna-dom-viewer-text-node .luna-dom-viewer-keyword{color:var(--keyword-color)}._container .luna-dom-viewer .luna-dom-viewer-text-node .luna-dom-viewer-operator{color:var(--operator-color)}._container .luna-dom-viewer .luna-dom-viewer-text-node .luna-dom-viewer-comment{color:var(--comment-color)}._container .luna-dom-viewer-children{margin:0;padding-left:15px!important}._container ._inline .luna-modal,._container ._inline .luna-notification{position:absolute}._container .luna-modal{z-index:9999999}._container .luna-modal-body,._container .luna-modal-input{color:var(--foreground);background:var(--background)}._container .luna-modal-body{border-color:var(--border)}._container .luna-modal-input{-webkit-user-select:text!important;-moz-user-select:text!important;-ms-user-select:text!important;user-select:text!important;border-color:var(--border)}._container .luna-modal-button-group .luna-modal-secondary{border-color:var(--border);color:var(--foreground);background:var(--background)}._container .luna-modal-button-group .luna-modal-primary{background:var(--accent)}._container .luna-modal-button-group .luna-modal-button:active::before{background:var(--accent)}._container .luna-tab{position:absolute;left:0;top:0;color:var(--foreground);background:var(--darker-background)}._container .luna-tab-tabs-container{border-color:var(--border)}._container .luna-tab-item.luna-tab-selected,._container .luna-tab-item:hover{background:var(--highlight);color:var(--select-foreground)}._container .luna-tab-slider{background:var(--accent)}._container .luna-text-viewer{color:var(--foreground);border:none;border-bottom:1px solid var(--border);background:var(--background);font-size:12px}._container .luna-text-viewer .luna-text-viewer-line-text{-webkit-user-select:text;-moz-user-select:text;-ms-user-select:text;user-select:text}._container .luna-text-viewer .luna-text-viewer-line-text *{-webkit-user-select:text;-moz-user-select:text;-ms-user-select:text;user-select:text}._container .luna-text-viewer .luna-text-viewer-copy,._container .luna-text-viewer .luna-text-viewer-line-number{border-color:var(--border)}._container .luna-text-viewer .luna-text-viewer-copy .luna-text-viewer-icon-check{color:var(--accent)}._container .luna-text-viewer .luna-text-viewer-copy{background-color:var(--background)}._container .luna-setting{color:var(--foreground);background:var(--background)}._container .luna-setting-item.luna-setting-selected,._container .luna-setting-item:hover{background:var(--darker-background)}._container .luna-setting-item.luna-setting-selected:focus{outline:0}._container .luna-setting-item-title{font-size:14px}._container .luna-setting-item-separator{border-color:var(--border)}._container .luna-setting-item-checkbox input{border-color:var(--border)}._container .luna-setting-item-checkbox input:checked{background-color:var(--accent);border-color:var(--accent)}._container .luna-setting-item-select .luna-setting-select select{color:var(--foreground);border-color:var(--border);background:var(--background)}._container .luna-setting-item-select .luna-setting-select:after{border-top-color:var(--foreground)}._container .luna-setting-item-button button{color:var(--accent);background:var(--background);border-color:var(--border)}._container .luna-setting-item-button button:active,._container .luna-setting-item-button button:hover{background:var(--darker-background)}._container .luna-setting-item-button button:active{border:1px solid var(--accent)}._container .luna-setting-item-number .luna-setting-range-container .luna-setting-range-track .luna-setting-range-track-bar{background:var(--border)}._container .luna-setting-item-number .luna-setting-range-container .luna-setting-range-track .luna-setting-range-track-bar .luna-setting-range-track-progress{background:var(--accent)}._container .luna-setting-item-number .luna-setting-range-container input::-webkit-slider-thumb{border-color:var(--border);background:radial-gradient(circle at center,var(--dark) 0,var(--dark) 15%,var(--light) 22%,var(--light) 100%)}._container .luna-box-model{background:var(--background)}._container .luna-box-model-border,._container .luna-box-model-content,._container .luna-box-model-margin,._container .luna-box-model-padding,._container .luna-box-model-position{color:var(--foreground);background:var(--background)}._container{min-width:320px;pointer-events:none;position:fixed;left:0;top:0;width:100%;height:100%;z-index:9999999;color:var(--foreground);font-family:-apple-system,system-ui,BlinkMacSystemFont,".SFNSDisplay-Regular","Helvetica Neue","Lucida Grande","Segoe UI",Tahoma,sans-serif;font-size:14px;direction:ltr}._container._dark{color-scheme:dark}._container *{box-sizing:border-box;pointer-events:all;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;-webkit-tap-highlight-color:transparent;-webkit-text-size-adjust:none}._container ul{list-style:none;padding:0;margin:0}._container h1,._container h2,._container h3,._container h4{margin:0}._container h2{font-size:14px}._container h2 [class*=" _icon-"],._container h2 [class^=icon-]{font-weight:400}._container._inline{position:static}._hidden{display:none}._icon-disabled{opacity:.5;pointer-events:none;cursor:default!important}._icon-disabled:active{color:inherit!important}._tag-name-color{color:var(--tag-name-color)}._function-color{color:var(--function-color)}._attribute-name-color{color:var(--attribute-name-color)}._operator-color{color:var(--operator-color)}._string-color{color:var(--string-color)}',""]),e.exports=t},8734:function(e,t,n){var o=n(2717),r=n(1738),i=n(6026),a=n(8),s=n(928),c=n(9848);t=o({initialize:function(e){r(e)&&(e=t.parse(e)),this.model=e.model,this.val=e.val},toRgb:function(){var e=this.val;"hsl"===this.model&&(e=s(e));var t="rgba";return 1===e[3]&&(t="rgb",e=e.slice(0,3)),t+"("+e.join(", ")+")"},toHex:function(){var e=this.val;"hsl"===this.model&&(e=s(e));var t=c.encode(e.slice(0,3));return t[0]===t[1]&&t[2]===t[3]&&t[4]===t[5]&&(t=t[0]+t[2]+t[5]),"#"+t},toHsl:function(){var e=this.val;"rgb"===this.model&&(e=a(e));var t="hsla";return 1===e[3]&&(t="hsl",e=e.slice(0,3)),e[1]=e[1]+"%",e[2]=e[2]+"%",t+"("+e.join(", ")+")"}},{parse:function(e){var t,n,o=[0,0,0,1],r="rgb";if(n=e.match(l))for(n=n[1],t=0;t<3;t++)o[t]=parseInt(n[t]+n[t],16);else if(n=e.match(u))for(n=n[1],t=0;t<3;t++){var a=2*t;o[t]=parseInt(n.slice(a,a+2),16)}else if(n=e.match(d)){for(t=0;t<3;t++)o[t]=parseInt(n[t+1],0);n[4]&&(o[3]=parseFloat(n[4]))}else if(n=e.match(h)){for(t=0;t<3;t++)o[t]=Math.round(2.55*parseFloat(n[t+1]));n[4]&&(o[3]=parseFloat(n[4]))}else(n=e.match(f))&&(r="hsl",o=[(parseFloat(n[1])%360+360)%360,i(parseFloat(n[2]),0,100),i(parseFloat(n[3]),0,100),i(parseFloat(n[4]),0,1)]);return{val:o,model:r}}});var l=/^#([a-fA-F0-9]{3})$/,u=/^#([a-fA-F0-9]{6})$/,d=/^rgba?\(\s*([+-]?\d+)\s*,\s*([+-]?\d+)\s*,\s*([+-]?\d+)\s*(?:,\s*([+-]?[\d.]+)\s*)?\)$/,h=/^rgba?\(\s*([+-]?[\d.]+)%\s*,\s*([+-]?[\d.]+)%\s*,\s*([+-]?[\d.]+)%\s*(?:,\s*([+-]?[\d.]+)\s*)?\)$/,f=/^hsla?\(\s*([+-]?\d*[.]?\d+)(?:deg)?\s*,\s*([+-]?[\d.]+)%\s*,\s*([+-]?[\d.]+)%\s*(?:,\s*([+-]?[\d.]+)\s*)?\)$/;e.exports=t},8757:function(e,t,n){"use strict";var o,r=this&&this.__extends||(o=function(e,t){return o=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var n in t)Object.prototype.hasOwnProperty.call(t,n)&&(e[n]=t[n])},o(e,t)},function(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Class extends value "+String(t)+" is not a constructor or null");function n(){this.constructor=e}o(e,t),e.prototype=null===t?Object.create(t):(n.prototype=t.prototype,new n)}),i=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var a=i(n(2263)),s=i(n(9100)),c=function(e){function t(){var t=e.call(this)||this;return t.observer=new MutationObserver((function(e){(0,s.default)(e,(function(e){return t.handleMutation(e)}))})),t}return r(t,e),t.prototype.observe=function(e){this.observer.observe(e,{attributes:!0,childList:!0,characterData:!0,subtree:!0})},t.prototype.disconnect=function(){this.observer.disconnect()},t.prototype.handleMutation=function(e){"attributes"===e.type?this.emit("attributes",e.target,e.attributeName):"childList"===e.type?this.emit("childList",e.target,e.addedNodes,e.removedNodes):"characterData"===e.type&&this.emit("characterData",e.target)},t}(a.default);t.default=new c},8785:function(e,t){t=function(){for(var e=arguments,t=e[0],n=1,o=e.length;n0&&(n+="["+o+"]"),new i(n,e.nodeType===Node.DOCUMENT_NODE)}t=function(e,t){return o(e)?function(e){for(var t=[],n=document.evaluate(e,document,null,XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,null),o=0;o1&&void 0!==arguments[1]&&arguments[1];if(e.nodeType===Node.DOCUMENT_NODE)return"/";var n=[],o=e;for(;o;){var i=r(o,t);if(!i)break;if(n.push(i),i.optimized)break;o=o.parentNode}return n.reverse(),(n.length&&n[0].optimized?"":"/")+n.join("/")}(e,t)};var i=n(2717)({initialize:function(e,t){this.value=e,this.optimized=t||!1},toString:function(){return this.value}});e.exports=t},8966:function(e,t,n){var o=n(2717),r=n(3249);function i(){return!0}function a(){return!1}function s(e){var n,o=this.events[e.type],r=c.call(this,e,o);e=new t.Event(e);for(var i,a,s=0;(a=r[s++])&&!e.isPropagationStopped();)for(e.curTarget=a.el,i=0;(n=a.handlers[i++])&&!e.isImmediatePropagationStopped();)!1===n.handler.apply(a.el,[e])&&(e.preventDefault(),e.stopPropagation())}function c(e,t){var n,o,i,a,s=e.target,c=[],l=t.delegateCount;if(s.nodeType)for(;s!==this;s=s.parentNode||this){for(o=[],a=0;a=i[a[l]]){c=a[l];break}return+(n/i[c]).toFixed(2)+c};var i={ms:1,s:1e3};i.m=60*i.s,i.h=60*i.m,i.d=24*i.h,i.y=365.25*i.d;var a=["y","d","h","m","s"],s=/^((?:\d+)?\.?\d+) *(s|m|h|d|y)?$/;e.exports=t},9122:function(e,t,n){var o=n(9760),r=n(3957),i=n(1168);t=function(e){return!!o(e)&&(r(e)?s.test(i(e)):c.test(i(e)))};var a=Object.prototype.hasOwnProperty,s=new RegExp("^"+i(a).replace(/[\\^$.*+?()[\]{}|]/g,"\\$&").replace(/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g,"$1.*?")+"$"),c=/^\[object .+?Constructor\]$/;e.exports=t},9186:function(e,t,n){var o=n(769),r=n(9760),i=n(1738),a=n(9100),s=n(8971),c=n(3612);(t=function(e,t,n){if(e=c(e),s(n)&&i(t))return function(e,t){return e.getAttribute(t)}(e[0],t);var o=t;r(o)||((o={})[t]=n),function(e,t){a(e,(function(e){a(t,(function(t,n){e.setAttribute(n,t)}))}))}(e,o)}).remove=function(e,t){e=c(e),t=o(t),a(e,(function(e){a(t,(function(t){e.removeAttribute(t)}))}))},e.exports=t},9196:function(e,t,n){"use strict";var o,r=this&&this.__extends||(o=function(e,t){return o=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var n in t)Object.prototype.hasOwnProperty.call(t,n)&&(e[n]=t[n])},o(e,t)},function(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Class extends value "+String(t)+" is not a constructor or null");function n(){this.constructor=e}o(e,t),e.prototype=null===t?Object.create(t):(n.prototype=t.prototype,new n)}),i=this&&this.__assign||function(){return i=Object.assign||function(e){for(var t,n=1,o=arguments.length;n=e.length&&(e=void 0),{value:e&&e[o++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},s=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var c=s(n(7214)),l=n(926),u=n(4725),d=s(n(4095)),h=s(n(5865)),f=s(n(96)),p=s(n(9100)),v=s(n(8734)),g=s(n(9848)),m=s(n(3805)),b=s(n(8105)),y=s(n(387)),A=s(n(3249)),w=s(n(6030)),_=s(n(3019)),x=s(n(1738));n(3270);var k=function(e){function t(t,n){void 0===n&&(n={});var o=e.call(this,t,{compName:"dom-highlighter"},n)||this;return o.overlay=new l.HighlightOverlay(window),o.reset=function(){var e=document.documentElement.clientWidth,t=document.documentElement.clientHeight;o.overlay.reset({viewportSize:{width:e,height:t},deviceScaleFactor:1,pageScaleFactor:1,pageZoomFactor:1,emulationScaleFactor:1,scrollX:window.scrollX,scrollY:window.scrollY})},o.initOptions(n,{showRulers:!1,showExtensionLines:!1,showInfo:!0,showStyles:!0,showAccessibilityInfo:!0,colorFormat:"hex",contentColor:"rgba(111, 168, 220, .66)",paddingColor:"rgba(147, 196, 125, .55)",borderColor:"rgba(255, 229, 153, .66)",marginColor:"rgba(246, 178, 107, .66)",monitorResize:!0}),o.overlay.setContainer(t),o.overlay.setPlatform("mac"),o.redraw=(0,h.default)((function(){o.reset(),o.draw()}),16),o.redraw(),o.bindEvent(),o}return r(t,e),t.prototype.highlight=function(e,t){t&&(0,b.default)(this.options,t),this.target=e,e instanceof HTMLElement&&this.options.monitorResize&&(this.resizeSensor&&this.resizeSensor.destroy(),this.resizeSensor=new d.default(e),this.resizeSensor.addListener(this.redraw)),this.redraw()},t.prototype.hide=function(){this.target=null,this.redraw()},t.prototype.intercept=function(e){this.interceptor=e},t.prototype.destroy=function(){window.removeEventListener("resize",this.redraw),window.removeEventListener("scroll",this.redraw),this.resizeSensor&&this.resizeSensor.destroy(),e.prototype.destroy.call(this)},t.prototype.draw=function(){var e=this.target;e&&(e instanceof Text?this.drawText(e):this.drawElement(e))},t.prototype.drawText=function(e){var t=this.options,n=document.createRange();n.selectNode(e);var o=n.getBoundingClientRect(),r=o.left,i=o.top,a=o.width,s=o.height;n.detach();var c={paths:[{path:this.rectToPath({left:r,top:i,width:a,height:s}),fillColor:E(t.contentColor),name:"content"}],showExtensionLines:t.showExtensionLines,showRulers:t.showRulers};t.showInfo&&(c.elementInfo={tagName:"#text",nodeWidth:a,nodeHeight:s}),this.overlay.drawHighlight(c)},t.prototype.drawElement=function(e){var t={paths:this.getPaths(e),showExtensionLines:this.options.showExtensionLines,showRulers:this.options.showRulers,colorFormat:this.options.colorFormat};if(this.options.showInfo&&(t.elementInfo=this.getElementInfo(e)),this.interceptor){var n=this.interceptor(t);n&&(t=n)}this.overlay.drawHighlight(t)},t.prototype.getPaths=function(e){var t=this.options,n=window.getComputedStyle(e),o=e.getBoundingClientRect(),r=o.left,i=o.top,a=o.width,s=o.height,c=function(e){return(0,u.pxToNum)(n.getPropertyValue(e))},l=c("margin-left"),d=c("margin-right"),h=c("margin-top"),f=c("margin-bottom"),p=c("border-left-width"),v=c("border-right-width"),g=c("border-top-width"),m=c("border-bottom-width"),b=c("padding-left"),y=c("padding-right"),A=c("padding-top"),w=c("padding-bottom");return[{path:this.rectToPath({left:r+p+b,top:i+g+A,width:a-p-b-v-y,height:s-g-A-m-w}),fillColor:E(t.contentColor),name:"content"},{path:this.rectToPath({left:r+p,top:i+g,width:a-p-v,height:s-g-m}),fillColor:E(t.paddingColor),name:"padding"},{path:this.rectToPath({left:r,top:i,width:a,height:s}),fillColor:E(t.borderColor),name:"border"},{path:this.rectToPath({left:r-l,top:i-h,width:a+l+d,height:s+h+f}),fillColor:E(t.marginColor),name:"margin"}]},t.prototype.getElementInfo=function(e){var t=e.getBoundingClientRect(),n=t.width,o=t.height,r=e.getAttribute("class")||"";r=r.split(/\s+/).map((function(e){return"."+e})).join("");var i={tagName:(0,f.default)(e.tagName),className:r,idValue:e.id,nodeWidth:n,nodeHeight:o};return this.options.showStyles&&(i.style=this.getStyles(e)),this.options.showAccessibilityInfo&&(0,b.default)(i,this.getAccessibilityInfo(e)),i},t.prototype.getStyles=function(e){for(var t=window.getComputedStyle(e),n=!1,o=e.childNodes,r=0,i=o.length;r-1)},t.prototype.getAccessibleNameAndRole=function(e){var t=e.getAttribute("labelledby")||e.getAttribute("aria-label"),n=e.getAttribute("role"),o=(0,f.default)(e.tagName);return _.default.forEach((function(t){var r,i;if(!n){var s=t[0],c=t[2];if(s===o){if(c)try{for(var l=a(c),u=l.next();!u.done;u=l.next()){var d=u.value;if(e.getAttribute(d[0])!==d[1])return}}catch(e){r={error:e}}finally{try{u&&!u.done&&(i=l.return)&&i.call(l)}finally{if(r)throw r.error}}n=t[1]}}})),{accessibleName:t||e.getAttribute("title")||"",accessibleRole:n||"generic"}},t.prototype.bindEvent=function(){var e=this;window.addEventListener("resize",this.redraw),window.addEventListener("scroll",this.redraw),this.on("optionChange",(function(){return e.redraw()}))},t.prototype.rectToPath=function(e){var t=e.left,n=e.top,o=e.width,r=e.height,i=[];return i.push("M",t,n),i.push("L",t+o,n),i.push("L",t+o,n+r),i.push("L",t,n+r),i.push("Z"),i},t}(c.default);t.default=k,e.exports=k,e.exports.default=k;var C=/^rgb\((\d{1,3}),\s*(\d{1,3}),\s*(\d{1,3})\)$/,S=/^rgba\((\d{1,3}),\s*(\d{1,3}),\s*(\d{1,3}),\s*(\d*(?:\.\d+)?)\)$/;function E(e){return(0,x.default)(e)?e:e.a?"rgba(".concat(e.r,", ").concat(e.g,", ").concat(e.b,", ").concat(e.a,")"):"rgb(".concat(e.r,", ").concat(e.g,", ").concat(e.b,")")}function T(e,t,n){void 0===n&&(n=!1);var o={};return(0,p.default)(t,(function(t){var r,i=e["text-opacity"===t?"color":t];i&&(r=i,(C.test(r)||S.test(r))&&(i=function(e){var t=v.default.parse(e),n=t.val[3]||1;return t.val=t.val.slice(0,3),t.val.push(Math.round(255*n)),"#"+(0,m.default)(g.default.encode(t.val))}(i),"text-opacity"===t&&(i=i.slice(7),i=g.default.decode(i)[0]/255)),n&&(t=(0,y.default)(t)),o[t]=i)})),o}},9350:function(e,t){t=function(e){return"symbol"==typeof e},e.exports=t},9389:function(e,t,n){var o=n(5902),r=n(3145);t=function(e){return s.test(e)?e.replace(c,l):e};var i=n(5282)(o.map),a="(?:"+r(i).join("|")+")",s=new RegExp(a),c=new RegExp(a,"g");function l(e){return i[e]}e.exports=t},9405:function(e,t,n){var o=n(5333),r=n(9651);t=function(e,t){return null==t&&e.trim?e.trim():o(r(e,t),t)},e.exports=t},9410:function(e,t,n){var o=n(9993);t=function(e,t){t=t||o;var n=new Image;n.onload=function(){t(null,n)},n.onerror=function(e){t(e)},n.src=e},e.exports=t},9464:function(e,t,n){var o=n(5793),r=n(6214),i=n(1738),a=n(5132),s=n(3145);t=function(e){return null==e||(o(e)&&(r(e)||i(e)||a(e))?0===e.length:0===s(e).length)},e.exports=t},9548:function(e,t,n){var o=n(383),r=n(7005),i=n(6214),a=n(9100),s=n(1738),c=n(5154);var l=function(e){return e.replace(/"/g,'"')},u=function(e){return e.replace(/"/g,""")};t={parse:function(e){var t=[],n=new r;return o(e,{start:function(e,t){t=c(t,(function(e){return l(e)})),n.push({tag:e,attrs:t})},end:function(){var e=n.pop();if(n.size){var o=n.peek();i(o.content)||(o.content=[]),o.content.push(e)}else t.push(e)},comment:function(e){var o="\x3c!--".concat(e,"--\x3e"),r=n.peek();r?(r.content||(r.content=[]),r.content.push(o)):t.push(o)},text:function(e){var o=n.peek();o?(o.content||(o.content=[]),o.content.push(e)):t.push(e)}}),t},stringify:function e(t){var n="";return i(t)?a(t,(function(t){return n+=e(t)})):s(t)?n=t:(n+="<".concat(t.tag),a(t.attrs,(function(e,t){return n+=" ".concat(t,'="').concat(u(e),'"')})),n+=">",t.content&&(n+=e(t.content)),n+="")),n}},e.exports=t},9651:function(e,t){t=function(e,t){if(null==t){if(e.trimRight)return e.trimRight();t=" \r\n\t\f\v"}for(var n,o,r=e.length-1,i=t.length,a=!0;a&&r>=0;)for(a=!1,n=-1,o=e.charAt(r);++n=0?e.substring(0,r+1):""},e.exports=t},9756:function(e,t){t=function(e){for(var n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:t.defComparator,o=0,r=e.length;o0)return!1;return!0},t.defComparator=function(e,t){return et?1:0},e.exports=t},9760:function(e,t){t=function(e){var t=typeof e;return!!e&&("function"===t||"object"===t)},e.exports=t},9848:function(e,t,n){var o=n(6024);t={encode:function(e){for(var t=[],n=0,o=e.length;n>>4).toString(16)),t.push((15&r).toString(16))}return t.join("")},decode:function(e){var t=[],n=e.length;o(n)&&n--;for(var r=0;r0||u)&&(i.children=g(e,o)),i}function g(e,t){var n=m(e.childNodes);return(0,r.default)(n,(function(e){return v(e,{depth:t-1})}))}function m(e){return i.default(e,(function(e){return b(e)}))}function b(e){if(1===e.nodeType){var t=e.getAttribute("class")||"";if((0,c.default)(t,"__chobitsu-hide__")||(0,c.default)(t,"html2canvas-container"))return!1}var n=!(3===e.nodeType&&""===(0,s.default)(e.nodeValue||""));return n&&e.parentNode?b(e.parentNode):n}},9907:function(e,t,n){(t=n(6314)(!1)).push([e.id,"@font-face{font-family:luna-object-viewer-icon;src:url('data:application/x-font-woff;charset=utf-8;base64,d09GRgABAAAAAAS8AAsAAAAAB7QAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAABHU1VCAAABCAAAAGEAAACMISgl+k9TLzIAAAFsAAAAPQAAAFZLxUkWY21hcAAAAawAAADWAAACdBU42qdnbHlmAAAChAAAAC4AAAAwabU7V2hlYWQAAAK0AAAALwAAADZzjr4faGhlYQAAAuQAAAAYAAAAJAFyANlobXR4AAAC/AAAABAAAABAAZAAAGxvY2EAAAMMAAAAEAAAACIAtACobWF4cAAAAxwAAAAfAAAAIAEbAA9uYW1lAAADPAAAASkAAAIWm5e+CnBvc3QAAARoAAAAUwAAAHZW8MNZeJxNjTsOQFAQRc/z/+sV1mABohKV0gZeJRJR2X9cT4RJZu7nFIMBMjoGvHGaF6rdngcNAc/c/O/Nvq2W5E1igdNE2zv1iGh1c5FQPlYXUlJRyxt9+/pUKadQa/AveGEGZQAAAHicY2BkkGScwMDKwMBQx9ADJGWgdAIDJ4MxAwMTAyszA1YQkOaawnCAQfcjE8MJIFcITDIwMIIIAFqDCGkAAAB4nM2STQ4BQRCFv54ZP8MwFhYW4gQcShBsSERi50BWDuFCcwJedddKRGKnOt8k9aanqudVAy0gF3NRQLgTsLhJDVHP6UW94Kp8zEhKwYIlG/YcOXHm0mTPp96aumLLwdUQ1fcIqmJrwpSZL+iqak5JmyE1Ayr1bdGhr/2ZPmp/qPQtuj/uJzqQl+pfDyypesQD6AT/ElV8PjyrMccT9rdLR3PUFBI227VTio1jbm6dodg5VnPvmAsHxzofHfmi+Sbs/pwdWcXFkWdNSNg9arIE2QufuSCyAAB4nGNgZACBlQzTGZgYGMyVxVc2O073AIpAxHsYloHFRc2dPZY2OTIwAACmEQesAAB4nGNgZGBgAOINe2b6x/PbfGXgZjgBFIjifLyvAUEDwUqGZUCSg4EJxAEAUn4LLAB4nGNgZGBgOMHAACdXMjAyoAIBADizAkx4nGNgAIITUEwGAABZUAGReJxjYAACHgYJ3BAAE94BXXicY2BkYGAQYGBmANEMDExAzAWEDAz/wXwGAApcASsAeJxlkD1uwkAUhMdgSAJSghQpKbNVCiKZn5IDQE9Bl8KYtTGyvdZ6QaLLCXKEHCGniHKCHChj82hgLT9/M2/e7soABviFh3p5uG1qvVq4oTpxm/Qg7JOfhTvo40W4S38o3MMbpsJ9POKdO3j+HZ0BSuEW7vEh3Kb/KeyTv4Q7eMK3cJf+j3APK/wJ9/HqDdPIFLEp3FIn+yy0Z3n+rrStUlOoSTA+WwtdaBs6vVHro6oOydS5WMXW5GrOrs4yo0prdjpywda5cjYaxeIHkcmRIoJBgbipDktoJNgjQwh71b3UK6YtKvq1VpggwPgqtWCqaJIhlcaGyTWOrBUOPG1K1zGt+FrO5KS5zGreJCMr/u+6t6MT0Q+wbaZKzDDiE1/kg+YO+T89EV6oAAAAeJxdxjkOgCAUANE/uOOGB+FQBIjaaEJIuL6FsfE1M6Lk9fXPoKioaWjp6BnQjEzMLKwYNtHepZhtuMs1vpvO/ch4HIlIxhK4KVyc7BwiD8nvDlkA') format('woff')}[class*=' luna-object-viewer-icon-'],[class^=luna-object-viewer-icon-]{display:inline-block;font-family:luna-object-viewer-icon!important;font-size:16px;font-style:normal;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.luna-object-viewer-icon-caret-down:before{content:'\\f101'}.luna-object-viewer-icon-caret-right:before{content:'\\f102'}.luna-object-viewer{overflow-x:auto;-webkit-overflow-scrolling:touch;overflow-y:hidden;cursor:default;font-family:ui-monospace,SFMono-Regular,SF Mono,Menlo,Consolas,Liberation Mono,monospace;font-size:12px;line-height:1.2;min-height:100%;color:#333;list-style:none!important}.luna-object-viewer ul{list-style:none!important;padding:0!important;padding-left:12px!important;margin:0!important}.luna-object-viewer li{position:relative;white-space:nowrap;line-height:16px;min-height:16px}.luna-object-viewer>li>.luna-object-viewer-key{display:none}.luna-object-viewer span{position:static!important}.luna-object-viewer li .luna-object-viewer-collapsed~.luna-object-viewer-close:before{color:#999}.luna-object-viewer-array .luna-object-viewer-object .luna-object-viewer-key{display:inline}.luna-object-viewer-null{color:#5e5e5e}.luna-object-viewer-regexp,.luna-object-viewer-string{color:#c41a16}.luna-object-viewer-number{color:#1c00cf}.luna-object-viewer-boolean{color:#0d22aa}.luna-object-viewer-special{color:#5e5e5e}.luna-object-viewer-key,.luna-object-viewer-key-lighter{color:#881391}.luna-object-viewer-key-lighter{opacity:.6}.luna-object-viewer-key-special{color:#5e5e5e}.luna-object-viewer-collapsed .luna-object-viewer-icon,.luna-object-viewer-expanded .luna-object-viewer-icon{position:absolute!important;left:-12px;color:#727272;font-size:12px}.luna-object-viewer-icon-caret-right{top:0}.luna-object-viewer-icon-caret-down{top:1px}.luna-object-viewer-expanded>.luna-object-viewer-icon-caret-down{display:inline}.luna-object-viewer-expanded>.luna-object-viewer-icon-caret-right{display:none}.luna-object-viewer-collapsed>.luna-object-viewer-icon-caret-down{display:none}.luna-object-viewer-collapsed>.luna-object-viewer-icon-caret-right{display:inline}.luna-object-viewer-hidden~ul{display:none}.luna-object-viewer-theme-dark{color:#fff}.luna-object-viewer-theme-dark .luna-object-viewer-null,.luna-object-viewer-theme-dark .luna-object-viewer-special{color:#a1a1a1}.luna-object-viewer-theme-dark .luna-object-viewer-regexp,.luna-object-viewer-theme-dark .luna-object-viewer-string{color:#f28b54}.luna-object-viewer-theme-dark .luna-object-viewer-boolean,.luna-object-viewer-theme-dark .luna-object-viewer-number{color:#9980ff}.luna-object-viewer-theme-dark .luna-object-viewer-key,.luna-object-viewer-theme-dark .luna-object-viewer-key-lighter{color:#5db0d7}",""]),e.exports=t},9931:function(e,t){t=function(e){return e.length<1?e:e[0].toUpperCase()+e.slice(1)},e.exports=t},9993:function(e,t){t=function(){},e.exports=t}},__webpack_module_cache__={};function __webpack_require__(e){var t=__webpack_module_cache__[e];if(void 0!==t)return t.exports;var n=__webpack_module_cache__[e]={id:e,loaded:!1,exports:{}};return __webpack_modules__[e].call(n.exports,n,n.exports,__webpack_require__),n.loaded=!0,n.exports}__webpack_require__.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return __webpack_require__.d(t,{a:t}),t},__webpack_require__.d=function(e,t){for(var n in t)__webpack_require__.o(t,n)&&!__webpack_require__.o(e,n)&&Object.defineProperty(e,n,{enumerable:!0,get:t[n]})},__webpack_require__.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(e){if("object"==typeof window)return window}}(),__webpack_require__.hmd=function(e){return(e=Object.create(e)).children||(e.children=[]),Object.defineProperty(e,"exports",{enumerable:!0,set:function(){throw new Error("ES Modules may not assign module.exports or exports.*, Use ESM export syntax, instead: "+e.id)}}),e},__webpack_require__.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},__webpack_require__.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})};var __webpack_exports__=__webpack_require__(2980);return __webpack_exports__}()})); -//# sourceMappingURL=eruda.js.map \ No newline at end of file diff --git a/manager/app/src/main/cpp/CMakeLists.txt b/manager/app/src/main/cpp/CMakeLists.txt deleted file mode 100644 index 4556ce01..00000000 --- a/manager/app/src/main/cpp/CMakeLists.txt +++ /dev/null @@ -1,21 +0,0 @@ - -# For more information about using CMake with Android Studio, read the -# documentation: https://d.android.com/studio/projects/add-native-code.html - -# Sets the minimum version of CMake required to build the native library. -cmake_minimum_required(VERSION 3.18.1) - -project("kernelsu") - -find_package(cxx REQUIRED CONFIG) -link_libraries(cxx::cxx) - -add_library(kernelsu - SHARED - jni.cc - ksu.cc - ) - -find_library(log-lib log) - -target_link_libraries(kernelsu ${log-lib}) \ No newline at end of file diff --git a/manager/app/src/main/cpp/jni.cc b/manager/app/src/main/cpp/jni.cc deleted file mode 100644 index 05021ce0..00000000 --- a/manager/app/src/main/cpp/jni.cc +++ /dev/null @@ -1,328 +0,0 @@ -#include - -#include - -#include -#include - -#include "ksu.h" - -#define LOG_TAG "KernelSU-Next" -#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__) - -extern "C" -JNIEXPORT jboolean JNICALL -Java_com_rifsxd_ksunext_Natives_becomeManager(JNIEnv *env, jobject, jstring pkg) { - auto cpkg = env->GetStringUTFChars(pkg, nullptr); - auto result = become_manager(cpkg); - env->ReleaseStringUTFChars(pkg, cpkg); - return result; -} - -extern "C" -JNIEXPORT jint JNICALL -Java_com_rifsxd_ksunext_Natives_getVersion(JNIEnv *env, jobject) { - return get_version(); -} - -extern "C" -JNIEXPORT jint JNICALL -Java_com_rifsxd_ksunext_Natives_getManagerUid(JNIEnv *env, jobject) { - uid_t manager_uid = get_manager_uid(); - return (jint)manager_uid; -} - -extern "C" -JNIEXPORT jstring JNICALL -Java_com_rifsxd_ksunext_Natives_getHookMode(JNIEnv *env, jobject) { - const char* mode = get_hook_mode(); - return env->NewStringUTF(mode); -} - -extern "C" -JNIEXPORT jintArray JNICALL -Java_com_rifsxd_ksunext_Natives_getAllowList(JNIEnv *env, jobject) { - int uids[1024]; - int size = 0; - bool result = get_allow_list(uids, &size); - LOGD("getAllowList: %d, size: %d", result, size); - if (result) { - auto array = env->NewIntArray(size); - env->SetIntArrayRegion(array, 0, size, uids); - return array; - } - return env->NewIntArray(0); -} - -extern "C" -JNIEXPORT jboolean JNICALL -Java_com_rifsxd_ksunext_Natives_isSafeMode(JNIEnv *env, jclass clazz) { - return is_safe_mode(); -} - -extern "C" -JNIEXPORT jboolean JNICALL -Java_com_rifsxd_ksunext_Natives_isLkmMode(JNIEnv *env, jclass clazz) { - return is_lkm_mode(); -} - -static void fillIntArray(JNIEnv *env, jobject list, int *data, int count) { - auto cls = env->GetObjectClass(list); - auto add = env->GetMethodID(cls, "add", "(Ljava/lang/Object;)Z"); - auto integerCls = env->FindClass("java/lang/Integer"); - auto constructor = env->GetMethodID(integerCls, "", "(I)V"); - for (int i = 0; i < count; ++i) { - auto integer = env->NewObject(integerCls, constructor, data[i]); - env->CallBooleanMethod(list, add, integer); - } -} - -static void addIntToList(JNIEnv *env, jobject list, int ele) { - auto cls = env->GetObjectClass(list); - auto add = env->GetMethodID(cls, "add", "(Ljava/lang/Object;)Z"); - auto integerCls = env->FindClass("java/lang/Integer"); - auto constructor = env->GetMethodID(integerCls, "", "(I)V"); - auto integer = env->NewObject(integerCls, constructor, ele); - env->CallBooleanMethod(list, add, integer); -} - -static uint64_t capListToBits(JNIEnv *env, jobject list) { - auto cls = env->GetObjectClass(list); - auto get = env->GetMethodID(cls, "get", "(I)Ljava/lang/Object;"); - auto size = env->GetMethodID(cls, "size", "()I"); - auto listSize = env->CallIntMethod(list, size); - auto integerCls = env->FindClass("java/lang/Integer"); - auto intValue = env->GetMethodID(integerCls, "intValue", "()I"); - uint64_t result = 0; - for (int i = 0; i < listSize; ++i) { - auto integer = env->CallObjectMethod(list, get, i); - int data = env->CallIntMethod(integer, intValue); - - if (cap_valid(data)) { - result |= (1ULL << data); - } - } - - return result; -} - -static int getListSize(JNIEnv *env, jobject list) { - auto cls = env->GetObjectClass(list); - auto size = env->GetMethodID(cls, "size", "()I"); - return env->CallIntMethod(list, size); -} - -static void fillArrayWithList(JNIEnv *env, jobject list, int *data, int count) { - auto cls = env->GetObjectClass(list); - auto get = env->GetMethodID(cls, "get", "(I)Ljava/lang/Object;"); - auto integerCls = env->FindClass("java/lang/Integer"); - auto intValue = env->GetMethodID(integerCls, "intValue", "()I"); - for (int i = 0; i < count; ++i) { - auto integer = env->CallObjectMethod(list, get, i); - data[i] = env->CallIntMethod(integer, intValue); - } -} - -extern "C" -JNIEXPORT jobject JNICALL -Java_com_rifsxd_ksunext_Natives_getAppProfile(JNIEnv *env, jobject, jstring pkg, jint uid) { - if (env->GetStringLength(pkg) > KSU_MAX_PACKAGE_NAME) { - return nullptr; - } - - p_key_t key = {}; - auto cpkg = env->GetStringUTFChars(pkg, nullptr); - strcpy(key, cpkg); - env->ReleaseStringUTFChars(pkg, cpkg); - - app_profile profile = {}; - profile.version = KSU_APP_PROFILE_VER; - - strcpy(profile.key, key); - profile.current_uid = uid; - - bool useDefaultProfile = !get_app_profile(key, &profile); - - auto cls = env->FindClass("com/rifsxd/ksunext/Natives$Profile"); - auto constructor = env->GetMethodID(cls, "", "()V"); - auto obj = env->NewObject(cls, constructor); - auto keyField = env->GetFieldID(cls, "name", "Ljava/lang/String;"); - auto currentUidField = env->GetFieldID(cls, "currentUid", "I"); - auto allowSuField = env->GetFieldID(cls, "allowSu", "Z"); - - auto rootUseDefaultField = env->GetFieldID(cls, "rootUseDefault", "Z"); - auto rootTemplateField = env->GetFieldID(cls, "rootTemplate", "Ljava/lang/String;"); - - auto uidField = env->GetFieldID(cls, "uid", "I"); - auto gidField = env->GetFieldID(cls, "gid", "I"); - auto groupsField = env->GetFieldID(cls, "groups", "Ljava/util/List;"); - auto capabilitiesField = env->GetFieldID(cls, "capabilities", "Ljava/util/List;"); - auto domainField = env->GetFieldID(cls, "context", "Ljava/lang/String;"); - auto namespacesField = env->GetFieldID(cls, "namespace", "I"); - - auto nonRootUseDefaultField = env->GetFieldID(cls, "nonRootUseDefault", "Z"); - auto umountModulesField = env->GetFieldID(cls, "umountModules", "Z"); - - env->SetObjectField(obj, keyField, env->NewStringUTF(profile.key)); - env->SetIntField(obj, currentUidField, profile.current_uid); - - if (useDefaultProfile) { - // no profile found, so just use default profile: - // don't allow root and use default profile! - LOGD("use default profile for: %s, %d", key, uid); - - // allow_su = false - // non root use default = true - env->SetBooleanField(obj, allowSuField, false); - env->SetBooleanField(obj, nonRootUseDefaultField, true); - - return obj; - } - - auto allowSu = profile.allow_su; - - if (allowSu) { - env->SetBooleanField(obj, rootUseDefaultField, (jboolean) profile.rp_config.use_default); - if (strlen(profile.rp_config.template_name) > 0) { - env->SetObjectField(obj, rootTemplateField, - env->NewStringUTF(profile.rp_config.template_name)); - } - - env->SetIntField(obj, uidField, profile.rp_config.profile.uid); - env->SetIntField(obj, gidField, profile.rp_config.profile.gid); - - jobject groupList = env->GetObjectField(obj, groupsField); - int groupCount = profile.rp_config.profile.groups_count; - if (groupCount > KSU_MAX_GROUPS) { - LOGD("kernel group count too large: %d???", groupCount); - groupCount = KSU_MAX_GROUPS; - } - fillIntArray(env, groupList, profile.rp_config.profile.groups, groupCount); - - jobject capList = env->GetObjectField(obj, capabilitiesField); - for (int i = 0; i <= CAP_LAST_CAP; i++) { - if (profile.rp_config.profile.capabilities.effective & (1ULL << i)) { - addIntToList(env, capList, i); - } - } - - env->SetObjectField(obj, domainField, - env->NewStringUTF(profile.rp_config.profile.selinux_domain)); - env->SetIntField(obj, namespacesField, profile.rp_config.profile.namespaces); - env->SetBooleanField(obj, allowSuField, profile.allow_su); - } else { - env->SetBooleanField(obj, nonRootUseDefaultField, - (jboolean) profile.nrp_config.use_default); - env->SetBooleanField(obj, umountModulesField, profile.nrp_config.profile.umount_modules); - } - - return obj; -} - -extern "C" -JNIEXPORT jboolean JNICALL -Java_com_rifsxd_ksunext_Natives_setAppProfile(JNIEnv *env, jobject clazz, jobject profile) { - auto cls = env->FindClass("com/rifsxd/ksunext/Natives$Profile"); - - auto keyField = env->GetFieldID(cls, "name", "Ljava/lang/String;"); - auto currentUidField = env->GetFieldID(cls, "currentUid", "I"); - auto allowSuField = env->GetFieldID(cls, "allowSu", "Z"); - - auto rootUseDefaultField = env->GetFieldID(cls, "rootUseDefault", "Z"); - auto rootTemplateField = env->GetFieldID(cls, "rootTemplate", "Ljava/lang/String;"); - - auto uidField = env->GetFieldID(cls, "uid", "I"); - auto gidField = env->GetFieldID(cls, "gid", "I"); - auto groupsField = env->GetFieldID(cls, "groups", "Ljava/util/List;"); - auto capabilitiesField = env->GetFieldID(cls, "capabilities", "Ljava/util/List;"); - auto domainField = env->GetFieldID(cls, "context", "Ljava/lang/String;"); - auto namespacesField = env->GetFieldID(cls, "namespace", "I"); - - auto nonRootUseDefaultField = env->GetFieldID(cls, "nonRootUseDefault", "Z"); - auto umountModulesField = env->GetFieldID(cls, "umountModules", "Z"); - - auto key = env->GetObjectField(profile, keyField); - if (!key) { - return false; - } - if (env->GetStringLength((jstring) key) > KSU_MAX_PACKAGE_NAME) { - return false; - } - - auto cpkg = env->GetStringUTFChars((jstring) key, nullptr); - p_key_t p_key = {}; - strcpy(p_key, cpkg); - env->ReleaseStringUTFChars((jstring) key, cpkg); - - auto currentUid = env->GetIntField(profile, currentUidField); - - auto uid = env->GetIntField(profile, uidField); - auto gid = env->GetIntField(profile, gidField); - auto groups = env->GetObjectField(profile, groupsField); - auto capabilities = env->GetObjectField(profile, capabilitiesField); - auto domain = env->GetObjectField(profile, domainField); - auto allowSu = env->GetBooleanField(profile, allowSuField); - auto umountModules = env->GetBooleanField(profile, umountModulesField); - - app_profile p = {}; - p.version = KSU_APP_PROFILE_VER; - - strcpy(p.key, p_key); - p.allow_su = allowSu; - p.current_uid = currentUid; - - if (allowSu) { - p.rp_config.use_default = env->GetBooleanField(profile, rootUseDefaultField); - auto templateName = env->GetObjectField(profile, rootTemplateField); - if (templateName) { - auto ctemplateName = env->GetStringUTFChars((jstring) templateName, nullptr); - strcpy(p.rp_config.template_name, ctemplateName); - env->ReleaseStringUTFChars((jstring) templateName, ctemplateName); - } - - p.rp_config.profile.uid = uid; - p.rp_config.profile.gid = gid; - - int groups_count = getListSize(env, groups); - if (groups_count > KSU_MAX_GROUPS) { - LOGD("groups count too large: %d", groups_count); - return false; - } - p.rp_config.profile.groups_count = groups_count; - fillArrayWithList(env, groups, p.rp_config.profile.groups, groups_count); - - p.rp_config.profile.capabilities.effective = capListToBits(env, capabilities); - - auto cdomain = env->GetStringUTFChars((jstring) domain, nullptr); - strcpy(p.rp_config.profile.selinux_domain, cdomain); - env->ReleaseStringUTFChars((jstring) domain, cdomain); - - p.rp_config.profile.namespaces = env->GetIntField(profile, namespacesField); - } else { - p.nrp_config.use_default = env->GetBooleanField(profile, nonRootUseDefaultField); - p.nrp_config.profile.umount_modules = umountModules; - } - - return set_app_profile(&p); -} -extern "C" -JNIEXPORT jboolean JNICALL -Java_com_rifsxd_ksunext_Natives_uidShouldUmount(JNIEnv *env, jobject thiz, jint uid) { - return uid_should_umount(uid); -} -extern "C" -JNIEXPORT jboolean JNICALL -Java_com_rifsxd_ksunext_Natives_isSuEnabled(JNIEnv *env, jobject thiz) { - return is_su_enabled(); -} -extern "C" -JNIEXPORT jboolean JNICALL -Java_com_rifsxd_ksunext_Natives_setSuEnabled(JNIEnv *env, jobject thiz, jboolean enabled) { - return set_su_enabled(enabled); -} - -extern "C" -JNIEXPORT jboolean JNICALL -Java_com_rifsxd_ksunext_Natives_isZygiskEnabled(JNIEnv *env, jobject) { - return is_zygisk_enabled(); -} \ No newline at end of file diff --git a/manager/app/src/main/cpp/ksu.cc b/manager/app/src/main/cpp/ksu.cc deleted file mode 100644 index 4e55a068..00000000 --- a/manager/app/src/main/cpp/ksu.cc +++ /dev/null @@ -1,121 +0,0 @@ -// -// Created by weishu on 2022/12/9. -// - -#include -#include -#include -#include -#include -#include - -#include "ksu.h" - -#define KERNEL_SU_OPTION 0xDEADBEEF - -#define CMD_GRANT_ROOT 0 - -#define CMD_BECOME_MANAGER 1 -#define CMD_GET_VERSION 2 -#define CMD_ALLOW_SU 3 -#define CMD_DENY_SU 4 -#define CMD_GET_SU_LIST 5 -#define CMD_GET_DENY_LIST 6 -#define CMD_CHECK_SAFEMODE 9 - -#define CMD_GET_APP_PROFILE 10 -#define CMD_SET_APP_PROFILE 11 - -#define CMD_IS_UID_GRANTED_ROOT 12 -#define CMD_IS_UID_SHOULD_UMOUNT 13 -#define CMD_IS_SU_ENABLED 14 -#define CMD_ENABLE_SU 15 -#define CMD_GET_MANAGER_UID 16 - -#define CMD_HOOK_MODE 0xC0DEAD1A - -static bool ksuctl(int cmd, void* arg1, void* arg2) { - int32_t result = 0; - prctl(KERNEL_SU_OPTION, cmd, arg1, arg2, &result); - return result == KERNEL_SU_OPTION; -} - -bool become_manager(const char* pkg) { - char param[128]; - uid_t uid = getuid(); - uint32_t userId = uid / 100000; - if (userId == 0) { - sprintf(param, "/data/data/%s", pkg); - } else { - snprintf(param, sizeof(param), "/data/user/%d/%s", userId, pkg); - } - - return ksuctl(CMD_BECOME_MANAGER, param, nullptr); -} - -// cache the result to avoid unnecessary syscall -static bool is_lkm = false; - -int get_version(void) { - int32_t version = -1; - int32_t flags = 0; - ksuctl(CMD_GET_VERSION, &version, &flags); - if (!is_lkm && (flags & 0x1)) { - is_lkm = true; - } - return version; -} - -uid_t get_manager_uid() { - uid_t manager_uid = 0; - ksuctl(CMD_GET_MANAGER_UID, &manager_uid, nullptr); - return manager_uid; -} - -const char* get_hook_mode() { - static char mode[16]; - ksuctl(CMD_HOOK_MODE, mode, nullptr); - return mode; -} - - -bool get_allow_list(int *uids, int *size) { - return ksuctl(CMD_GET_SU_LIST, uids, size); -} - -bool is_safe_mode() { - return ksuctl(CMD_CHECK_SAFEMODE, nullptr, nullptr); -} - -bool is_lkm_mode() { - // you should call get_version first! - return is_lkm; -} - -bool uid_should_umount(int uid) { - bool should; - return ksuctl(CMD_IS_UID_SHOULD_UMOUNT, reinterpret_cast(uid), &should) && should; -} - -bool set_app_profile(const app_profile *profile) { - return ksuctl(CMD_SET_APP_PROFILE, (void*) profile, nullptr); -} - -bool get_app_profile(p_key_t key, app_profile *profile) { - return ksuctl(CMD_GET_APP_PROFILE, (void*) profile, nullptr); -} - -bool set_su_enabled(bool enabled) { - return ksuctl(CMD_ENABLE_SU, (void*) enabled, nullptr); -} - -bool is_su_enabled() { - bool enabled = true; - // if ksuctl failed, we assume su is enabled, and it cannot be disabled. - ksuctl(CMD_IS_SU_ENABLED, &enabled, nullptr); - return enabled; -} - -bool is_zygisk_enabled() { - return !!getenv("ZYGISK_ENABLED"); -} \ No newline at end of file diff --git a/manager/app/src/main/cpp/ksu.h b/manager/app/src/main/cpp/ksu.h deleted file mode 100644 index 1035937c..00000000 --- a/manager/app/src/main/cpp/ksu.h +++ /dev/null @@ -1,92 +0,0 @@ -// -// Created by weishu on 2022/12/9. -// - -#ifndef KERNELSU_KSU_H -#define KERNELSU_KSU_H - -#include - -bool become_manager(const char *); - -int get_version(); - -uid_t get_manager_uid(); - -const char* get_hook_mode(); - -bool get_allow_list(int *uids, int *size); - -bool uid_should_umount(int uid); - -bool is_safe_mode(); - -bool is_lkm_mode(); - -#define KSU_APP_PROFILE_VER 2 -#define KSU_MAX_PACKAGE_NAME 256 -// NGROUPS_MAX for Linux is 65535 generally, but we only supports 32 groups. -#define KSU_MAX_GROUPS 32 -#define KSU_SELINUX_DOMAIN 64 - -using p_key_t = char[KSU_MAX_PACKAGE_NAME]; - -struct root_profile { - int32_t uid; - int32_t gid; - - int32_t groups_count; - int32_t groups[KSU_MAX_GROUPS]; - - // kernel_cap_t is u32[2] for capabilities v3 - struct { - uint64_t effective; - uint64_t permitted; - uint64_t inheritable; - } capabilities; - - char selinux_domain[KSU_SELINUX_DOMAIN]; - - int32_t namespaces; -}; - -struct non_root_profile { - bool umount_modules; -}; - -struct app_profile { - // It may be utilized for backward compatibility, although we have never explicitly made any promises regarding this. - uint32_t version; - - // this is usually the package of the app, but can be other value for special apps - char key[KSU_MAX_PACKAGE_NAME]; - int32_t current_uid; - bool allow_su; - - union { - struct { - bool use_default; - char template_name[KSU_MAX_PACKAGE_NAME]; - - struct root_profile profile; - } rp_config; - - struct { - bool use_default; - - struct non_root_profile profile; - } nrp_config; - }; -}; - -bool set_app_profile(const app_profile *profile); - -bool get_app_profile(p_key_t key, app_profile *profile); - -bool set_su_enabled(bool enabled); - -bool is_su_enabled(); - -bool is_zygisk_enabled(); - -#endif //KERNELSU_KSU_H diff --git a/manager/app/src/main/java/com/rifsxd/ksunext/KernelSUApplication.kt b/manager/app/src/main/java/com/rifsxd/ksunext/KernelSUApplication.kt deleted file mode 100644 index 46b66a8d..00000000 --- a/manager/app/src/main/java/com/rifsxd/ksunext/KernelSUApplication.kt +++ /dev/null @@ -1,55 +0,0 @@ -package com.rifsxd.ksunext - -import android.app.Application -import android.system.Os -import coil.Coil -import coil.ImageLoader -import me.zhanghai.android.appiconloader.coil.AppIconFetcher -import me.zhanghai.android.appiconloader.coil.AppIconKeyer -import okhttp3.Cache -import okhttp3.OkHttpClient -import java.io.File -import java.util.Locale - -lateinit var ksuApp: KernelSUApplication - -class KernelSUApplication : Application() { - - lateinit var okhttpClient: OkHttpClient - - override fun onCreate() { - super.onCreate() - ksuApp = this - - val context = this - val iconSize = resources.getDimensionPixelSize(android.R.dimen.app_icon_size) - Coil.setImageLoader( - ImageLoader.Builder(context) - .components { - add(AppIconKeyer()) - add(AppIconFetcher.Factory(iconSize, false, context)) - } - .build() - ) - - val webroot = File(dataDir, "webroot") - if (!webroot.exists()) { - webroot.mkdir() - } - - // Provide working env for rust's temp_dir() - Os.setenv("TMPDIR", cacheDir.absolutePath, true) - - okhttpClient = - OkHttpClient.Builder().cache(Cache(File(cacheDir, "okhttp"), 10 * 1024 * 1024)) - .addInterceptor { block -> - block.proceed( - block.request().newBuilder() - .header("User-Agent", "KernelSU/${BuildConfig.VERSION_CODE}") - .header("Accept-Language", Locale.getDefault().toLanguageTag()).build() - ) - }.build() - } - - -} diff --git a/manager/app/src/main/java/com/rifsxd/ksunext/Kernels.kt b/manager/app/src/main/java/com/rifsxd/ksunext/Kernels.kt deleted file mode 100644 index 484334eb..00000000 --- a/manager/app/src/main/java/com/rifsxd/ksunext/Kernels.kt +++ /dev/null @@ -1,57 +0,0 @@ -package com.rifsxd.ksunext - -import android.system.Os - -/** - * @author weishu - * @date 2022/12/10. - */ - -data class KernelVersion(val major: Int, val patchLevel: Int, val subLevel: Int) { - override fun toString(): String { - return "$major.$patchLevel.$subLevel" - } - - fun isGKI(): Boolean { - - // kernel 6.x - if (major > 5) { - return true - } - - // kernel 5.10.x - if (major == 5) { - return patchLevel >= 10 - } - - return false - } - - fun isULegacy(): Boolean { - return major == 3 - } - - fun isLegacy(): Boolean { - return major == 4 && patchLevel in 1..18 - } - - fun isGKI1(): Boolean { - return (major == 4 && patchLevel >= 19) || (major == 5 && patchLevel < 10) - } - -} - -fun parseKernelVersion(version: String): KernelVersion { - val find = "(\\d+)\\.(\\d+)\\.(\\d+)".toRegex().find(version) - return if (find != null) { - KernelVersion(find.groupValues[1].toInt(), find.groupValues[2].toInt(), find.groupValues[3].toInt()) - } else { - KernelVersion(-1, -1, -1) - } -} - -fun getKernelVersion(): KernelVersion { - Os.uname().release.let { - return parseKernelVersion(it) - } -} \ No newline at end of file diff --git a/manager/app/src/main/java/com/rifsxd/ksunext/KsuService.java b/manager/app/src/main/java/com/rifsxd/ksunext/KsuService.java deleted file mode 100644 index 85e613b0..00000000 --- a/manager/app/src/main/java/com/rifsxd/ksunext/KsuService.java +++ /dev/null @@ -1,77 +0,0 @@ -package com.rifsxd.ksunext.ui; - -import android.content.Context; -import android.content.Intent; -import android.content.pm.PackageInfo; -import android.content.pm.PackageManager; -import android.os.IBinder; -import android.os.UserHandle; -import android.os.UserManager; -import android.util.Log; - -import androidx.annotation.NonNull; - -import com.topjohnwu.superuser.ipc.RootService; - -import java.lang.reflect.Method; -import java.util.ArrayList; -import java.util.List; - -import com.rifsxd.ksunext.IKsuInterface; -import rikka.parcelablelist.ParcelableListSlice; - -/** - * @author weishu - * @date 2023/4/18. - */ - -public class KsuService extends RootService { - - private static final String TAG = "KsuService"; - - class Stub extends IKsuInterface.Stub { - @Override - public ParcelableListSlice getPackages(int flags) { - List list = getInstalledPackagesAll(flags); - Log.i(TAG, "getPackages: " + list.size()); - return new ParcelableListSlice<>(list); - } - } - - @Override - public IBinder onBind(@NonNull Intent intent) { - return new Stub(); - } - - List getUserIds() { - List result = new ArrayList<>(); - UserManager um = (UserManager) getSystemService(Context.USER_SERVICE); - List userProfiles = um.getUserProfiles(); - for (UserHandle userProfile : userProfiles) { - int userId = userProfile.hashCode(); - result.add(userProfile.hashCode()); - } - return result; - } - - ArrayList getInstalledPackagesAll(int flags) { - ArrayList packages = new ArrayList<>(); - for (Integer userId : getUserIds()) { - Log.i(TAG, "getInstalledPackagesAll: " + userId); - packages.addAll(getInstalledPackagesAsUser(flags, userId)); - } - return packages; - } - - List getInstalledPackagesAsUser(int flags, int userId) { - try { - PackageManager pm = getPackageManager(); - Method getInstalledPackagesAsUser = pm.getClass().getDeclaredMethod("getInstalledPackagesAsUser", int.class, int.class); - return (List) getInstalledPackagesAsUser.invoke(pm, flags, userId); - } catch (Throwable e) { - Log.e(TAG, "err", e); - } - - return new ArrayList<>(); - } -} \ No newline at end of file diff --git a/manager/app/src/main/java/com/rifsxd/ksunext/Natives.kt b/manager/app/src/main/java/com/rifsxd/ksunext/Natives.kt deleted file mode 100644 index 5b6fbd18..00000000 --- a/manager/app/src/main/java/com/rifsxd/ksunext/Natives.kt +++ /dev/null @@ -1,157 +0,0 @@ -package com.rifsxd.ksunext - -import android.os.Parcelable -import androidx.annotation.Keep -import androidx.compose.runtime.Immutable -import kotlinx.parcelize.Parcelize - -/** - * @author weishu - * @date 2022/12/8. - */ -object Natives { - // minimal supported kernel version - // 10915: allowlist breaking change, add app profile - // 10931: app profile struct add 'version' field - // 10946: add capabilities - // 10977: change groups_count and groups to avoid overflow write - // 11071: Fix the issue of failing to set a custom SELinux type. - const val MINIMAL_SUPPORTED_KERNEL = 11071 - - // 11640: Support query working mode, LKM or GKI - // when MINIMAL_SUPPORTED_KERNEL > 11640, we can remove this constant. - const val MINIMAL_SUPPORTED_KERNEL_LKM = 11648 - - // 12404: Support disable sucompat mode - const val MINIMAL_SUPPORTED_SU_COMPAT = 12404 - - // 12569: support get hook mode - const val MINIMAL_SUPPORTED_HOOK_MODE = 12569 - - // 12750: support get manager UID - const val MINIMAL_SUPPORTED_MANAGER_UID = 12751 - - const val KERNEL_SU_DOMAIN = "u:r:su:s0" - - const val ROOT_UID = 0 - const val ROOT_GID = 0 - - init { - System.loadLibrary("kernelsu") - } - - // become root manager, return true if success. - external fun becomeManager(pkg: String?): Boolean - val version: Int - external get - - // get the uid list of allowed su processes. - val allowList: IntArray - external get - - val isSafeMode: Boolean - external get - - val isLkmMode: Boolean - external get - - external fun uidShouldUmount(uid: Int): Boolean - - /** - * Get the UID of the current root manager. - * @return manager UID, or 0 if unavailable. - */ - external fun getManagerUid(): Int - - /** - * Get a string indicating the SU hook mode enabled in kernel. - * The return values are: - * - "Manual": Manual hooks was enabled. - * - "Kprobes": Kprobes hooks was enabled (CONFIG_KSU_KPROBES_HOOK). - * - * @return return hook mode, or null if unavailable. - */ - external fun getHookMode(): String? - - /** - * Check if Zygisk injection is enabled in the environment. - */ - external fun isZygiskEnabled(): Boolean - - /** - * Get the profile of the given package. - * @param key usually the package name - * @return return null if failed. - */ - external fun getAppProfile(key: String?, uid: Int): Profile - external fun setAppProfile(profile: Profile?): Boolean - - /** - * `su` compat mode can be disabled temporarily. - * 0: disabled - * 1: enabled - * negative : error - */ - external fun isSuEnabled(): Boolean - external fun setSuEnabled(enabled: Boolean): Boolean - - private const val NON_ROOT_DEFAULT_PROFILE_KEY = "$" - private const val NOBODY_UID = 9999 - - fun setDefaultUmountModules(umountModules: Boolean): Boolean { - Profile( - NON_ROOT_DEFAULT_PROFILE_KEY, - NOBODY_UID, - false, - umountModules = umountModules - ).let { - return setAppProfile(it) - } - } - - fun isDefaultUmountModules(): Boolean { - getAppProfile(NON_ROOT_DEFAULT_PROFILE_KEY, NOBODY_UID).let { - return it.umountModules - } - } - - fun requireNewKernel(): Boolean { - return version < MINIMAL_SUPPORTED_KERNEL - } - - @Immutable - @Parcelize - @Keep - data class Profile( - // and there is a default profile for root and non-root - val name: String, - // current uid for the package, this is convivent for kernel to check - // if the package name doesn't match uid, then it should be invalidated. - val currentUid: Int = 0, - - // if this is true, kernel will grant root permission to this package - val allowSu: Boolean = false, - - // these are used for root profile - val rootUseDefault: Boolean = true, - val rootTemplate: String? = null, - val uid: Int = ROOT_UID, - val gid: Int = ROOT_GID, - val groups: List = mutableListOf(), - val capabilities: List = mutableListOf(), - val context: String = KERNEL_SU_DOMAIN, - val namespace: Int = Namespace.INHERITED.ordinal, - - val nonRootUseDefault: Boolean = true, - val umountModules: Boolean = true, - var rules: String = "", // this field is save in ksud!! - ) : Parcelable { - enum class Namespace { - INHERITED, - GLOBAL, - INDIVIDUAL, - } - - constructor() : this("") - } -} \ No newline at end of file diff --git a/manager/app/src/main/java/com/rifsxd/ksunext/profile/Capabilities.kt b/manager/app/src/main/java/com/rifsxd/ksunext/profile/Capabilities.kt deleted file mode 100644 index 9eb98a12..00000000 --- a/manager/app/src/main/java/com/rifsxd/ksunext/profile/Capabilities.kt +++ /dev/null @@ -1,49 +0,0 @@ -package com.rifsxd.ksunext.profile - -/** - * @author weishu - * @date 2023/6/3. - */ -enum class Capabilities(val cap: Int, val display: String, val desc: String) { - CAP_CHOWN(0, "CHOWN", "Make arbitrary changes to file UIDs and GIDs (see chown(2))"), - CAP_DAC_OVERRIDE(1, "DAC_OVERRIDE", "Bypass file read, write, and execute permission checks"), - CAP_DAC_READ_SEARCH(2, "DAC_READ_SEARCH", "Bypass file read permission checks and directory read and execute permission checks"), - CAP_FOWNER(3, "FOWNER", "Bypass permission checks on operations that normally require the filesystem UID of the process to match the UID of the file (e.g., chmod(2), utime(2)), excluding those operations covered by CAP_DAC_OVERRIDE and CAP_DAC_READ_SEARCH"), - CAP_FSETID(4, "FSETID", "Don’t clear set-user-ID and set-group-ID permission bits when a file is modified; set the set-group-ID bit for a file whose GID does not match the filesystem or any of the supplementary GIDs of the calling process"), - CAP_KILL(5, "KILL", "Bypass permission checks for sending signals (see kill(2))."), - CAP_SETGID(6, "SETGID", "Make arbitrary manipulations of process GIDs and supplementary GID list; allow setgid(2) manipulation of the caller’s effective and real group IDs"), - CAP_SETUID(7, "SETUID", "Make arbitrary manipulations of process UIDs (setuid(2), setreuid(2), setresuid(2), setfsuid(2)); allow changing the current process user IDs; allow changing of the current process group ID to any value in the system’s range of legal group IDs"), - CAP_SETPCAP(8, "SETPCAP", "If file capabilities are supported: grant or remove any capability in the caller’s permitted capability set to or from any other process. (This property supersedes the obsolete notion of giving a process all capabilities by granting all capabilities in its permitted set, and of removing all capabilities from a process by granting no capabilities in its permitted set. It does not permit any actions that were not permitted before.)"), - CAP_LINUX_IMMUTABLE(9, "LINUX_IMMUTABLE", "Set the FS_APPEND_FL and FS_IMMUTABLE_FL inode flags (see chattr(1))."), - CAP_NET_BIND_SERVICE(10, "NET_BIND_SERVICE", "Bind a socket to Internet domain"), - CAP_NET_BROADCAST(11, "NET_BROADCAST", "Make socket broadcasts, and listen to multicasts"), - CAP_NET_ADMIN(12, "NET_ADMIN", "Perform various network-related operations: interface configuration, administration of IP firewall, masquerading, and accounting, modify routing tables, bind to any address for transparent proxying, set type-of-service (TOS), clear driver statistics, set promiscuous mode, enabling multicasting, use setsockopt(2) to set the following socket options: SO_DEBUG, SO_MARK, SO_PRIORITY (for a priority outside the range 0 to 6), SO_RCVBUFFORCE, and SO_SNDBUFFORCE"), - CAP_NET_RAW(13, "NET_RAW", "Use RAW and PACKET sockets"), - CAP_IPC_LOCK(14, "IPC_LOCK", "Lock memory (mlock(2), mlockall(2), mmap(2), shmctl(2))"), - CAP_IPC_OWNER(15, "IPC_OWNER", "Bypass permission checks for operations on System V IPC objects"), - CAP_SYS_MODULE(16, "SYS_MODULE", "Load and unload kernel modules (see init_module(2) and delete_module(2)); in kernels before 2.6.25, this also granted rights for various other operations related to kernel modules"), - CAP_SYS_RAWIO(17, "SYS_RAWIO", "Perform I/O port operations (iopl(2) and ioperm(2)); access /proc/kcore"), - CAP_SYS_CHROOT(18, "SYS_CHROOT", "Use chroot(2)"), - CAP_SYS_PTRACE(19, "SYS_PTRACE", "Trace arbitrary processes using ptrace(2)"), - CAP_SYS_PACCT(20, "SYS_PACCT", "Use acct(2)"), - CAP_SYS_ADMIN(21, "SYS_ADMIN", "Perform a range of system administration operations including: quotactl(2), mount(2), umount(2), swapon(2), swapoff(2), sethostname(2), and setdomainname(2); set and modify process resource limits (setrlimit(2)); perform various network-related operations (e.g., setting privileged socket options, enabling multicasting, interface configuration); perform various IPC operations (e.g., SysV semaphores, POSIX message queues, System V shared memory); allow reboot and kexec_load(2); override /proc/sys kernel tunables; perform ptrace(2) PTRACE_SECCOMP_GET_FILTER operation; perform some tracing and debugging operations (see ptrace(2)); administer the lifetime of kernel tracepoints (tracefs(5)); perform the KEYCTL_CHOWN and KEYCTL_SETPERM keyctl(2) operations; perform the following keyctl(2) operations: KEYCTL_CAPABILITIES, KEYCTL_CAPSQUASH, and KEYCTL_PKEY_ OPERATIONS; set state for the Extensible Authentication Protocol (EAP) kernel module; and override the RLIMIT_NPROC resource limit; allow ioperm/iopl access to I/O ports"), - CAP_SYS_BOOT(22, "SYS_BOOT", "Use reboot(2) and kexec_load(2), reboot and load a new kernel for later execution"), - CAP_SYS_NICE(23, "SYS_NICE", "Raise process nice value (nice(2), setpriority(2)) and change the nice value for arbitrary processes; set real-time scheduling policies for calling process, and set scheduling policies and priorities for arbitrary processes (sched_setscheduler(2), sched_setparam(2)"), - CAP_SYS_RESOURCE(24, "SYS_RESOURCE", "Override resource Limits. Set resource limits (setrlimit(2), prlimit(2)), override quota limits (quota(2), quotactl(2)), override reserved space on ext2 filesystem (ext2_ioctl(2)), override size restrictions on IPC message queues (msg(2)) and system V shared memory segments (shmget(2)), and override the /proc/sys/fs/pipe-size-max limit"), - CAP_SYS_TIME(25, "SYS_TIME", "Set system clock (settimeofday(2), stime(2), adjtimex(2)); set real-time (hardware) clock"), - CAP_SYS_TTY_CONFIG(26, "SYS_TTY_CONFIG", "Use vhangup(2); employ various privileged ioctl(2) operations on virtual terminals"), - CAP_MKNOD(27, "MKNOD", "Create special files using mknod(2)"), - CAP_LEASE(28, "LEASE", "Establish leases on arbitrary files (see fcntl(2))"), - CAP_AUDIT_WRITE(29, "AUDIT_WRITE", "Write records to kernel auditing log"), - CAP_AUDIT_CONTROL(30, "AUDIT_CONTROL", "Enable and disable kernel auditing; change auditing filter rules; retrieve auditing status and filtering rules"), - CAP_SETFCAP(31, "SETFCAP", "If file capabilities are supported: grant or remove any capability in any capability set to any file"), - CAP_MAC_OVERRIDE(32, "MAC_OVERRIDE", "Override Mandatory Access Control (MAC). Implemented for the Smack Linux Security Module (LSM)"), - CAP_MAC_ADMIN(33, "MAC_ADMIN", "Allow MAC configuration or state changes. Implemented for the Smack LSM"), - CAP_SYSLOG(34, "SYSLOG", "Perform privileged syslog(2) operations. See syslog(2) for information on which operations require privilege"), - CAP_WAKE_ALARM(35, "WAKE_ALARM", "Trigger something that will wake up the system"), - CAP_BLOCK_SUSPEND(36, "BLOCK_SUSPEND", "Employ features that can block system suspend"), - CAP_AUDIT_READ(37, "AUDIT_READ", "Allow reading the audit log via a multicast netlink socket"), - CAP_PERFMON(38, "PERFMON", "Allow performance monitoring via perf_event_open(2)"), - CAP_BPF(39, "BPF", "Allow BPF operations via bpf(2)"), - CAP_CHECKPOINT_RESTORE(40, "CHECKPOINT_RESTORE", "Allow processes to be checkpointed via checkpoint/restore in user namespace(2)"), -} \ No newline at end of file diff --git a/manager/app/src/main/java/com/rifsxd/ksunext/profile/Groups.kt b/manager/app/src/main/java/com/rifsxd/ksunext/profile/Groups.kt deleted file mode 100644 index b1bbfd81..00000000 --- a/manager/app/src/main/java/com/rifsxd/ksunext/profile/Groups.kt +++ /dev/null @@ -1,130 +0,0 @@ -package com.rifsxd.ksunext.profile - -/** - * https://cs.android.com/android/platform/superproject/main/+/main:system/core/libcutils/include/private/android_filesystem_config.h - * @author weishu - * @date 2023/6/3. - */ -enum class Groups(val gid: Int, val display: String, val desc: String) { - ROOT(0, "root", "traditional unix root user"), - DAEMON(1, "daemon", "Traditional unix daemon owner."), - BIN(2, "bin", "Traditional unix binaries owner."), - SYS(3, "sys", "A group with the same gid on Linux/macOS/Android."), - SYSTEM(1000, "system", "system server"), - RADIO(1001, "radio", "telephony subsystem, RIL"), - BLUETOOTH(1002, "bluetooth", "bluetooth subsystem"), - GRAPHICS(1003, "graphics", "graphics devices"), - INPUT(1004, "input", "input devices"), - AUDIO(1005, "audio", "audio devices"), - CAMERA(1006, "camera", "camera devices"), - LOG(1007, "log", "log devices"), - COMPASS(1008, "compass", "compass device"), - MOUNT(1009, "mount", "mountd socket"), - WIFI(1010, "wifi", "wifi subsystem"), - ADB(1011, "adb", "android debug bridge (adbd)"), - INSTALL(1012, "install", "group for installing packages"), - MEDIA(1013, "media", "mediaserver process"), - DHCP(1014, "dhcp", "dhcp client"), - SDCARD_RW(1015, "sdcard_rw", "external storage write access"), - VPN(1016, "vpn", "vpn system"), - KEYSTORE(1017, "keystore", "keystore subsystem"), - USB(1018, "usb", "USB devices"), - DRM(1019, "drm", "DRM server"), - MDNSR(1020, "mdnsr", "MulticastDNSResponder (service discovery)"), - GPS(1021, "gps", "GPS daemon"), - UNUSED1(1022, "unused1", "deprecated, DO NOT USE"), - MEDIA_RW(1023, "media_rw", "internal media storage write access"), - MTP(1024, "mtp", "MTP USB driver access"), - UNUSED2(1025, "unused2", "deprecated, DO NOT USE"), - DRMRPC(1026, "drmrpc", "group for drm rpc"), - NFC(1027, "nfc", "nfc subsystem"), - SDCARD_R(1028, "sdcard_r", "external storage read access"), - CLAT(1029, "clat", "clat part of nat464"), - LOOP_RADIO(1030, "loop_radio", "loop radio devices"), - MEDIA_DRM(1031, "media_drm", "MediaDrm plugins"), - PACKAGE_INFO(1032, "package_info", "access to installed package details"), - SDCARD_PICS(1033, "sdcard_pics", "external storage photos access"), - SDCARD_AV(1034, "sdcard_av", "external storage audio/video access"), - SDCARD_ALL(1035, "sdcard_all", "access all users external storage"), - LOGD(1036, "logd", "log daemon"), - SHARED_RELRO(1037, "shared_relro", "creator of shared GNU RELRO files"), - DBUS(1038, "dbus", "dbus-daemon IPC broker process"), - TLSDATE(1039, "tlsdate", "tlsdate unprivileged user"), - MEDIA_EX(1040, "media_ex", "mediaextractor process"), - AUDIOSERVER(1041, "audioserver", "audioserver process"), - METRICS_COLL(1042, "metrics_coll", "metrics_collector process"), - METRICSD(1043, "metricsd", "metricsd process"), - WEBSERV(1044, "webserv", "webservd process"), - DEBUGGERD(1045, "debuggerd", "debuggerd unprivileged user"), - MEDIA_CODEC(1046, "media_codec", "media_codec process"), - CAMERASERVER(1047, "cameraserver", "cameraserver process"), - FIREWALL(1048, "firewall", "firewall process"), - TRUNKS(1049, "trunks", "trunksd process"), - NVRAM(1050, "nvram", "nvram daemon"), - DNS(1051, "dns", "DNS resolution daemon (system: netd)"), - DNS_TETHER(1052, "dns_tether", "DNS resolution daemon (tether: dnsmasq)"), - WEBVIEW_ZYGOTE(1053, "webview_zygote", "WebView zygote process"), - VEHICLE_NETWORK(1054, "vehicle_network", "Vehicle network service"), - MEDIA_AUDIO(1055, "media_audio", "GID for audio files on internal media storage"), - MEDIA_VIDEO(1056, "media_video", "GID for video files on internal media storage"), - MEDIA_IMAGE(1057, "media_image", "GID for image files on internal media storage"), - TOMBSTONED(1058, "tombstoned", "tombstoned user"), - MEDIA_OBB(1059, "media_obb", "GID for OBB files on internal media storage"), - ESE(1060, "ese", "embedded secure element (eSE) subsystem"), - OTA_UPDATE(1061, "ota_update", "resource tracking UID for OTA updates"), - AUTOMOTIVE_EVS(1062, "automotive_evs", "Automotive rear and surround view system"), - LOWPAN(1063, "lowpan", "LoWPAN subsystem"), - HSM(1064, "lowpan", "hardware security module subsystem"), - RESERVED_DISK(1065, "reserved_disk", "GID that has access to reserved disk space"), - STATSD(1066, "statsd", "statsd daemon"), - INCIDENTD(1067, "incidentd", "incidentd daemon"), - SECURE_ELEMENT(1068, "secure_element", "secure element subsystem"), - LMKD(1069, "lmkd", "low memory killer daemon"), - LLKD(1070, "llkd", "live lock daemon"), - IORAPD(1071, "iorapd", "input/output readahead and pin daemon"), - GPU_SERVICE(1072, "gpu_service", "GPU service daemon"), - NETWORK_STACK(1073, "network_stack", "network stack service"), - GSID(1074, "GSID", "GSI service daemon"), - FSVERITY_CERT(1075, "fsverity_cert", "fs-verity key ownership in keystore"), - CREDSTORE(1076, "credstore", "identity credential manager service"), - EXTERNAL_STORAGE(1077, "external_storage", "Full external storage access including USB OTG volumes"), - EXT_DATA_RW(1078, "ext_data_rw", "GID for app-private data directories on external storage"), - EXT_OBB_RW(1079, "ext_obb_rw", "GID for OBB directories on external storage"), - CONTEXT_HUB(1080, "context_hub", "GID for access to the Context Hub"), - VIRTUALIZATIONSERVICE(1081, "virtualizationservice", "VirtualizationService daemon"), - ARTD(1082, "artd", "ART Service daemon"), - UWB(1083, "uwb", "UWB subsystem"), - THREAD_NETWORK(1084, "thread_network", "Thread Network subsystem"), - DICED(1085, "diced", "Android's DICE daemon"), - DMESGD(1086, "dmesgd", "dmesg parsing daemon for kernel report collection"), - JC_WEAVER(1087, "jc_weaver", "Javacard Weaver HAL - to manage omapi ARA rules"), - JC_STRONGBOX(1088, "jc_strongbox", "Javacard Strongbox HAL - to manage omapi ARA rules"), - JC_IDENTITYCRED(1089, "jc_identitycred", "Javacard Identity Cred HAL - to manage omapi ARA rules"), - SDK_SANDBOX(1090, "sdk_sandbox", "SDK sandbox virtual UID"), - SECURITY_LOG_WRITER(1091, "security_log_writer", "write to security log"), - PRNG_SEEDER(1092, "prng_seeder", "PRNG seeder daemon"), - - SHELL(2000, "shell", "adb and debug shell user"), - CACHE(2001, "cache", "cache access"), - DIAG(2002, "diag", "access to diagnostic resources"), - - /* The 3000 series are intended for use as supplemental group id's only. - * They indicate special Android capabilities that the kernel is aware of. */ - NET_BT_ADMIN(3001, "net_bt_admin", "bluetooth: create any socket"), - NET_BT(3002, "net_bt", "bluetooth: create sco, rfcomm or l2cap sockets"), - INET(3003, "inet", "can create AF_INET and AF_INET6 sockets"), - NET_RAW(3004, "net_raw", "can create raw INET sockets"), - NET_ADMIN(3005, "net_admin", "can configure interfaces and routing tables."), - NET_BW_STATS(3006, "net_bw_stats", "read bandwidth statistics"), - NET_BW_ACCT(3007, "net_bw_acct", "change bandwidth statistics accounting"), - NET_BT_STACK(3008, "net_bt_stack", "access to various bluetooth management functions"), - READPROC(3009, "readproc", "Allow /proc read access"), - WAKELOCK(3010, "wakelock", "Allow system wakelock read/write access"), - UHID(3011, "uhid", "Allow read/write to /dev/uhid node"), - READTRACEFS(3012, "readtracefs", "Allow tracefs read"), - - EVERYBODY(9997, "everybody", "Shared external storage read/write"), - MISC(9998, "misc", "Access to misc storage"), - NOBODY(9999, "nobody", "Reserved"), - APP(10000, "app", "Access to app data"), -} \ No newline at end of file diff --git a/manager/app/src/main/java/com/rifsxd/ksunext/ui/MainActivity.kt b/manager/app/src/main/java/com/rifsxd/ksunext/ui/MainActivity.kt deleted file mode 100644 index 86f14fba..00000000 --- a/manager/app/src/main/java/com/rifsxd/ksunext/ui/MainActivity.kt +++ /dev/null @@ -1,248 +0,0 @@ -package com.rifsxd.ksunext.ui - -import android.content.Intent -import android.net.Uri -import android.content.Context -import android.os.Build -import android.os.Bundle -import androidx.activity.ComponentActivity -import androidx.activity.compose.setContent -import androidx.activity.enableEdgeToEdge -import androidx.compose.animation.AnimatedVisibility -import androidx.compose.animation.slideInVertically -import androidx.compose.animation.slideOutVertically -import androidx.compose.animation.AnimatedContentTransitionScope -import androidx.compose.animation.EnterTransition -import androidx.compose.animation.ExitTransition -import androidx.compose.animation.core.tween -import androidx.compose.animation.fadeIn -import androidx.compose.animation.fadeOut -import androidx.compose.foundation.layout.WindowInsets -import androidx.compose.foundation.layout.WindowInsetsSides -import androidx.compose.foundation.layout.displayCutout -import androidx.compose.foundation.layout.only -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.systemBars -import androidx.compose.foundation.layout.union -import androidx.compose.material3.Badge -import androidx.compose.material3.BadgedBox -import androidx.compose.material3.Icon -import androidx.compose.material3.NavigationBar -import androidx.compose.material3.NavigationBarItem -import androidx.compose.material3.Scaffold -import androidx.compose.material3.SnackbarHostState -import androidx.compose.material3.Text -import androidx.compose.runtime.Composable -import androidx.compose.runtime.CompositionLocalProvider -import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.getValue -import androidx.compose.runtime.remember -import androidx.compose.ui.Modifier -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.unit.dp -import androidx.lifecycle.viewmodel.compose.viewModel -import androidx.navigation.NavBackStackEntry -import androidx.navigation.NavHostController -import androidx.navigation.compose.rememberNavController -import androidx.navigation.compose.currentBackStackEntryAsState -import com.ramcosta.composedestinations.DestinationsNavHost -import com.ramcosta.composedestinations.animations.NavHostAnimatedDestinationStyle -import com.ramcosta.composedestinations.generated.destinations.ExecuteModuleActionScreenDestination -import com.ramcosta.composedestinations.generated.destinations.FlashScreenDestination -import com.ramcosta.composedestinations.generated.NavGraphs -import com.ramcosta.composedestinations.utils.isRouteOnBackStackAsState -import com.ramcosta.composedestinations.utils.rememberDestinationsNavigator -import com.rifsxd.ksunext.Natives -import com.rifsxd.ksunext.ksuApp -import com.rifsxd.ksunext.ui.screen.BottomBarDestination -import com.rifsxd.ksunext.ui.theme.KernelSUTheme -import com.rifsxd.ksunext.ui.util.* -import com.rifsxd.ksunext.ui.util.LocalSnackbarHost -import com.rifsxd.ksunext.ui.util.LocaleHelper -import com.rifsxd.ksunext.ui.util.rootAvailable -import com.rifsxd.ksunext.ui.util.install -import com.rifsxd.ksunext.ui.util.isSuCompatDisabled -import com.rifsxd.ksunext.ui.screen.FlashIt -import com.rifsxd.ksunext.ui.viewmodel.ModuleViewModel -import com.rifsxd.ksunext.ui.viewmodel.SuperUserViewModel - -class MainActivity : ComponentActivity() { - - override fun attachBaseContext(newBase: Context?) { - super.attachBaseContext(newBase?.let { LocaleHelper.applyLanguage(it) }) - } - - override fun onCreate(savedInstanceState: Bundle?) { - - // Enable edge to edge - enableEdgeToEdge() - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { - window.isNavigationBarContrastEnforced = false - } - - super.onCreate(savedInstanceState) - - val isManager = Natives.becomeManager(ksuApp.packageName) - if (isManager) install() - - val zipUri: Uri? = when (intent?.action) { - Intent.ACTION_VIEW, Intent.ACTION_SEND -> { - val uri = intent.data ?: intent.getParcelableExtra(Intent.EXTRA_STREAM) - uri?.let { - val name = when (it.scheme) { - "file" -> it.lastPathSegment ?: "" - "content" -> { - contentResolver.query(it, null, null, null, null)?.use { cursor -> - val nameIndex = cursor.getColumnIndex(android.provider.OpenableColumns.DISPLAY_NAME) - if (cursor.moveToFirst() && nameIndex != -1) { - cursor.getString(nameIndex) - } else { - it.lastPathSegment ?: "" - } - } ?: (it.lastPathSegment ?: "") - } - else -> it.lastPathSegment ?: "" - } - if (name.lowercase().endsWith(".zip")) it else null - } - } - else -> null - } - - setContent { - // Read AMOLED mode preference - val prefs = getSharedPreferences("settings", Context.MODE_PRIVATE) - val amoledMode = prefs.getBoolean("enable_amoled", false) - - val moduleViewModel: ModuleViewModel = viewModel() - val superUserViewModel: SuperUserViewModel = viewModel() - val moduleUpdateCount = moduleViewModel.moduleList.count { - moduleViewModel.checkUpdate(it).first.isNotEmpty() - } - - KernelSUTheme ( - amoledMode = amoledMode - ) { - val navController = rememberNavController() - val snackBarHostState = remember { SnackbarHostState() } - val currentDestination = navController.currentBackStackEntryAsState()?.value?.destination - - val navigator = navController.rememberDestinationsNavigator() - - LaunchedEffect(zipUri) { - if (zipUri != null) { - navigator.navigate( - FlashScreenDestination( - FlashIt.FlashModules(listOf(zipUri)), - finishIntent = true - ) - ) - } - } - - LaunchedEffect(Unit) { - if (superUserViewModel.appList.isEmpty()) { - superUserViewModel.fetchAppList() - } - - if (moduleViewModel.moduleList.isEmpty()) { - moduleViewModel.fetchModuleList() - } - } - - val showBottomBar = when (currentDestination?.route) { - FlashScreenDestination.route -> false // Hide for FlashScreenDestination - ExecuteModuleActionScreenDestination.route -> false // Hide for ExecuteModuleActionScreen - else -> true - } - - Scaffold( - bottomBar = { - AnimatedVisibility( - visible = showBottomBar, - enter = slideInVertically(initialOffsetY = { it }) + fadeIn(), - exit = slideOutVertically(targetOffsetY = { it }) + fadeOut() - ) { - BottomBar(navController, moduleUpdateCount) - } - }, - contentWindowInsets = WindowInsets(0, 0, 0, 0) - ) { innerPadding -> - CompositionLocalProvider( - LocalSnackbarHost provides snackBarHostState, - ) { - DestinationsNavHost( - modifier = Modifier.padding(innerPadding), - navGraph = NavGraphs.root, - 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)) } - } - ) - } - } - } - } - } -} - -@Composable -private fun BottomBar(navController: NavHostController, moduleUpdateCount: Int) { - val navigator = navController.rememberDestinationsNavigator() - val isManager = Natives.becomeManager(ksuApp.packageName) - val fullFeatured = isManager && !Natives.requireNewKernel() && rootAvailable() - val suCompatDisabled = isSuCompatDisabled() - val suSFS = getSuSFS() - val susSUMode = susfsSUS_SU_Mode() - - NavigationBar( - tonalElevation = 8.dp, - windowInsets = WindowInsets.systemBars.union(WindowInsets.displayCutout).only( - WindowInsetsSides.Horizontal + WindowInsetsSides.Bottom - ) - ) { - BottomBarDestination.entries - .forEach { destination -> - if (!fullFeatured && destination.rootRequired) return@forEach - val isCurrentDestOnBackStack by navController.isRouteOnBackStackAsState(destination.direction) - NavigationBarItem( - selected = isCurrentDestOnBackStack, - onClick = { - if (isCurrentDestOnBackStack) { - navigator.popBackStack(destination.direction, false) - } - navigator.navigate(destination.direction) { - popUpTo(NavGraphs.root) { - saveState = true - } - launchSingleTop = true - restoreState = true - } - }, - icon = { - // Show badge for Module icon if moduleUpdateCount > 0 - if (destination == BottomBarDestination.Module && moduleUpdateCount > 0) { - BadgedBox(badge = { Badge { Text(moduleUpdateCount.toString()) } }) { - if (isCurrentDestOnBackStack) { - Icon(destination.iconSelected, stringResource(destination.label)) - } else { - Icon(destination.iconNotSelected, stringResource(destination.label)) - } - } - } else { - if (isCurrentDestOnBackStack) { - Icon(destination.iconSelected, stringResource(destination.label)) - } else { - Icon(destination.iconNotSelected, stringResource(destination.label)) - } - } - }, - label = { Text(stringResource(destination.label)) }, - alwaysShowLabel = true - ) - } - } -} diff --git a/manager/app/src/main/java/com/rifsxd/ksunext/ui/component/AboutCard.kt b/manager/app/src/main/java/com/rifsxd/ksunext/ui/component/AboutCard.kt deleted file mode 100644 index 020dd5d1..00000000 --- a/manager/app/src/main/java/com/rifsxd/ksunext/ui/component/AboutCard.kt +++ /dev/null @@ -1,126 +0,0 @@ -package com.rifsxd.ksunext.ui.component - -import androidx.compose.foundation.Image -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.fillMaxWidth -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.MaterialTheme -import androidx.compose.material3.Surface -import androidx.compose.material3.Text -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.scale -import androidx.compose.ui.res.colorResource -import androidx.compose.ui.res.painterResource -import androidx.compose.ui.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.window.Dialog -import androidx.compose.ui.text.font.FontWeight -import com.rifsxd.ksunext.BuildConfig -import com.rifsxd.ksunext.R - -@Preview -@Composable -fun AboutCard() { - ElevatedCard( - modifier = Modifier.fillMaxWidth(), - shape = RoundedCornerShape(8.dp) - ) { - Row( - modifier = Modifier - .fillMaxWidth() - .padding(24.dp) - ) { - AboutCardContent() - } - } -} - -@Composable -fun AboutDialog(dismiss: () -> Unit) { - Dialog( - onDismissRequest = { dismiss() } - ) { - AboutCard() - } -} - -@Composable -private fun AboutCardContent() { - Column( - modifier = Modifier.fillMaxWidth() - ) { - Row { - Surface( - modifier = Modifier.size(40.dp), - color = colorResource(id = R.color.ic_launcher_background), - shape = CircleShape - ) { - Image( - painter = painterResource(id = R.mipmap.ic_launcher_foreground), - contentDescription = "icon", - modifier = Modifier.scale(1.5f) - ) - } - - Spacer(modifier = Modifier.width(12.dp)) - - Column { - - Text( - text = stringResource(id = R.string.app_name), - style = MaterialTheme.typography.titleMedium, - fontWeight = FontWeight.SemiBold, - fontSize = 18.sp - ) - Text( - BuildConfig.VERSION_NAME, - style = MaterialTheme.typography.bodySmall, - fontSize = 14.sp - ) - - Spacer(modifier = Modifier.height(8.dp)) - - val annotatedString = AnnotatedString.Companion.fromHtml( - htmlString = stringResource( - id = R.string.about_source_code, - "GitHub" - ), - 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 - ) - ) - } - } - } -} diff --git a/manager/app/src/main/java/com/rifsxd/ksunext/ui/component/Dialog.kt b/manager/app/src/main/java/com/rifsxd/ksunext/ui/component/Dialog.kt deleted file mode 100644 index dd3c059d..00000000 --- a/manager/app/src/main/java/com/rifsxd/ksunext/ui/component/Dialog.kt +++ /dev/null @@ -1,458 +0,0 @@ -package com.rifsxd.ksunext.ui.component - -import android.graphics.text.LineBreaker -import android.os.Build -import android.os.Parcelable -import android.text.Layout -import android.text.method.LinkMovementMethod -import android.util.Log -import android.view.ViewGroup -import android.widget.TextView -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.size -import androidx.compose.foundation.layout.wrapContentHeight -import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material3.* -import androidx.compose.runtime.* -import androidx.compose.runtime.saveable.Saver -import androidx.compose.runtime.saveable.rememberSaveable -import androidx.compose.ui.text.font.FontWeight -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.toArgb -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.unit.dp -import androidx.compose.ui.viewinterop.AndroidView -import androidx.compose.ui.window.Dialog -import androidx.compose.ui.window.DialogProperties -import io.noties.markwon.Markwon -import io.noties.markwon.utils.NoCopySpannableFactory -import kotlinx.coroutines.* -import kotlinx.coroutines.channels.Channel -import kotlinx.coroutines.channels.ReceiveChannel -import kotlinx.coroutines.flow.FlowCollector -import kotlinx.coroutines.flow.consumeAsFlow -import kotlinx.coroutines.flow.onEach -import kotlinx.parcelize.Parcelize -import kotlin.coroutines.resume - -private const val TAG = "DialogComponent" - -interface ConfirmDialogVisuals : Parcelable { - val title: String - val content: String - val isMarkdown: Boolean - val confirm: String? - val dismiss: String? -} - -@Parcelize -private data class ConfirmDialogVisualsImpl( - override val title: String, - override val content: String, - override val isMarkdown: Boolean, - override val confirm: String?, - override val dismiss: String?, -) : ConfirmDialogVisuals { - companion object { - val Empty: ConfirmDialogVisuals = ConfirmDialogVisualsImpl("", "", false, null, null) - } -} - -interface DialogHandle { - val isShown: Boolean - val dialogType: String - fun show() - fun hide() -} - -interface LoadingDialogHandle : DialogHandle { - suspend fun withLoading(block: suspend () -> R): R - fun showLoading() -} - -sealed interface ConfirmResult { - object Confirmed : ConfirmResult - object Canceled : ConfirmResult -} - -interface ConfirmDialogHandle : DialogHandle { - val visuals: ConfirmDialogVisuals - - fun showConfirm( - title: String, - content: String, - markdown: Boolean = false, - confirm: String? = null, - dismiss: String? = null - ) - - suspend fun awaitConfirm( - title: String, - content: String, - markdown: Boolean = false, - confirm: String? = null, - dismiss: String? = null - ): ConfirmResult -} - -private abstract class DialogHandleBase( - val visible: MutableState, - val coroutineScope: CoroutineScope -) : DialogHandle { - override val isShown: Boolean - get() = visible.value - - override fun show() { - coroutineScope.launch { - visible.value = true - } - } - - final override fun hide() { - coroutineScope.launch { - visible.value = false - } - } - - override fun toString(): String { - return dialogType - } -} - -private class LoadingDialogHandleImpl( - visible: MutableState, - coroutineScope: CoroutineScope -) : LoadingDialogHandle, DialogHandleBase(visible, coroutineScope) { - override suspend fun withLoading(block: suspend () -> R): R { - return coroutineScope.async { - try { - visible.value = true - block() - } finally { - visible.value = false - } - }.await() - } - - override fun showLoading() { - show() - } - - override val dialogType: String get() = "LoadingDialog" -} - -typealias NullableCallback = (() -> Unit)? - -interface ConfirmCallback { - - val onConfirm: NullableCallback - - val onDismiss: NullableCallback - - val isEmpty: Boolean get() = onConfirm == null && onDismiss == null - - companion object { - operator fun invoke(onConfirmProvider: () -> NullableCallback, onDismissProvider: () -> NullableCallback): ConfirmCallback { - return object : ConfirmCallback { - override val onConfirm: NullableCallback - get() = onConfirmProvider() - override val onDismiss: NullableCallback - get() = onDismissProvider() - } - } - } -} - -private class ConfirmDialogHandleImpl( - visible: MutableState, - coroutineScope: CoroutineScope, - callback: ConfirmCallback, - override var visuals: ConfirmDialogVisuals = ConfirmDialogVisualsImpl.Empty, - private val resultFlow: ReceiveChannel -) : ConfirmDialogHandle, DialogHandleBase(visible, coroutineScope) { - private class ResultCollector( - private val callback: ConfirmCallback - ) : FlowCollector { - fun handleResult(result: ConfirmResult) { - Log.d(TAG, "handleResult: ${result.javaClass.simpleName}") - when (result) { - ConfirmResult.Confirmed -> onConfirm() - ConfirmResult.Canceled -> onDismiss() - } - } - - fun onConfirm() { - callback.onConfirm?.invoke() - } - - fun onDismiss() { - callback.onDismiss?.invoke() - } - - override suspend fun emit(value: ConfirmResult) { - handleResult(value) - } - } - - private val resultCollector = ResultCollector(callback) - - private var awaitContinuation: CancellableContinuation? = null - - private val isCallbackEmpty = callback.isEmpty - - init { - coroutineScope.launch { - resultFlow - .consumeAsFlow() - .onEach { result -> - awaitContinuation?.let { - awaitContinuation = null - if (it.isActive) { - it.resume(result) - } - } - } - .onEach { hide() } - .collect(resultCollector) - } - } - - private suspend fun awaitResult(): ConfirmResult { - return suspendCancellableCoroutine { - awaitContinuation = it.apply { - if (isCallbackEmpty) { - invokeOnCancellation { - visible.value = false - } - } - } - } - } - - fun updateVisuals(visuals: ConfirmDialogVisuals) { - this.visuals = visuals - } - - override fun show() { - if (visuals !== ConfirmDialogVisualsImpl.Empty) { - super.show() - } else { - throw UnsupportedOperationException("can't show confirm dialog with the Empty visuals") - } - } - - override fun showConfirm( - title: String, - content: String, - markdown: Boolean, - confirm: String?, - dismiss: String? - ) { - coroutineScope.launch { - updateVisuals(ConfirmDialogVisualsImpl(title, content, markdown, confirm, dismiss)) - show() - } - } - - override suspend fun awaitConfirm( - title: String, - content: String, - markdown: Boolean, - confirm: String?, - dismiss: String? - ): ConfirmResult { - coroutineScope.launch { - updateVisuals(ConfirmDialogVisualsImpl(title, content, markdown, confirm, dismiss)) - show() - } - return awaitResult() - } - - override val dialogType: String get() = "ConfirmDialog" - - override fun toString(): String { - return "${super.toString()}(visuals: $visuals)" - } - - companion object { - fun Saver( - visible: MutableState, - coroutineScope: CoroutineScope, - callback: ConfirmCallback, - resultChannel: ReceiveChannel - ) = Saver( - save = { - it.visuals - }, - restore = { - Log.d(TAG, "ConfirmDialog restore, visuals: $it") - ConfirmDialogHandleImpl(visible, coroutineScope, callback, it, resultChannel) - } - ) - } -} - -private class CustomDialogHandleImpl( - visible: MutableState, - coroutineScope: CoroutineScope -) : DialogHandleBase(visible, coroutineScope) { - override val dialogType: String get() = "CustomDialog" -} - -@Composable -fun rememberLoadingDialog(): LoadingDialogHandle { - val visible = remember { - mutableStateOf(false) - } - val coroutineScope = rememberCoroutineScope() - - if (visible.value) { - LoadingDialog() - } - - return remember { - LoadingDialogHandleImpl(visible, coroutineScope) - } -} - -@Composable -private fun rememberConfirmDialog(visuals: ConfirmDialogVisuals, callback: ConfirmCallback): ConfirmDialogHandle { - val visible = rememberSaveable { - mutableStateOf(false) - } - val coroutineScope = rememberCoroutineScope() - val resultChannel = remember { - Channel() - } - - val handle = rememberSaveable( - saver = ConfirmDialogHandleImpl.Saver(visible, coroutineScope, callback, resultChannel), - init = { - ConfirmDialogHandleImpl(visible, coroutineScope, callback, visuals, resultChannel) - } - ) - - if (visible.value) { - ConfirmDialog( - handle.visuals, - confirm = { coroutineScope.launch { resultChannel.send(ConfirmResult.Confirmed) } }, - dismiss = { coroutineScope.launch { resultChannel.send(ConfirmResult.Canceled) } } - ) - } - - return handle -} - -@Composable -fun rememberConfirmCallback(onConfirm: NullableCallback, onDismiss: NullableCallback): ConfirmCallback { - val currentOnConfirm by rememberUpdatedState(newValue = onConfirm) - val currentOnDismiss by rememberUpdatedState(newValue = onDismiss) - return remember { - ConfirmCallback({ currentOnConfirm }, { currentOnDismiss }) - } -} - -@Composable -fun rememberConfirmDialog(onConfirm: NullableCallback = null, onDismiss: NullableCallback = null): ConfirmDialogHandle { - return rememberConfirmDialog(rememberConfirmCallback(onConfirm, onDismiss)) -} - -@Composable -fun rememberConfirmDialog(callback: ConfirmCallback): ConfirmDialogHandle { - return rememberConfirmDialog(ConfirmDialogVisualsImpl.Empty, callback) -} - -@Composable -fun rememberCustomDialog(composable: @Composable (dismiss: () -> Unit) -> Unit): DialogHandle { - val visible = rememberSaveable { - mutableStateOf(false) - } - val coroutineScope = rememberCoroutineScope() - if (visible.value) { - composable { visible.value = false } - } - return remember { - CustomDialogHandleImpl(visible, coroutineScope) - } -} - -@Composable -private fun LoadingDialog() { - Dialog( - onDismissRequest = {}, - properties = DialogProperties(dismissOnClickOutside = false, dismissOnBackPress = false) - ) { - Surface( - modifier = Modifier.size(100.dp), shape = RoundedCornerShape(8.dp) - ) { - Box( - contentAlignment = Alignment.Center, - ) { - CircularProgressIndicator() - } - } - } -} - -@Composable -private fun ConfirmDialog(visuals: ConfirmDialogVisuals, confirm: () -> Unit, dismiss: () -> Unit) { - AlertDialog( - onDismissRequest = { - dismiss() - }, - title = { - Text( - text = visuals.title, - style = MaterialTheme.typography.titleLarge, - fontWeight = FontWeight.SemiBold - ) - }, - text = { - if (visuals.isMarkdown) { - MarkdownContent(content = visuals.content) - } else { - Text(text = visuals.content) - } - }, - confirmButton = { - TextButton(onClick = confirm) { - Text(text = visuals.confirm ?: stringResource(id = android.R.string.ok)) - } - }, - dismissButton = { - TextButton(onClick = dismiss) { - Text(text = visuals.dismiss ?: stringResource(id = android.R.string.cancel)) - } - }, - ) -} - -@Composable -private fun MarkdownContent(content: String) { - val contentColor = LocalContentColor.current - - AndroidView( - factory = { context -> - TextView(context).apply { - movementMethod = LinkMovementMethod.getInstance() - setSpannableFactory(NoCopySpannableFactory.getInstance()) - 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 - ) - } - }, - modifier = Modifier - .fillMaxWidth() - .wrapContentHeight(), - update = { - Markwon.create(it.context).setMarkdown(it, content) - it.setTextColor(contentColor.toArgb()) - } - ) -} \ No newline at end of file diff --git a/manager/app/src/main/java/com/rifsxd/ksunext/ui/component/KeyEventBlocker.kt b/manager/app/src/main/java/com/rifsxd/ksunext/ui/component/KeyEventBlocker.kt deleted file mode 100644 index 5aa806ca..00000000 --- a/manager/app/src/main/java/com/rifsxd/ksunext/ui/component/KeyEventBlocker.kt +++ /dev/null @@ -1,28 +0,0 @@ -package com.rifsxd.ksunext.ui.component - -import androidx.compose.foundation.focusable -import androidx.compose.foundation.layout.Box -import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.remember -import androidx.compose.ui.Modifier -import androidx.compose.ui.focus.FocusRequester -import androidx.compose.ui.focus.focusRequester -import androidx.compose.ui.input.key.KeyEvent -import androidx.compose.ui.input.key.onKeyEvent - -@Composable -fun KeyEventBlocker(predicate: (KeyEvent) -> Boolean) { - val requester = remember { FocusRequester() } - Box( - Modifier - .onKeyEvent { - predicate(it) - } - .focusRequester(requester) - .focusable() - ) - LaunchedEffect(Unit) { - requester.requestFocus() - } -} \ No newline at end of file diff --git a/manager/app/src/main/java/com/rifsxd/ksunext/ui/component/SearchBar.kt b/manager/app/src/main/java/com/rifsxd/ksunext/ui/component/SearchBar.kt deleted file mode 100644 index 800b59ca..00000000 --- a/manager/app/src/main/java/com/rifsxd/ksunext/ui/component/SearchBar.kt +++ /dev/null @@ -1,158 +0,0 @@ -package com.rifsxd.ksunext.ui.component - -import android.util.Log -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 -import androidx.compose.material.icons.automirrored.outlined.ArrowBack -import androidx.compose.material.icons.filled.Close -import androidx.compose.material.icons.filled.Search -import androidx.compose.material3.ExperimentalMaterial3Api -import androidx.compose.material3.Icon -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 -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.setValue -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.focus.FocusRequester -import androidx.compose.ui.focus.focusRequester -import androidx.compose.ui.focus.onFocusChanged -import androidx.compose.ui.platform.LocalSoftwareKeyboardController -import androidx.compose.ui.text.input.ImeAction -import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.dp - -private const val TAG = "SearchBar" - -@OptIn(ExperimentalMaterial3Api::class) -@Composable -fun SearchAppBar( - title: @Composable () -> Unit, - searchText: String, - onSearchTextChange: (String) -> Unit, - onClearClick: () -> Unit, - onBackClick: (() -> Unit)? = null, - onConfirm: (() -> Unit)? = null, - dropdownContent: @Composable (() -> Unit)? = null, - scrollBehavior: TopAppBarScrollBehavior? = null -) { - val keyboardController = LocalSoftwareKeyboardController.current - val focusRequester = remember { FocusRequester() } - var onSearch by remember { mutableStateOf(false) } - - if (onSearch) { - LaunchedEffect(Unit) { focusRequester.requestFocus() } - } - DisposableEffect(Unit) { - onDispose { - keyboardController?.hide() - } - } - - TopAppBar( - title = { - Box { - AnimatedVisibility( - modifier = Modifier.align(Alignment.CenterStart), - visible = !onSearch, - enter = fadeIn(), - exit = fadeOut(), - content = { title() } - ) - - AnimatedVisibility( - visible = onSearch, - enter = fadeIn(), - exit = fadeOut() - ) { - OutlinedTextField( - modifier = Modifier - .fillMaxWidth() - .padding(top = 2.dp, bottom = 2.dp, end = if (onBackClick != null) 0.dp else 14.dp) - .focusRequester(focusRequester) - .onFocusChanged { focusState -> - if (focusState.isFocused) onSearch = true - Log.d(TAG, "onFocusChanged: $focusState") - }, - value = searchText, - onValueChange = onSearchTextChange, - trailingIcon = { - IconButton( - onClick = { - onSearch = false - keyboardController?.hide() - onClearClick() - }, - content = { Icon(Icons.Filled.Close, null) } - ) - }, - maxLines = 1, - singleLine = true, - keyboardOptions = KeyboardOptions.Default.copy(imeAction = ImeAction.Done), - keyboardActions = KeyboardActions(onDone = { - keyboardController?.hide() - onConfirm?.invoke() - }) - ) - } - } - }, - navigationIcon = { - if (onBackClick != null) { - IconButton( - onClick = onBackClick, - content = { Icon(Icons.AutoMirrored.Outlined.ArrowBack, null) } - ) - } - }, - actions = { - AnimatedVisibility( - visible = !onSearch - ) { - IconButton( - onClick = { onSearch = true }, - content = { Icon(Icons.Filled.Search, null) } - ) - } - - if (dropdownContent != null) { - dropdownContent() - } - - }, - windowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal), - scrollBehavior = scrollBehavior - ) -} - -@OptIn(ExperimentalMaterial3Api::class) -@Preview -@Composable -private fun SearchAppBarPreview() { - var searchText by remember { mutableStateOf("") } - SearchAppBar( - title = { Text("Search text") }, - searchText = searchText, - onSearchTextChange = { searchText = it }, - onClearClick = { searchText = "" } - ) -} diff --git a/manager/app/src/main/java/com/rifsxd/ksunext/ui/component/SettingsItem.kt b/manager/app/src/main/java/com/rifsxd/ksunext/ui/component/SettingsItem.kt deleted file mode 100644 index 9e0a5e9d..00000000 --- a/manager/app/src/main/java/com/rifsxd/ksunext/ui/component/SettingsItem.kt +++ /dev/null @@ -1,107 +0,0 @@ -package com.rifsxd.ksunext.ui.component - -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.LocalTextStyle -import androidx.compose.material3.RadioButton -import androidx.compose.material3.Switch -import androidx.compose.material3.Text -import androidx.compose.material3.MaterialTheme -import androidx.compose.runtime.Composable -import androidx.compose.runtime.remember -import androidx.compose.ui.text.font.FontWeight -import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.alpha -import androidx.compose.ui.graphics.vector.ImageVector -import androidx.compose.ui.semantics.Role -import com.dergoogler.mmrl.ui.component.LabelItem -import com.dergoogler.mmrl.ui.component.text.TextRow - -@Composable -fun SwitchItem( - icon: ImageVector? = null, - title: String, - summary: String? = null, - checked: Boolean, - enabled: Boolean = true, - beta: Boolean = false, - onCheckedChange: (Boolean) -> Unit, -) { - val interactionSource = remember { MutableInteractionSource() } - val stateAlpha = remember(checked, enabled) { Modifier.alpha(if (enabled) 1f else 0.5f) } - - ListItem( - modifier = Modifier - .toggleable( - value = checked, - interactionSource = interactionSource, - role = Role.Switch, - enabled = enabled, - indication = LocalIndication.current, - onValueChange = onCheckedChange - ), - headlineContent = { - TextRow( - leadingContent = if (beta) { - { - LabelItem( - modifier = Modifier.then(stateAlpha), - text = "Beta" - ) - } - } else null - ) { - Text( - modifier = Modifier.then(stateAlpha), - text = title, - style = MaterialTheme.typography.titleMedium, - fontWeight = FontWeight.SemiBold - ) - } - }, - leadingContent = icon?.let { - { - Icon( - modifier = Modifier.then(stateAlpha), - imageVector = icon, - contentDescription = title - ) - } - }, - trailingContent = { - Switch( - checked = checked, - enabled = enabled, - onCheckedChange = onCheckedChange, - interactionSource = interactionSource - ) - }, - supportingContent = { - if (summary != null) { - Text( - modifier = Modifier.then(stateAlpha), - text = summary - ) - } - } - ) -} - -@Composable -fun RadioItem( - title: String, - selected: Boolean, - onClick: () -> Unit, -) { - ListItem( - headlineContent = { - Text(title) - }, - leadingContent = { - RadioButton(selected = selected, onClick = onClick) - } - ) -} diff --git a/manager/app/src/main/java/com/rifsxd/ksunext/ui/component/profile/AppProfileConfig.kt b/manager/app/src/main/java/com/rifsxd/ksunext/ui/component/profile/AppProfileConfig.kt deleted file mode 100644 index 695c427b..00000000 --- a/manager/app/src/main/java/com/rifsxd/ksunext/ui/component/profile/AppProfileConfig.kt +++ /dev/null @@ -1,63 +0,0 @@ -package com.rifsxd.ksunext.ui.component.profile - -import androidx.compose.foundation.layout.Column -import androidx.compose.material3.OutlinedTextField -import androidx.compose.material3.Text -import androidx.compose.runtime.Composable -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.setValue -import androidx.compose.ui.Modifier -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.tooling.preview.Preview -import com.rifsxd.ksunext.Natives -import com.rifsxd.ksunext.R -import com.rifsxd.ksunext.ui.component.SwitchItem - -@Composable -fun AppProfileConfig( - modifier: Modifier = Modifier, - fixedName: Boolean, - enabled: Boolean, - profile: Natives.Profile, - onProfileChange: (Natives.Profile) -> Unit, -) { - Column(modifier = modifier) { - if (!fixedName) { - OutlinedTextField( - label = { Text(stringResource(R.string.profile_name)) }, - value = profile.name, - onValueChange = { onProfileChange(profile.copy(name = it)) } - ) - } - - SwitchItem( - title = stringResource(R.string.profile_umount_modules), - summary = stringResource(R.string.profile_umount_modules_summary), - checked = if (enabled) { - profile.umountModules - } else { - Natives.isDefaultUmountModules() - }, - enabled = enabled, - onCheckedChange = { - onProfileChange( - profile.copy( - umountModules = it, - nonRootUseDefault = false - ) - ) - } - ) - } -} - -@Preview -@Composable -private fun AppProfileConfigPreview() { - var profile by remember { mutableStateOf(Natives.Profile("")) } - AppProfileConfig(fixedName = true, enabled = false, profile = profile) { - profile = it - } -} diff --git a/manager/app/src/main/java/com/rifsxd/ksunext/ui/component/profile/RootProfileConfig.kt b/manager/app/src/main/java/com/rifsxd/ksunext/ui/component/profile/RootProfileConfig.kt deleted file mode 100644 index 3e1e1db1..00000000 --- a/manager/app/src/main/java/com/rifsxd/ksunext/ui/component/profile/RootProfileConfig.kt +++ /dev/null @@ -1,487 +0,0 @@ -package com.rifsxd.ksunext.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 -import androidx.compose.foundation.text.KeyboardOptions -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.ArrowDropDown -import androidx.compose.material.icons.filled.ArrowDropUp -import androidx.compose.material3.AssistChip -import androidx.compose.material3.DropdownMenuItem -import androidx.compose.material3.ExperimentalMaterial3Api -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 -import androidx.compose.ui.Modifier -import androidx.compose.ui.platform.LocalSoftwareKeyboardController -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.text.input.ImeAction -import androidx.compose.ui.text.input.KeyboardType -import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.dp -import androidx.core.text.isDigitsOnly -import com.maxkeppeker.sheets.core.models.base.Header -import com.maxkeppeker.sheets.core.models.base.rememberUseCaseState -import com.maxkeppeler.sheets.input.InputDialog -import com.maxkeppeler.sheets.input.models.InputHeader -import com.maxkeppeler.sheets.input.models.InputSelection -import com.maxkeppeler.sheets.input.models.InputTextField -import com.maxkeppeler.sheets.input.models.InputTextFieldType -import com.maxkeppeler.sheets.input.models.ValidationResult -import com.maxkeppeler.sheets.list.ListDialog -import com.maxkeppeler.sheets.list.models.ListOption -import com.maxkeppeler.sheets.list.models.ListSelection -import com.rifsxd.ksunext.Natives -import com.rifsxd.ksunext.R -import com.rifsxd.ksunext.profile.Capabilities -import com.rifsxd.ksunext.profile.Groups -import com.rifsxd.ksunext.ui.component.rememberCustomDialog -import com.rifsxd.ksunext.ui.util.isSepolicyValid - -@OptIn(ExperimentalMaterial3Api::class) -@Composable -fun RootProfileConfig( - modifier: Modifier = Modifier, - fixedName: Boolean, - profile: Natives.Profile, - onProfileChange: (Natives.Profile) -> Unit, -) { - Column(modifier = modifier) { - if (!fixedName) { - OutlinedTextField( - label = { Text(stringResource(R.string.profile_name)) }, - value = profile.name, - onValueChange = { onProfileChange(profile.copy(name = it)) } - ) - } - - /* - var expanded by remember { mutableStateOf(false) } - val currentNamespace = when (profile.namespace) { - Natives.Profile.Namespace.INHERITED.ordinal -> stringResource(R.string.profile_namespace_inherited) - Natives.Profile.Namespace.GLOBAL.ordinal -> stringResource(R.string.profile_namespace_global) - Natives.Profile.Namespace.INDIVIDUAL.ordinal -> stringResource(R.string.profile_namespace_individual) - else -> stringResource(R.string.profile_namespace_inherited) - } - ListItem(headlineContent = { - ExposedDropdownMenuBox( - expanded = expanded, - onExpandedChange = { expanded = !expanded } - ) { - OutlinedTextField( - modifier = Modifier - .menuAnchor(MenuAnchorType.PrimaryNotEditable) - .fillMaxWidth(), - readOnly = true, - label = { Text(stringResource(R.string.profile_namespace)) }, - value = currentNamespace, - onValueChange = {}, - trailingIcon = { - if (expanded) Icon(Icons.Filled.ArrowDropUp, null) - else Icon(Icons.Filled.ArrowDropDown, null) - }, - ) - ExposedDropdownMenu( - expanded = expanded, - onDismissRequest = { expanded = false } - ) { - DropdownMenuItem( - text = { Text(stringResource(R.string.profile_namespace_inherited)) }, - onClick = { - onProfileChange(profile.copy(namespace = Natives.Profile.Namespace.INHERITED.ordinal)) - expanded = false - }, - ) - DropdownMenuItem( - text = { Text(stringResource(R.string.profile_namespace_global)) }, - onClick = { - onProfileChange(profile.copy(namespace = Natives.Profile.Namespace.GLOBAL.ordinal)) - expanded = false - }, - ) - DropdownMenuItem( - text = { Text(stringResource(R.string.profile_namespace_individual)) }, - onClick = { - onProfileChange(profile.copy(namespace = Natives.Profile.Namespace.INDIVIDUAL.ordinal)) - expanded = false - }, - ) - } - } - }) - */ - - UidPanel(uid = profile.uid, label = "uid", onUidChange = { - onProfileChange( - profile.copy( - uid = it, - rootUseDefault = false - ) - ) - }) - - UidPanel(uid = profile.gid, label = "gid", onUidChange = { - onProfileChange( - profile.copy( - gid = it, - rootUseDefault = false - ) - ) - }) - - val selectedGroups = profile.groups.ifEmpty { listOf(0) }.let { e -> - e.mapNotNull { g -> - Groups.entries.find { it.gid == g } - } - } - GroupsPanel(selectedGroups) { - onProfileChange( - profile.copy( - groups = it.map { group -> group.gid }.ifEmpty { listOf(0) }, - rootUseDefault = false - ) - ) - } - - val selectedCaps = profile.capabilities.mapNotNull { e -> - Capabilities.entries.find { it.cap == e } - } - - CapsPanel(selectedCaps) { - onProfileChange( - profile.copy( - capabilities = it.map { cap -> cap.cap }, - rootUseDefault = false - ) - ) - } - - SELinuxPanel(profile = profile, onSELinuxChange = { domain, rules -> - onProfileChange( - profile.copy( - context = domain, - rules = rules, - rootUseDefault = false - ) - ) - }) - - } -} - -@OptIn(ExperimentalLayoutApi::class, ExperimentalMaterial3Api::class) -@Composable -fun GroupsPanel(selected: List, closeSelection: (selection: Set) -> Unit) { - val selectGroupsDialog = rememberCustomDialog { dismiss: () -> Unit -> - val groups = Groups.entries.toTypedArray().sortedWith( - compareBy { if (selected.contains(it)) 0 else 1 } - .then(compareBy { - when (it) { - Groups.ROOT -> 0 - Groups.SYSTEM -> 1 - Groups.SHELL -> 2 - else -> Int.MAX_VALUE - } - }) - .then(compareBy { it.name }) - - ) - val options = groups.map { value -> - ListOption( - titleText = value.display, - subtitleText = value.desc, - selected = selected.contains(value), - ) - } - - val selection = HashSet(selected) - ListDialog( - state = rememberUseCaseState(visible = true, onFinishedRequest = { - closeSelection(selection) - }, onCloseRequest = { - dismiss() - }), - header = Header.Default( - title = stringResource(R.string.profile_groups), - ), - selection = ListSelection.Multiple( - showCheckBoxes = true, - options = options, - maxChoices = 32, // Kernel only supports 32 groups at most - ) { indecies, _ -> - // Handle selection - selection.clear() - indecies.forEach { index -> - val group = groups[index] - selection.add(group) - } - } - ) - } - - OutlinedCard( - modifier = Modifier - .fillMaxWidth() - .padding(16.dp) - ) { - - Column( - modifier = Modifier - .fillMaxSize() - .clickable { - selectGroupsDialog.show() - } - .padding(16.dp) - ) { - Text(stringResource(R.string.profile_groups)) - FlowRow { - selected.forEach { group -> - AssistChip( - modifier = Modifier.padding(3.dp), - onClick = { /*TODO*/ }, - label = { Text(group.display) }) - } - } - } - - } -} - -@OptIn(ExperimentalLayoutApi::class, ExperimentalMaterial3Api::class) -@Composable -fun CapsPanel( - selected: Collection, - closeSelection: (selection: Set) -> Unit -) { - val selectCapabilitiesDialog = rememberCustomDialog { dismiss -> - val caps = Capabilities.entries.toTypedArray().sortedWith( - compareBy { if (selected.contains(it)) 0 else 1 } - .then(compareBy { it.name }) - ) - val options = caps.map { value -> - ListOption( - titleText = value.display, - subtitleText = value.desc, - selected = selected.contains(value), - ) - } - - val selection = HashSet(selected) - ListDialog( - state = rememberUseCaseState(visible = true, onFinishedRequest = { - closeSelection(selection) - }, onCloseRequest = { - dismiss() - }), - header = Header.Default( - title = stringResource(R.string.profile_capabilities), - ), - selection = ListSelection.Multiple( - showCheckBoxes = true, - options = options - ) { indecies, _ -> - // Handle selection - selection.clear() - indecies.forEach { index -> - val group = caps[index] - selection.add(group) - } - } - ) - } - - OutlinedCard( - modifier = Modifier - .fillMaxWidth() - .padding(16.dp) - ) { - - Column( - modifier = Modifier - .fillMaxSize() - .clickable { - selectCapabilitiesDialog.show() - } - .padding(16.dp) - ) { - Text(stringResource(R.string.profile_capabilities)) - FlowRow { - selected.forEach { group -> - AssistChip( - modifier = Modifier.padding(3.dp), - onClick = { /*TODO*/ }, - label = { Text(group.display) }) - } - } - } - - } -} - -@Composable -private fun UidPanel(uid: Int, label: String, onUidChange: (Int) -> Unit) { - - ListItem(headlineContent = { - var isError by remember { - mutableStateOf(false) - } - var lastValidUid by remember { - mutableIntStateOf(uid) - } - val keyboardController = LocalSoftwareKeyboardController.current - - OutlinedTextField( - modifier = Modifier.fillMaxWidth(), - label = { Text(label) }, - value = uid.toString(), - isError = isError, - keyboardOptions = KeyboardOptions( - keyboardType = KeyboardType.Number, - imeAction = ImeAction.Done - ), - keyboardActions = KeyboardActions(onDone = { - keyboardController?.hide() - }), - onValueChange = { - if (it.isEmpty()) { - onUidChange(0) - return@OutlinedTextField - } - val valid = isTextValidUid(it) - - val targetUid = if (valid) it.toInt() else lastValidUid - if (valid) { - lastValidUid = it.toInt() - } - - onUidChange(targetUid) - - isError = !valid - } - ) - }) -} - -@OptIn(ExperimentalMaterial3Api::class) -@Composable -private fun SELinuxPanel( - profile: Natives.Profile, - onSELinuxChange: (domain: String, rules: String) -> Unit -) { - val editSELinuxDialog = rememberCustomDialog { dismiss -> - var domain by remember { mutableStateOf(profile.context) } - var rules by remember { mutableStateOf(profile.rules) } - - val inputOptions = listOf( - InputTextField( - text = domain, - header = InputHeader( - title = stringResource(id = R.string.profile_selinux_domain), - ), - type = InputTextFieldType.OUTLINED, - required = true, - keyboardOptions = KeyboardOptions( - keyboardType = KeyboardType.Ascii, - imeAction = ImeAction.Next - ), - resultListener = { - domain = it ?: "" - }, - validationListener = { value -> - // value can be a-zA-Z0-9_ - val regex = Regex("^[a-z_]+:[a-z0-9_]+:[a-z0-9_]+(:[a-z0-9_]+)?$") - if (value?.matches(regex) == true) ValidationResult.Valid - else ValidationResult.Invalid("Domain must be in the format of \"user:role:type:level\"") - } - ), - InputTextField( - text = rules, - header = InputHeader( - title = stringResource(id = R.string.profile_selinux_rules), - ), - type = InputTextFieldType.OUTLINED, - keyboardOptions = KeyboardOptions( - keyboardType = KeyboardType.Ascii, - ), - singleLine = false, - resultListener = { - rules = it ?: "" - }, - validationListener = { value -> - if (isSepolicyValid(value)) ValidationResult.Valid - else ValidationResult.Invalid("SELinux rules is invalid!") - } - ) - ) - - InputDialog( - state = rememberUseCaseState(visible = true, - onFinishedRequest = { - onSELinuxChange(domain, rules) - }, - onCloseRequest = { - dismiss() - }), - header = Header.Default( - title = stringResource(R.string.profile_selinux_context), - ), - selection = InputSelection( - input = inputOptions, - onPositiveClick = { result -> - // Handle selection - }, - ) - ) - } - - ListItem(headlineContent = { - OutlinedTextField( - modifier = Modifier - .fillMaxWidth() - .clickable { - editSELinuxDialog.show() - }, - enabled = false, - colors = OutlinedTextFieldDefaults.colors( - disabledTextColor = MaterialTheme.colorScheme.onSurface, - disabledBorderColor = MaterialTheme.colorScheme.outline, - disabledPlaceholderColor = MaterialTheme.colorScheme.onSurfaceVariant, - disabledLabelColor = MaterialTheme.colorScheme.onSurfaceVariant - ), - label = { Text(text = stringResource(R.string.profile_selinux_context)) }, - value = profile.context, - onValueChange = { } - ) - }) -} - -@Preview -@Composable -private fun RootProfileConfigPreview() { - var profile by remember { mutableStateOf(Natives.Profile("")) } - RootProfileConfig(fixedName = true, profile = profile) { - profile = it - } -} - -private fun isTextValidUid(text: String): Boolean { - return text.isNotEmpty() && text.isDigitsOnly() && text.toInt() >= 0 && text.toInt() <= Int.MAX_VALUE -} diff --git a/manager/app/src/main/java/com/rifsxd/ksunext/ui/component/profile/TemplateConfig.kt b/manager/app/src/main/java/com/rifsxd/ksunext/ui/component/profile/TemplateConfig.kt deleted file mode 100644 index 686dc948..00000000 --- a/manager/app/src/main/java/com/rifsxd/ksunext/ui/component/profile/TemplateConfig.kt +++ /dev/null @@ -1,117 +0,0 @@ -package com.rifsxd.ksunext.ui.component.profile - -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.automirrored.filled.ReadMore -import androidx.compose.material.icons.filled.ArrowDropDown -import androidx.compose.material.icons.filled.ArrowDropUp -import androidx.compose.material.icons.filled.Create -import androidx.compose.material3.DropdownMenuItem -import androidx.compose.material3.ExperimentalMaterial3Api -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 -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.saveable.rememberSaveable -import androidx.compose.runtime.setValue -import androidx.compose.ui.Modifier -import androidx.compose.ui.res.stringResource -import com.rifsxd.ksunext.Natives -import com.rifsxd.ksunext.R -import com.rifsxd.ksunext.ui.util.listAppProfileTemplates -import com.rifsxd.ksunext.ui.util.setSepolicy -import com.rifsxd.ksunext.ui.viewmodel.getTemplateInfoById - -/** - * @author weishu - * @date 2023/10/21. - */ -@OptIn(ExperimentalMaterial3Api::class) -@Composable -fun TemplateConfig( - profile: Natives.Profile, - onViewTemplate: (id: String) -> Unit = {}, - onManageTemplate: () -> Unit = {}, - onProfileChange: (Natives.Profile) -> Unit -) { - var expanded by remember { mutableStateOf(false) } - var template by rememberSaveable { - mutableStateOf(profile.rootTemplate ?: "") - } - val profileTemplates = listAppProfileTemplates() - val noTemplates = profileTemplates.isEmpty() - - ListItem(headlineContent = { - ExposedDropdownMenuBox( - expanded = expanded, - onExpandedChange = { expanded = it }, - ) { - OutlinedTextField( - modifier = Modifier - .menuAnchor(MenuAnchorType.PrimaryNotEditable) - .fillMaxWidth(), - readOnly = true, - label = { Text(stringResource(R.string.profile_template)) }, - value = template.ifEmpty { "None" }, - onValueChange = {}, - trailingIcon = { - if (noTemplates) { - IconButton( - onClick = onManageTemplate - ) { - Icon(Icons.Filled.Create, null) - } - } else if (expanded) Icon(Icons.Filled.ArrowDropUp, null) - else Icon(Icons.Filled.ArrowDropDown, null) - }, - ) - if (profileTemplates.isEmpty()) { - return@ExposedDropdownMenuBox - } - ExposedDropdownMenu( - expanded = expanded, - onDismissRequest = { expanded = false } - ) { - profileTemplates.forEach { tid -> - val templateInfo = - getTemplateInfoById(tid) ?: return@forEach - DropdownMenuItem( - text = { Text(tid) }, - onClick = { - template = tid - if (setSepolicy(tid, templateInfo.rules.joinToString("\n"))) { - onProfileChange( - profile.copy( - rootTemplate = tid, - rootUseDefault = false, - uid = templateInfo.uid, - gid = templateInfo.gid, - groups = templateInfo.groups, - capabilities = templateInfo.capabilities, - context = templateInfo.context, - namespace = templateInfo.namespace, - ) - ) - } - expanded = false - }, - trailingIcon = { - IconButton(onClick = { - onViewTemplate(tid) - }) { - Icon(Icons.AutoMirrored.Filled.ReadMore, null) - } - } - ) - } - } - } - }) -} \ No newline at end of file diff --git a/manager/app/src/main/java/com/rifsxd/ksunext/ui/screen/AppProfile.kt b/manager/app/src/main/java/com/rifsxd/ksunext/ui/screen/AppProfile.kt deleted file mode 100644 index 1ae0a486..00000000 --- a/manager/app/src/main/java/com/rifsxd/ksunext/ui/screen/AppProfile.kt +++ /dev/null @@ -1,410 +0,0 @@ -package com.rifsxd.ksunext.ui.screen - -import androidx.annotation.StringRes -import androidx.compose.animation.Crossfade -import androidx.compose.foundation.gestures.detectTapGestures -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 -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.automirrored.filled.ArrowBack -import androidx.compose.material.icons.filled.AccountCircle -import androidx.compose.material.icons.filled.Android -import androidx.compose.material.icons.filled.AdminPanelSettings -import androidx.compose.material3.DropdownMenu -import androidx.compose.material3.DropdownMenuItem -import androidx.compose.material3.ExperimentalMaterial3Api -import androidx.compose.material3.FilterChip -import androidx.compose.material3.HorizontalDivider -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.material3.MaterialTheme -import androidx.compose.runtime.Composable -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.rememberCoroutineScope -import androidx.compose.runtime.saveable.rememberSaveable -import androidx.compose.runtime.setValue -import androidx.compose.ui.text.font.FontWeight -import androidx.compose.ui.Modifier -import androidx.compose.ui.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 -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.Dp -import androidx.compose.ui.unit.DpOffset -import androidx.compose.ui.unit.dp -import androidx.lifecycle.compose.dropUnlessResumed -import 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.AppProfileTemplateScreenDestination -import com.ramcosta.composedestinations.generated.destinations.TemplateEditorScreenDestination -import com.ramcosta.composedestinations.navigation.DestinationsNavigator -import kotlinx.coroutines.launch -import com.rifsxd.ksunext.Natives -import com.rifsxd.ksunext.R -import com.rifsxd.ksunext.ui.component.SwitchItem -import com.rifsxd.ksunext.ui.component.profile.AppProfileConfig -import com.rifsxd.ksunext.ui.component.profile.RootProfileConfig -import com.rifsxd.ksunext.ui.component.profile.TemplateConfig -import com.rifsxd.ksunext.ui.util.LocalSnackbarHost -import com.rifsxd.ksunext.ui.util.forceStopApp -import com.rifsxd.ksunext.ui.util.getSepolicy -import com.rifsxd.ksunext.ui.util.launchApp -import com.rifsxd.ksunext.ui.util.restartApp -import com.rifsxd.ksunext.ui.util.setSepolicy -import com.rifsxd.ksunext.ui.viewmodel.SuperUserViewModel -import com.rifsxd.ksunext.ui.viewmodel.getTemplateInfoById - -/** - * @author weishu - * @date 2023/5/16. - */ -@OptIn(ExperimentalMaterial3Api::class) -@Destination -@Composable -fun AppProfileScreen( - navigator: DestinationsNavigator, - appInfo: SuperUserViewModel.AppInfo, -) { - val context = LocalContext.current - val snackBarHost = LocalSnackbarHost.current - val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior() - val scope = rememberCoroutineScope() - val viewModel: SuperUserViewModel = viewModel() - val failToUpdateAppProfile = stringResource(R.string.failed_to_update_app_profile).format(appInfo.label) - val failToUpdateSepolicy = stringResource(R.string.failed_to_update_sepolicy).format(appInfo.label) - val suNotAllowed = stringResource(R.string.su_not_allowed).format(appInfo.label) - - val packageName = appInfo.packageName - val initialProfile = Natives.getAppProfile(packageName, appInfo.uid) - if (initialProfile.allowSu) { - initialProfile.rules = getSepolicy(packageName) - } - var profile by rememberSaveable { - mutableStateOf(initialProfile) - } - - Scaffold( - topBar = { - TopBar( - onBack = dropUnlessResumed { 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(), - contentDescription = appInfo.label, - modifier = Modifier - .padding(4.dp) - .width(48.dp) - .height(48.dp) - ) - }, - profile = profile, - onViewTemplate = { - getTemplateInfoById(it)?.let { info -> - navigator.navigate(TemplateEditorScreenDestination(info)) - } - }, - onManageTemplate = { - navigator.navigate(AppProfileTemplateScreenDestination()) - }, - onProfileChange = { - scope.launch { - if (it.allowSu) { - // sync with allowlist.c - forbid_system_uid - if (appInfo.uid < 2000 && appInfo.uid != 1000) { - snackBarHost.showSnackbar(suNotAllowed) - return@launch - } - if (!it.rootUseDefault && it.rules.isNotEmpty() && !setSepolicy(profile.name, it.rules)) { - snackBarHost.showSnackbar(failToUpdateSepolicy) - return@launch - } - } - if (!Natives.setAppProfile(it)) { - snackBarHost.showSnackbar(failToUpdateAppProfile.format(appInfo.uid)) - } else { - profile = it - viewModel.updateAppProfile(packageName, it) - } - } - }, - ) - } -} - -@Composable -private fun AppProfileInner( - modifier: Modifier = Modifier, - packageName: String, - appLabel: String, - appIcon: @Composable () -> Unit, - profile: Natives.Profile, - onViewTemplate: (id: String) -> Unit = {}, - onManageTemplate: () -> Unit = {}, - onProfileChange: (Natives.Profile) -> Unit, -) { - val isRootGranted = profile.allowSu - - Column(modifier = modifier) { - AppMenuBox(packageName) { - ListItem( - headlineContent = { Text( - text = appLabel, - style = MaterialTheme.typography.titleMedium, - fontWeight = FontWeight.SemiBold - ) }, - supportingContent = { Text(packageName) }, - leadingContent = appIcon, - ) - } - - SwitchItem( - icon = Icons.Filled.AdminPanelSettings, - title = stringResource(id = R.string.superuser), - checked = isRootGranted, - onCheckedChange = { onProfileChange(profile.copy(allowSu = it)) }, - ) - - Crossfade(targetState = isRootGranted, label = "") { current -> - Column( - modifier = Modifier.padding(bottom = 6.dp + 48.dp + 6.dp /* SnackBar height */) - ) { - if (current) { - val initialMode = if (profile.rootUseDefault) { - Mode.Default - } else if (profile.rootTemplate != null) { - Mode.Template - } else { - Mode.Custom - } - var mode by rememberSaveable { - mutableStateOf(initialMode) - } - ProfileBox(mode, true) { - // template mode shouldn't change profile here! - if (it == Mode.Default || it == Mode.Custom) { - onProfileChange(profile.copy(rootUseDefault = it == Mode.Default)) - } - mode = it - } - Crossfade(targetState = mode, label = "") { currentMode -> - if (currentMode == Mode.Template) { - TemplateConfig( - profile = profile, - onViewTemplate = onViewTemplate, - onManageTemplate = onManageTemplate, - onProfileChange = onProfileChange - ) - } else if (mode == Mode.Custom) { - RootProfileConfig( - fixedName = true, - profile = profile, - onProfileChange = onProfileChange - ) - } - } - } else { - val mode = if (profile.nonRootUseDefault) Mode.Default else Mode.Custom - ProfileBox(mode, false) { - onProfileChange(profile.copy(nonRootUseDefault = (it == Mode.Default))) - } - Crossfade(targetState = mode, label = "") { currentMode -> - val modifyEnabled = currentMode == Mode.Custom - AppProfileConfig( - fixedName = true, - profile = profile, - enabled = modifyEnabled, - onProfileChange = onProfileChange - ) - } - } - } - } - } -} - -private enum class Mode(@StringRes private val res: Int) { - Default(R.string.profile_default), Template(R.string.profile_template), Custom(R.string.profile_custom); - - val text: String - @Composable get() = stringResource(res) -} - -@OptIn(ExperimentalMaterial3Api::class) -@Composable -private fun TopBar( - onBack: () -> Unit, - scrollBehavior: TopAppBarScrollBehavior? = null -) { - TopAppBar( - title = { - Text( - text = stringResource(R.string.profile), - style = MaterialTheme.typography.titleLarge, - fontWeight = FontWeight.Black, - ) - }, - navigationIcon = { - IconButton( - onClick = onBack - ) { Icon(Icons.AutoMirrored.Filled.ArrowBack, contentDescription = null) } - }, - windowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal), - scrollBehavior = scrollBehavior - ) -} - -@Composable -private fun ProfileBox( - mode: Mode, - hasTemplate: Boolean, - onModeChange: (Mode) -> Unit, -) { - ListItem( - headlineContent = { Text( - text = stringResource(R.string.profile), - style = MaterialTheme.typography.titleMedium, - fontWeight = FontWeight.SemiBold - ) }, - supportingContent = { Text(mode.text) }, - leadingContent = { Icon(Icons.Filled.AccountCircle, null) }, - ) - HorizontalDivider(thickness = Dp.Hairline) - ListItem(headlineContent = { - Row( - modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceEvenly - ) { - FilterChip( - selected = mode == Mode.Default, - label = { Text(stringResource(R.string.profile_default)) }, - onClick = { onModeChange(Mode.Default) }, - ) - if (hasTemplate) { - FilterChip( - selected = mode == Mode.Template, - label = { Text(stringResource(R.string.profile_template)) }, - onClick = { onModeChange(Mode.Template) }, - ) - } - FilterChip( - selected = mode == Mode.Custom, - label = { Text(stringResource(R.string.profile_custom)) }, - onClick = { onModeChange(Mode.Custom) }, - ) - } - }) -} - -@Composable -private fun AppMenuBox(packageName: String, content: @Composable () -> Unit) { - - var expanded by remember { mutableStateOf(false) } - var touchPoint: Offset by remember { mutableStateOf(Offset.Zero) } - val density = LocalDensity.current - - BoxWithConstraints( - Modifier - .fillMaxSize() - .pointerInput(Unit) { - detectTapGestures { - touchPoint = it - expanded = true - } - } - ) { - - content() - - val (offsetX, offsetY) = with(density) { - (touchPoint.x.toDp()) to (touchPoint.y.toDp()) - } - - DropdownMenu( - expanded = expanded, - offset = DpOffset(offsetX, -offsetY), - onDismissRequest = { - expanded = false - }, - ) { - DropdownMenuItem( - text = { Text(stringResource(id = R.string.launch_app)) }, - onClick = { - expanded = false - launchApp(packageName) - }, - ) - DropdownMenuItem( - text = { Text(stringResource(id = R.string.force_stop_app)) }, - onClick = { - expanded = false - forceStopApp(packageName) - }, - ) - DropdownMenuItem( - text = { Text(stringResource(id = R.string.restart_app)) }, - onClick = { - expanded = false - restartApp(packageName) - }, - ) - } - } - - -} - -@Preview -@Composable -private fun AppProfilePreview() { - var profile by remember { mutableStateOf(Natives.Profile("")) } - AppProfileInner( - packageName = "icu.nullptr.test", - appLabel = "Test", - appIcon = { Icon(Icons.Filled.Android, null) }, - profile = profile, - onProfileChange = { - profile = it - }, - ) -} - diff --git a/manager/app/src/main/java/com/rifsxd/ksunext/ui/screen/BackupRestore.kt b/manager/app/src/main/java/com/rifsxd/ksunext/ui/screen/BackupRestore.kt deleted file mode 100644 index 3aa68ce7..00000000 --- a/manager/app/src/main/java/com/rifsxd/ksunext/ui/screen/BackupRestore.kt +++ /dev/null @@ -1,302 +0,0 @@ -package com.rifsxd.ksunext.ui.screen - -import android.content.Context -import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.WindowInsets -import androidx.compose.foundation.layout.WindowInsetsSides -import androidx.compose.foundation.layout.only -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.safeDrawing -import androidx.compose.foundation.rememberScrollState -import androidx.compose.foundation.verticalScroll -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.automirrored.filled.* -import androidx.compose.material.icons.filled.* -import androidx.compose.material3.ExperimentalMaterial3Api -import androidx.compose.material3.HorizontalDivider -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.AlertDialog -import androidx.compose.material3.TextButton -import androidx.compose.material3.TopAppBar -import androidx.compose.material3.TopAppBarDefaults -import androidx.compose.material3.TopAppBarScrollBehavior -import androidx.compose.material3.rememberTopAppBarState -import androidx.compose.material3.MaterialTheme -import androidx.compose.runtime.Composable -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.rememberCoroutineScope -import androidx.compose.runtime.setValue -import androidx.compose.runtime.saveable.rememberSaveable -import androidx.compose.ui.text.font.FontWeight -import androidx.compose.ui.Modifier -import androidx.compose.ui.input.nestedscroll.nestedScroll -import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.Dp -import androidx.lifecycle.compose.dropUnlessResumed -import com.ramcosta.composedestinations.annotation.Destination -import com.ramcosta.composedestinations.annotation.RootGraph -import com.ramcosta.composedestinations.navigation.DestinationsNavigator -import com.ramcosta.composedestinations.navigation.EmptyDestinationsNavigator -import kotlinx.coroutines.launch -import kotlinx.coroutines.withContext -import com.rifsxd.ksunext.Natives -import com.rifsxd.ksunext.ksuApp -import com.rifsxd.ksunext.R -import com.rifsxd.ksunext.ui.component.ConfirmResult -import com.rifsxd.ksunext.ui.component.rememberConfirmDialog -import com.rifsxd.ksunext.ui.component.rememberLoadingDialog -import com.rifsxd.ksunext.ui.util.LocalSnackbarHost -import com.rifsxd.ksunext.ui.util.* - -/** - * @author rifsxd - * @date 2025/1/14. - */ -@OptIn(ExperimentalMaterial3Api::class) -@Destination -@Composable -fun BackupRestoreScreen(navigator: DestinationsNavigator) { - val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState()) - val snackBarHost = LocalSnackbarHost.current - - val isManager = Natives.becomeManager(ksuApp.packageName) - val ksuVersion = if (isManager) Natives.version else null - - Scaffold( - topBar = { - TopBar( - onBack = dropUnlessResumed { - navigator.popBackStack() - }, - scrollBehavior = scrollBehavior - ) - }, - snackbarHost = { SnackbarHost(snackBarHost) }, - contentWindowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal) - ) { paddingValues -> - val loadingDialog = rememberLoadingDialog() - val restoreDialog = rememberConfirmDialog() - val backupDialog = rememberConfirmDialog() - - Column( - modifier = Modifier - .padding(paddingValues) - .nestedScroll(scrollBehavior.nestedScrollConnection) - .verticalScroll(rememberScrollState()) - ) { - - val context = LocalContext.current - val scope = rememberCoroutineScope() - - var showRebootDialog by remember { mutableStateOf(false) } - - if (showRebootDialog) { - AlertDialog( - onDismissRequest = { showRebootDialog = false }, - title = { Text( - text = stringResource(R.string.reboot_required), - style = MaterialTheme.typography.titleLarge, - fontWeight = FontWeight.SemiBold - ) }, - text = { Text(stringResource(R.string.reboot_message)) }, - confirmButton = { - TextButton(onClick = { - showRebootDialog = false - reboot() - }) { - Text(stringResource(R.string.reboot)) - } - }, - dismissButton = { - TextButton(onClick = { showRebootDialog = false }) { - Text(stringResource(R.string.later)) - } - } - ) - } - - val moduleBackup = stringResource(id = R.string.module_backup) - val backupMessage = stringResource(id = R.string.module_backup_message) - ListItem( - leadingContent = { - Icon( - Icons.Filled.Backup, - moduleBackup - ) - }, - headlineContent = { Text( - text = moduleBackup, - style = MaterialTheme.typography.titleMedium, - fontWeight = FontWeight.SemiBold, - ) }, - modifier = Modifier.clickable { - scope.launch { - val result = backupDialog.awaitConfirm(title = moduleBackup, content = backupMessage) - if (result == ConfirmResult.Confirmed) { - loadingDialog.withLoading { - moduleBackup() - } - } - } - } - ) - - if (showRebootDialog) { - AlertDialog( - onDismissRequest = { showRebootDialog = false }, - title = { Text( - text = stringResource(R.string.reboot_required), - style = MaterialTheme.typography.titleLarge, - fontWeight = FontWeight.SemiBold - ) }, - text = { Text(stringResource(R.string.reboot_message)) }, - confirmButton = { - TextButton(onClick = { - showRebootDialog = false - reboot() - }) { - Text(stringResource(R.string.reboot)) - } - }, - dismissButton = { - TextButton(onClick = { showRebootDialog = false }) { - Text(stringResource(R.string.later)) - } - } - ) - } - - val prefs = context.getSharedPreferences("settings", Context.MODE_PRIVATE) - - var useOverlayFs by rememberSaveable { - mutableStateOf(readMountSystemFile()) - } - - val moduleRestore = stringResource(id = R.string.module_restore) - val restoreMessage = stringResource(id = R.string.module_restore_message) - - ListItem( - leadingContent = { - Icon( - Icons.Filled.Restore, - moduleRestore, - tint = if (useOverlayFs) MaterialTheme.colorScheme.onSurface.copy(alpha = 0.38f) else MaterialTheme.colorScheme.onSurface - ) - }, - headlineContent = { - Text( - moduleRestore, - style = MaterialTheme.typography.titleMedium, - fontWeight = FontWeight.SemiBold, - color = if (useOverlayFs) MaterialTheme.colorScheme.onSurface.copy(alpha = 0.38f) else MaterialTheme.colorScheme.onSurface - ) - }, - modifier = Modifier.clickable( - enabled = !useOverlayFs, - onClick = { - scope.launch { - val result = restoreDialog.awaitConfirm(title = moduleRestore, content = restoreMessage) - if (result == ConfirmResult.Confirmed) { - loadingDialog.withLoading { - moduleRestore() - showRebootDialog = true - } - } - } - } - ) - ) - - HorizontalDivider(thickness = Dp.Hairline) - - val allowlistBackup = stringResource(id = R.string.allowlist_backup) - val allowlistbackupMessage = stringResource(id = R.string.allowlist_backup_message) - ListItem( - leadingContent = { - Icon( - Icons.Filled.Backup, - allowlistBackup - ) - }, - headlineContent = { Text( - text = allowlistBackup, - style = MaterialTheme.typography.titleMedium, - fontWeight = FontWeight.SemiBold, - ) }, - modifier = Modifier.clickable { - scope.launch { - val result = backupDialog.awaitConfirm(title = allowlistBackup, content = allowlistbackupMessage) - if (result == ConfirmResult.Confirmed) { - loadingDialog.withLoading { - allowlistBackup() - } - } - } - } - ) - - val allowlistRestore = stringResource(id = R.string.allowlist_restore) - val allowlistrestoreMessage = stringResource(id = R.string.allowlist_restore_message) - ListItem( - leadingContent = { - Icon( - Icons.Filled.Restore, - allowlistRestore - ) - }, - headlineContent = { Text( - text = allowlistRestore, - style = MaterialTheme.typography.titleMedium, - fontWeight = FontWeight.SemiBold, - ) }, - modifier = Modifier.clickable { - scope.launch { - val result = restoreDialog.awaitConfirm(title = allowlistRestore, content = allowlistrestoreMessage) - if (result == ConfirmResult.Confirmed) { - loadingDialog.withLoading { - allowlistRestore() - } - } - } - } - ) - } - } -} - -@OptIn(ExperimentalMaterial3Api::class) -@Composable -private fun TopBar( - onBack: () -> Unit = {}, - scrollBehavior: TopAppBarScrollBehavior? = null -) { - TopAppBar( - title = { Text( - text = stringResource(R.string.backup_restore), - style = MaterialTheme.typography.titleLarge, - fontWeight = FontWeight.Black, - ) }, navigationIcon = { - IconButton( - onClick = onBack - ) { Icon(Icons.AutoMirrored.Filled.ArrowBack, contentDescription = null) } - }, - windowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal), - scrollBehavior = scrollBehavior - ) -} - -@Preview -@Composable -private fun BackupPreview() { - BackupRestoreScreen(EmptyDestinationsNavigator) -} diff --git a/manager/app/src/main/java/com/rifsxd/ksunext/ui/screen/BottomBarDestination.kt b/manager/app/src/main/java/com/rifsxd/ksunext/ui/screen/BottomBarDestination.kt deleted file mode 100644 index 12da30d7..00000000 --- a/manager/app/src/main/java/com/rifsxd/ksunext/ui/screen/BottomBarDestination.kt +++ /dev/null @@ -1,26 +0,0 @@ -package com.rifsxd.ksunext.ui.screen - -import androidx.annotation.StringRes -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.generated.destinations.SettingScreenDestination -import com.ramcosta.composedestinations.spec.DirectionDestinationSpec -import com.rifsxd.ksunext.R - -enum class BottomBarDestination( - val direction: DirectionDestinationSpec, - @StringRes val label: Int, - val iconSelected: ImageVector, - val iconNotSelected: ImageVector, - val rootRequired: Boolean, -) { - Home(HomeScreenDestination, R.string.home, Icons.Filled.Home, Icons.Outlined.Home, false), - SuperUser(SuperUserScreenDestination, R.string.superuser, Icons.Filled.AdminPanelSettings, Icons.Outlined.AdminPanelSettings, true), - Module(ModuleScreenDestination, R.string.module, Icons.Filled.Layers, Icons.Outlined.Layers, true), - Settings(SettingScreenDestination, R.string.settings, Icons.Filled.Settings, Icons.Outlined.Settings, false) -} diff --git a/manager/app/src/main/java/com/rifsxd/ksunext/ui/screen/Customization.kt b/manager/app/src/main/java/com/rifsxd/ksunext/ui/screen/Customization.kt deleted file mode 100644 index b8607e65..00000000 --- a/manager/app/src/main/java/com/rifsxd/ksunext/ui/screen/Customization.kt +++ /dev/null @@ -1,359 +0,0 @@ -package com.rifsxd.ksunext.ui.screen - -import android.content.Context -import android.content.Intent -import androidx.compose.foundation.isSystemInDarkTheme -import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.WindowInsets -import androidx.compose.foundation.layout.WindowInsetsSides -import androidx.compose.foundation.layout.only -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.safeDrawing -import androidx.compose.foundation.rememberScrollState -import androidx.compose.foundation.verticalScroll -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.automirrored.filled.* -import androidx.compose.material.icons.filled.* -import androidx.compose.material3.ExperimentalMaterial3Api -import androidx.compose.material3.Icon -import androidx.compose.material3.IconButton -import androidx.compose.material3.ListItem -import androidx.compose.material3.Scaffold -import androidx.compose.material3.SnackbarHost -import androidx.compose.material3.Text -import androidx.compose.material3.AlertDialog -import androidx.compose.material3.TextButton -import androidx.compose.material3.TopAppBar -import androidx.compose.material3.TopAppBarDefaults -import androidx.compose.material3.TopAppBarScrollBehavior -import androidx.compose.material3.rememberTopAppBarState -import androidx.compose.material3.MaterialTheme -import androidx.compose.runtime.Composable -import androidx.compose.runtime.getValue -import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.mutableIntStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.rememberCoroutineScope -import androidx.compose.runtime.saveable.rememberSaveable -import androidx.compose.runtime.setValue -import androidx.compose.ui.text.font.FontWeight -import androidx.compose.ui.Modifier -import androidx.compose.ui.input.nestedscroll.nestedScroll -import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.tooling.preview.Preview -import androidx.lifecycle.compose.dropUnlessResumed -import com.maxkeppeker.sheets.core.models.base.Header -import com.maxkeppeker.sheets.core.models.base.rememberUseCaseState -import com.maxkeppeler.sheets.list.ListDialog -import com.maxkeppeler.sheets.list.models.ListOption -import com.maxkeppeler.sheets.list.models.ListSelection -import com.ramcosta.composedestinations.annotation.Destination -import com.ramcosta.composedestinations.annotation.RootGraph -import com.ramcosta.composedestinations.navigation.DestinationsNavigator -import com.ramcosta.composedestinations.navigation.EmptyDestinationsNavigator -import com.rifsxd.ksunext.Natives -import com.rifsxd.ksunext.ksuApp -import com.rifsxd.ksunext.R -import com.rifsxd.ksunext.ui.component.rememberCustomDialog -import com.rifsxd.ksunext.ui.component.SwitchItem -import com.rifsxd.ksunext.ui.util.LocaleHelper -import com.rifsxd.ksunext.ui.util.LocalSnackbarHost -import com.rifsxd.ksunext.ui.util.* -import java.util.Locale - -/** - * @author rifsxd - * @date 2025/6/1. - */ -@OptIn(ExperimentalMaterial3Api::class) -@Destination -@Composable -fun CustomizationScreen(navigator: DestinationsNavigator) { - val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState()) - val snackBarHost = LocalSnackbarHost.current - - val isManager = Natives.becomeManager(ksuApp.packageName) - val ksuVersion = if (isManager) Natives.version else null - - Scaffold( - topBar = { - TopBar( - onBack = dropUnlessResumed { - navigator.popBackStack() - }, - scrollBehavior = scrollBehavior - ) - }, - snackbarHost = { SnackbarHost(snackBarHost) }, - contentWindowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal) - ) { paddingValues -> - - Column( - modifier = Modifier - .padding(paddingValues) - .nestedScroll(scrollBehavior.nestedScrollConnection) - .verticalScroll(rememberScrollState()) - ) { - - val context = LocalContext.current - val scope = rememberCoroutineScope() - - val prefs = context.getSharedPreferences("settings", Context.MODE_PRIVATE) - - // Track language state with current app locale - var currentAppLocale by remember { mutableStateOf(LocaleHelper.getCurrentAppLocale(context)) } - - // Listen for preference changes - LaunchedEffect(Unit) { - currentAppLocale = LocaleHelper.getCurrentAppLocale(context) - } - - // Language setting with selection dialog - val languageDialog = rememberCustomDialog { dismiss -> - // Check if should use system language settings - if (LocaleHelper.useSystemLanguageSettings) { - // Android 13+ - Jump to system settings - LocaleHelper.launchSystemLanguageSettings(context) - dismiss() - } else { - // Android < 13 - Show app language selector - // Dynamically detect supported locales from resources - val supportedLocales = remember { - val locales = mutableListOf() - - // Add system default first - locales.add(java.util.Locale.ROOT) // This will represent "System Default" - - // Dynamically detect available locales by checking resource directories - val resourceDirs = listOf( - "ar", "bg", "de", "fa", "fr", "hu", "in", "it", - "ja", "ko", "pl", "pt-rBR", "ru", "th", "tr", - "uk", "vi", "zh-rCN", "zh-rTW" - ) - - resourceDirs.forEach { dir -> - try { - val locale = when { - dir.contains("-r") -> { - val parts = dir.split("-r") - java.util.Locale.Builder() - .setLanguage(parts[0]) - .setRegion(parts[1]) - .build() - } - else -> java.util.Locale.Builder() - .setLanguage(dir) - .build() - } - - // Test if this locale has translated resources - val config = android.content.res.Configuration() - config.setLocale(locale) - val localizedContext = context.createConfigurationContext(config) - - // Try to get a translated string to verify the locale is supported - val testString = localizedContext.getString(R.string.settings_language) - val defaultString = context.getString(R.string.settings_language) - - // If the string is different or it's English, it's supported - if (testString != defaultString || locale.language == "en") { - locales.add(locale) - } - } catch (e: Exception) { - // Skip unsupported locales - } - } - - // Sort by display name - val sortedLocales = locales.drop(1).sortedBy { it.getDisplayName(it) } - mutableListOf().apply { - add(locales.first()) // System default first - addAll(sortedLocales) - } - } - - val allOptions = supportedLocales.map { locale -> - val tag = if (locale == java.util.Locale.ROOT) { - "system" - } else if (locale.country.isEmpty()) { - locale.language - } else { - "${locale.language}_${locale.country}" - } - - val displayName = if (locale == java.util.Locale.ROOT) { - context.getString(R.string.system_default) - } else { - locale.getDisplayName(locale) - } - - tag to displayName - } - - val currentLocale = prefs.getString("app_locale", "system") ?: "system" - val options = allOptions.map { (tag, displayName) -> - ListOption( - titleText = displayName, - selected = currentLocale == tag - ) - } - - var selectedIndex by remember { - mutableIntStateOf(allOptions.indexOfFirst { (tag, _) -> currentLocale == tag }) - } - - ListDialog( - state = rememberUseCaseState( - visible = true, - onFinishedRequest = { - if (selectedIndex >= 0 && selectedIndex < allOptions.size) { - val newLocale = allOptions[selectedIndex].first - prefs.edit().putString("app_locale", newLocale).apply() - - // Update local state immediately - currentAppLocale = LocaleHelper.getCurrentAppLocale(context) - - // Apply locale change immediately for Android < 13 - LocaleHelper.restartActivity(context) - } - dismiss() - }, - onCloseRequest = { - dismiss() - } - ), - header = Header.Default( - title = stringResource(R.string.settings_language), - ), - selection = ListSelection.Single( - showRadioButtons = true, - options = options - ) { index, _ -> - selectedIndex = index - } - ) - } - } - - val language = stringResource(id = R.string.settings_language) - - // Compute display name based on current app locale (similar to the reference implementation) - val currentLanguageDisplay = remember(currentAppLocale) { - val locale = currentAppLocale - if (locale != null) { - locale.getDisplayName(locale) - } else { - context.getString(R.string.system_default) - } - } - - ListItem( - leadingContent = { Icon(Icons.Filled.Translate, language) }, - headlineContent = { Text( - text = language, - style = MaterialTheme.typography.titleMedium, - fontWeight = FontWeight.SemiBold, - ) }, - supportingContent = { Text(currentLanguageDisplay) }, - modifier = Modifier.clickable { - languageDialog.show() - } - ) - - var useBanner by rememberSaveable { - mutableStateOf( - prefs.getBoolean("use_banner", true) - ) - } - if (ksuVersion != null) { - SwitchItem( - icon = Icons.Filled.ViewCarousel, - title = stringResource(id = R.string.settings_banner), - summary = stringResource(id = R.string.settings_banner_summary), - checked = useBanner - ) { - prefs.edit().putBoolean("use_banner", it).apply() - useBanner = it - } - } - - var enableAmoled by rememberSaveable { - mutableStateOf( - prefs.getBoolean("enable_amoled", false) - ) - } - var showRestartDialog by remember { mutableStateOf(false) } - if (isSystemInDarkTheme()) { - SwitchItem( - icon = Icons.Filled.Contrast, - title = stringResource(id = R.string.settings_amoled_mode), - summary = stringResource(id = R.string.settings_amoled_mode_summary), - checked = enableAmoled - ) { checked -> - prefs.edit().putBoolean("enable_amoled", checked).apply() - enableAmoled = checked - showRestartDialog = true - } - if (showRestartDialog) { - AlertDialog( - onDismissRequest = { showRestartDialog = false }, - title = { Text( - text = stringResource(R.string.restart_required), - style = MaterialTheme.typography.titleLarge, - fontWeight = FontWeight.SemiBold - ) }, - text = { Text(stringResource(R.string.restart_app_message)) }, - confirmButton = { - TextButton(onClick = { - showRestartDialog = false - // Restart the app - val packageManager = context.packageManager - val intent = packageManager.getLaunchIntentForPackage(context.packageName) - intent?.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_NEW_TASK) - context.startActivity(intent) - Runtime.getRuntime().exit(0) - }) { - Text(stringResource(R.string.restart_app)) - } - }, - dismissButton = { - TextButton(onClick = { showRestartDialog = false }) { - Text(stringResource(R.string.later)) - } - } - ) - } - } - } - } -} - -@OptIn(ExperimentalMaterial3Api::class) -@Composable -private fun TopBar( - onBack: () -> Unit = {}, - scrollBehavior: TopAppBarScrollBehavior? = null -) { - TopAppBar( - title = { Text( - text = stringResource(R.string.customization), - style = MaterialTheme.typography.titleLarge, - fontWeight = FontWeight.Black, - ) }, navigationIcon = { - IconButton( - onClick = onBack - ) { Icon(Icons.AutoMirrored.Filled.ArrowBack, contentDescription = null) } - }, - windowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal), - scrollBehavior = scrollBehavior - ) -} - -@Preview -@Composable -private fun CustomizationPreview() { - CustomizationScreen(EmptyDestinationsNavigator) -} diff --git a/manager/app/src/main/java/com/rifsxd/ksunext/ui/screen/Developer.kt b/manager/app/src/main/java/com/rifsxd/ksunext/ui/screen/Developer.kt deleted file mode 100644 index b67fa4e6..00000000 --- a/manager/app/src/main/java/com/rifsxd/ksunext/ui/screen/Developer.kt +++ /dev/null @@ -1,152 +0,0 @@ -package com.rifsxd.ksunext.ui.screen - -import android.content.Context -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.WindowInsets -import androidx.compose.foundation.layout.WindowInsetsSides -import androidx.compose.foundation.layout.only -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.safeDrawing -import androidx.compose.foundation.rememberScrollState -import androidx.compose.foundation.verticalScroll -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.automirrored.filled.* -import androidx.compose.material.icons.filled.* -import androidx.compose.material3.ExperimentalMaterial3Api -import androidx.compose.material3.Icon -import androidx.compose.material3.IconButton -import androidx.compose.material3.Scaffold -import androidx.compose.material3.SnackbarHost -import androidx.compose.material3.Text -import androidx.compose.material3.TopAppBar -import androidx.compose.material3.TopAppBarDefaults -import androidx.compose.material3.TopAppBarScrollBehavior -import androidx.compose.material3.rememberTopAppBarState -import androidx.compose.material3.MaterialTheme -import androidx.compose.runtime.Composable -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.rememberCoroutineScope -import androidx.compose.runtime.saveable.rememberSaveable -import androidx.compose.runtime.setValue -import androidx.compose.ui.text.font.FontWeight -import androidx.compose.ui.Modifier -import androidx.compose.ui.input.nestedscroll.nestedScroll -import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.dp -import com.ramcosta.composedestinations.annotation.Destination -import com.ramcosta.composedestinations.annotation.RootGraph -import com.ramcosta.composedestinations.navigation.DestinationsNavigator -import com.ramcosta.composedestinations.navigation.EmptyDestinationsNavigator -import kotlinx.coroutines.launch -import com.rifsxd.ksunext.Natives -import com.rifsxd.ksunext.ksuApp -import com.rifsxd.ksunext.R -import com.rifsxd.ksunext.ui.component.SwitchItem -import com.rifsxd.ksunext.ui.util.LocalSnackbarHost -import com.rifsxd.ksunext.ui.util.* - -/** - * @author rifsxd - * @date 2025/6/15. - */ -@OptIn(ExperimentalMaterial3Api::class) -@Destination -@Composable -fun DeveloperScreen(navigator: DestinationsNavigator) { - val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState()) - val snackBarHost = LocalSnackbarHost.current - - val isManager = Natives.becomeManager(ksuApp.packageName) - val ksuVersion = if (isManager) Natives.version else null - - Scaffold( - topBar = { - TopBar( - onBack = { navigator.popBackStack() }, - scrollBehavior = scrollBehavior - ) - }, - snackbarHost = { SnackbarHost(snackBarHost) }, - contentWindowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal) - ) { paddingValues -> - - Column( - modifier = Modifier - .padding(paddingValues) - .nestedScroll(scrollBehavior.nestedScrollConnection) - .verticalScroll(rememberScrollState()) - ) { - val context = LocalContext.current - val scope = rememberCoroutineScope() - val prefs = context.getSharedPreferences("settings", Context.MODE_PRIVATE) - - // --- Developer Options Switch --- - var developerOptionsEnabled by rememberSaveable { - mutableStateOf( - prefs.getBoolean("enable_developer_options", false) - ) - } - if (ksuVersion != null) { - SwitchItem( - icon = Icons.Filled.DeveloperMode, - title = stringResource(id = R.string.enable_developer_options), - summary = stringResource(id = R.string.enable_developer_options_summary), - checked = developerOptionsEnabled - ) { - prefs.edit().putBoolean("enable_developer_options", it).apply() - developerOptionsEnabled = it - } - } - - var enableWebDebugging by rememberSaveable { - mutableStateOf( - prefs.getBoolean("enable_web_debugging", false) - ) - } - if (ksuVersion != null) { - SwitchItem( - enabled = developerOptionsEnabled, - icon = Icons.Filled.Web, - title = stringResource(id = R.string.enable_web_debugging), - summary = stringResource(id = R.string.enable_web_debugging_summary), - checked = enableWebDebugging - ) { - prefs.edit().putBoolean("enable_web_debugging", it).apply() - enableWebDebugging = it - } - } - } - } -} - -@OptIn(ExperimentalMaterial3Api::class) -@Composable -private fun TopBar( - onBack: () -> Unit = {}, - scrollBehavior: TopAppBarScrollBehavior? = null -) { - TopAppBar( - title = { Text( - text = stringResource(R.string.developer), - style = MaterialTheme.typography.titleLarge, - fontWeight = FontWeight.Black, - ) }, navigationIcon = { - IconButton( - onClick = onBack - ) { Icon(Icons.AutoMirrored.Filled.ArrowBack, contentDescription = null) } - }, - windowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal), - scrollBehavior = scrollBehavior - ) -} - -@Preview -@Composable -private fun DeveloperPreview() { - DeveloperScreen(EmptyDestinationsNavigator) -} diff --git a/manager/app/src/main/java/com/rifsxd/ksunext/ui/screen/ExecuteModuleAction.kt b/manager/app/src/main/java/com/rifsxd/ksunext/ui/screen/ExecuteModuleAction.kt deleted file mode 100644 index a8ac166a..00000000 --- a/manager/app/src/main/java/com/rifsxd/ksunext/ui/screen/ExecuteModuleAction.kt +++ /dev/null @@ -1,205 +0,0 @@ -package com.rifsxd.ksunext.ui.screen - -import android.content.Context -import android.os.Environment -import androidx.activity.compose.BackHandler -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.foundation.layout.navigationBarsPadding -import androidx.compose.foundation.layout.WindowInsets -import androidx.compose.foundation.layout.WindowInsetsSides -import androidx.compose.foundation.layout.defaultMinSize -import androidx.compose.foundation.layout.safeDrawing -import androidx.compose.foundation.layout.only -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.automirrored.filled.ArrowBack -import androidx.compose.material.icons.filled.Close -import androidx.compose.material.icons.filled.Save -import androidx.compose.material3.ExperimentalMaterial3Api -import androidx.compose.material3.ExtendedFloatingActionButton -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.DisposableEffect -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.text.font.FontWeight -import androidx.compose.ui.Modifier -import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.input.key.Key -import androidx.compose.ui.input.key.key -import androidx.compose.ui.platform.LocalView -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.text.font.FontFamily -import androidx.compose.ui.unit.dp -import androidx.lifecycle.compose.dropUnlessResumed -import com.ramcosta.composedestinations.annotation.Destination -import com.ramcosta.composedestinations.annotation.RootGraph -import com.ramcosta.composedestinations.navigation.DestinationsNavigator -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.launch -import kotlinx.coroutines.withContext -import com.rifsxd.ksunext.R -import com.rifsxd.ksunext.ui.component.KeyEventBlocker -import com.rifsxd.ksunext.ui.util.LocalSnackbarHost -import com.rifsxd.ksunext.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("") } - var tempText: String - val logContent = rememberSaveable { StringBuilder() } - val snackBarHost = LocalSnackbarHost.current - val scope = rememberCoroutineScope() - val scrollState = rememberScrollState() - var actionResult: Boolean - var isActionRunning by rememberSaveable { mutableStateOf(true) } - - val context = LocalContext.current - // Read developer options from SharedPreferences - val prefs = context.getSharedPreferences("settings", Context.MODE_PRIVATE) - val developerOptionsEnabled = prefs.getBoolean("enable_developer_options", false) - - val view = LocalView.current - DisposableEffect(isActionRunning) { - view.keepScreenOn = isActionRunning - onDispose { - view.keepScreenOn = false - } - } - - BackHandler(enabled = isActionRunning) { - // Disable back button if action is running - } - - LaunchedEffect(Unit) { - if (text.isNotEmpty()) { - return@LaunchedEffect - } - withContext(Dispatchers.IO) { - runModuleAction( - moduleId = moduleId, - onStdout = { - tempText = "$it\n" - if (tempText.startsWith("")) { // clear command - text = tempText.substring(6) - } else { - text += tempText - } - logContent.append(it).append("\n") - }, - onStderr = { - logContent.append(it).append("\n") - } - ).let { - actionResult = it - } - } - isActionRunning = false - } - - Scaffold( - topBar = { - TopBar( - isActionRunning = isActionRunning, - onBack = dropUnlessResumed { - navigator.popBackStack() - }, - onSave = { - if (!isActionRunning) { - 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_Next_module_action_log_${date}.log" - ) - file.writeText(logContent.toString()) - snackBarHost.showSnackbar("Log saved to ${file.absolutePath}") - } - } - } - ) - }, - floatingActionButton = { - if (!isActionRunning) { - ExtendedFloatingActionButton( - text = { Text(text = stringResource(R.string.close)) }, - icon = { Icon(Icons.Filled.Close, contentDescription = null) }, - onClick = { - navigator.popBackStack() - } - ) - } - }, - contentWindowInsets = WindowInsets.safeDrawing, - 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 = if (developerOptionsEnabled) logContent.toString() else text, - fontSize = MaterialTheme.typography.bodySmall.fontSize, - fontFamily = FontFamily.Monospace, - lineHeight = MaterialTheme.typography.bodySmall.lineHeight, - ) - } - } -} - -@OptIn(ExperimentalMaterial3Api::class) -@Composable -private fun TopBar(isActionRunning: Boolean, onBack: () -> Unit = {}, onSave: () -> Unit = {}) { - TopAppBar( - title = { Text( - text = stringResource(R.string.action), - style = MaterialTheme.typography.titleLarge, - fontWeight = FontWeight.Black, - ) }, - navigationIcon = { - IconButton( - onClick = onBack, - enabled = !isActionRunning - ) { Icon(Icons.AutoMirrored.Filled.ArrowBack, contentDescription = null) } - }, - actions = { - IconButton( - onClick = onSave, - enabled = !isActionRunning - ) { - Icon( - imageVector = Icons.Filled.Save, - contentDescription = stringResource(id = R.string.save_log), - ) - } - } - ) -} diff --git a/manager/app/src/main/java/com/rifsxd/ksunext/ui/screen/Flash.kt b/manager/app/src/main/java/com/rifsxd/ksunext/ui/screen/Flash.kt deleted file mode 100644 index 88f336f8..00000000 --- a/manager/app/src/main/java/com/rifsxd/ksunext/ui/screen/Flash.kt +++ /dev/null @@ -1,445 +0,0 @@ -package com.rifsxd.ksunext.ui.screen - -import android.app.Activity -import android.content.Context -import android.net.Uri -import android.os.Environment -import android.os.Parcelable -import androidx.activity.compose.BackHandler -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.navigationBarsPadding -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 -import androidx.compose.material.icons.automirrored.filled.ArrowBack -import androidx.compose.material.icons.filled.Refresh -import androidx.compose.material.icons.filled.Close -import androidx.compose.material.icons.filled.Save -import androidx.compose.material3.ExperimentalMaterial3Api -import androidx.compose.material3.ExtendedFloatingActionButton -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.DisposableEffect -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.text.font.FontWeight -import androidx.compose.ui.Alignment -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.platform.LocalContext -import androidx.compose.ui.platform.LocalView -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.text.font.FontFamily -import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.dp -import androidx.lifecycle.compose.dropUnlessResumed -import com.ramcosta.composedestinations.annotation.Destination -import com.ramcosta.composedestinations.annotation.RootGraph -import com.ramcosta.composedestinations.navigation.DestinationsNavigator -import com.ramcosta.composedestinations.navigation.EmptyDestinationsNavigator -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.launch -import kotlinx.coroutines.withContext -import kotlinx.parcelize.Parcelize -import com.rifsxd.ksunext.R -import com.rifsxd.ksunext.ui.component.rememberConfirmDialog -import com.rifsxd.ksunext.ui.component.ConfirmResult -import com.rifsxd.ksunext.ui.component.KeyEventBlocker -import com.rifsxd.ksunext.ui.theme.ORANGE -import com.rifsxd.ksunext.ui.theme.GREEN -import com.rifsxd.ksunext.ui.theme.RED -import com.rifsxd.ksunext.ui.util.FlashResult -import com.rifsxd.ksunext.ui.util.LkmSelection -import com.rifsxd.ksunext.ui.util.LocalSnackbarHost -import com.rifsxd.ksunext.ui.util.flashModule -import com.rifsxd.ksunext.ui.util.installBoot -import com.rifsxd.ksunext.ui.util.reboot -import com.rifsxd.ksunext.ui.util.restoreBoot -import com.rifsxd.ksunext.ui.util.uninstallPermanently -import java.io.File -import java.text.SimpleDateFormat -import java.util.Date -import java.util.Locale - -enum class FlashingStatus { - FLASHING, - SUCCESS, - FAILED -} - -fun Context.findActivity(): Activity? = when (this) { - is Activity -> this - is android.content.ContextWrapper -> baseContext.findActivity() - else -> null -} - -// Lets you flash modules sequentially when mutiple zipUris are selected -fun flashModulesSequentially( - uris: List, - onStdout: (String) -> Unit, - onStderr: (String) -> Unit -): FlashResult { - for (uri in uris) { - flashModule(uri, onStdout, onStderr).apply { - if (code != 0) { - return FlashResult(code, err, showReboot) - } - } - } - return FlashResult(0, "", true) -} - -/** - * @author weishu - * @date 2023/1/1. - */ -@OptIn(ExperimentalMaterial3Api::class) -@Composable -@Destination -fun FlashScreen( - navigator: DestinationsNavigator, - flashIt: FlashIt, - finishIntent: Boolean = false -) { - - var text by rememberSaveable { mutableStateOf("") } - var tempText: String - val logContent = rememberSaveable { StringBuilder() } - var showFloatAction by rememberSaveable { mutableStateOf(false) } - - val snackBarHost = LocalSnackbarHost.current - val scope = rememberCoroutineScope() - val scrollState = rememberScrollState() - val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState()) - var flashing by rememberSaveable { - mutableStateOf(FlashingStatus.FLASHING) - } - - val context = LocalContext.current - - val prefs = context.getSharedPreferences("settings", Context.MODE_PRIVATE) - val developerOptionsEnabled = prefs.getBoolean("enable_developer_options", false) - - val activity = context.findActivity() - - val view = LocalView.current - DisposableEffect(flashing) { - view.keepScreenOn = flashing == FlashingStatus.FLASHING - onDispose { - view.keepScreenOn = false - } - } - - BackHandler(enabled = flashing == FlashingStatus.FLASHING) { - // Disable back button if flashing is running - } - - BackHandler(enabled = flashing != FlashingStatus.FLASHING) { - navigator.popBackStack() - if (finishIntent) activity?.finish() - } - - val confirmDialog = rememberConfirmDialog() - var confirmed by rememberSaveable { mutableStateOf(flashIt !is FlashIt.FlashModules) } - var pendingFlashIt by rememberSaveable { mutableStateOf(null) } - var hasFlashed by rememberSaveable { mutableStateOf(false) } - - LaunchedEffect(flashIt) { - if (flashIt is FlashIt.FlashModules && !confirmed) { - val uris = flashIt.uris - val moduleNames = - uris.mapIndexed { index, uri -> "\n${index + 1}. ${uri.getFileName(context)}" } - .joinToString("") - val confirmContent = - context.getString(R.string.module_install_prompt_with_name, moduleNames) - val confirmTitle = context.getString(R.string.module) - val result = confirmDialog.awaitConfirm( - title = confirmTitle, - content = confirmContent, - markdown = true - ) - if (result == ConfirmResult.Confirmed) { - confirmed = true - pendingFlashIt = flashIt - } else { - // User cancelled, go back - navigator.popBackStack() - if (finishIntent) activity?.finish() - } - } else { - confirmed = true - pendingFlashIt = flashIt - } - } - - LaunchedEffect(confirmed, pendingFlashIt) { - if (!confirmed || pendingFlashIt == null || text.isNotEmpty() || hasFlashed) return@LaunchedEffect - hasFlashed = true - withContext(Dispatchers.IO) { - flashIt(pendingFlashIt!!, onStdout = { - tempText = "$it\n" - if (tempText.startsWith("")) { // clear command - text = tempText.substring(6) - } else { - text += tempText - } - logContent.append(it).append("\n") - }, onStderr = { - logContent.append(it).append("\n") - }).apply { - if (code != 0) { - text += "Error code: $code.\n $err Please save and check the log.\n" - } - if (showReboot) { - text += "\n\n\n" - showFloatAction = true - } - flashing = if (code == 0) FlashingStatus.SUCCESS else FlashingStatus.FAILED - } - } - } - - Scaffold( - topBar = { - TopBar( - flashing, - onBack = dropUnlessResumed { - navigator.popBackStack() - if (finishIntent) activity?.finish() - }, - 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_Next_install_log_${date}.log" - ) - file.writeText(logContent.toString()) - snackBarHost.showSnackbar("Log saved to ${file.absolutePath}") - } - }, - scrollBehavior = scrollBehavior - ) - }, - floatingActionButton = { - if (flashIt is FlashIt.FlashModules && (flashing == FlashingStatus.SUCCESS)) { - // Reboot button for modules flashing - ExtendedFloatingActionButton( - onClick = { - scope.launch { - withContext(Dispatchers.IO) { - reboot() - } - } - }, - icon = { Icon(Icons.Filled.Refresh, contentDescription = stringResource(R.string.reboot)) }, - text = { Text(text = stringResource(R.string.reboot)) } - ) - } - - if (flashIt is FlashIt.FlashModules && (flashing == FlashingStatus.FAILED)) { - // Close button for modules flashing - ExtendedFloatingActionButton( - text = { Text(text = stringResource(R.string.close)) }, - icon = { Icon(Icons.Filled.Close, contentDescription = null) }, - onClick = { - navigator.popBackStack() - if (finishIntent) activity?.finish() - } - ) - } - - if (flashIt is FlashIt.FlashBoot && (flashing == FlashingStatus.SUCCESS || flashing == FlashingStatus.FAILED)) { - val isLocalPatch = flashIt.boot != null && !flashIt.ota - val isDirectOrOta = flashIt.boot == null || flashIt.ota - - if (flashing == FlashingStatus.FAILED) { - // Always show close on failure - ExtendedFloatingActionButton( - text = { Text(text = stringResource(R.string.close)) }, - icon = { Icon(Icons.Filled.Close, contentDescription = null) }, - onClick = { - navigator.popBackStack() - } - ) - } else if (flashing == FlashingStatus.SUCCESS) { - if (isLocalPatch) { - // Local patching: show only Close - ExtendedFloatingActionButton( - text = { Text(text = stringResource(R.string.close)) }, - icon = { Icon(Icons.Filled.Close, contentDescription = null) }, - onClick = { - navigator.popBackStack() - } - ) - } else if (isDirectOrOta) { - // Direct install or OTA inactive slot: show only Reboot - ExtendedFloatingActionButton( - onClick = { - scope.launch { - withContext(Dispatchers.IO) { - reboot() - } - } - }, - icon = { Icon(Icons.Filled.Refresh, contentDescription = stringResource(R.string.reboot)) }, - text = { Text(text = stringResource(R.string.reboot)) } - ) - } - } - } - }, - contentWindowInsets = WindowInsets.safeDrawing, - snackbarHost = { SnackbarHost(hostState = snackBarHost) } - ) { innerPadding -> - KeyEventBlocker { - it.key == Key.VolumeDown || it.key == Key.VolumeUp - } - Column( - modifier = Modifier - .fillMaxSize(1f) - .padding(innerPadding) - .nestedScroll(scrollBehavior.nestedScrollConnection) - .verticalScroll(scrollState), - ) { - LaunchedEffect(text) { - scrollState.animateScrollTo(scrollState.maxValue) - } - Text( - modifier = Modifier.padding(8.dp), - text = if (developerOptionsEnabled) logContent.toString() else text, - fontSize = MaterialTheme.typography.bodySmall.fontSize, - fontFamily = FontFamily.Monospace, - lineHeight = MaterialTheme.typography.bodySmall.lineHeight, - ) - } - } -} - -fun Uri.getFileName(context: Context): String { - val contentResolver = context.contentResolver - val cursor = contentResolver.query(this, null, null, null, null) - return cursor?.use { - val nameIndex = it.getColumnIndex(android.provider.OpenableColumns.DISPLAY_NAME) - if (it.moveToFirst() && nameIndex != -1) { - it.getString(nameIndex) - } else { - this.lastPathSegment ?: "unknown.zip" - } - } ?: (this.lastPathSegment ?: "unknown.zip") -} - -@Parcelize -sealed class FlashIt : Parcelable { - data class FlashBoot(val boot: Uri? = null, val lkm: LkmSelection, val ota: Boolean) : - FlashIt() - - data class FlashModules(val uris: List) : FlashIt() - - data object FlashRestore : FlashIt() - - data object FlashUninstall : FlashIt() -} - -fun flashIt( - flashIt: FlashIt, - onStdout: (String) -> Unit, - onStderr: (String) -> Unit -): FlashResult { - return when (flashIt) { - is FlashIt.FlashBoot -> installBoot( - flashIt.boot, - flashIt.lkm, - flashIt.ota, - onStdout, - onStderr - ) - - is FlashIt.FlashModules -> { - flashModulesSequentially(flashIt.uris, onStdout, onStderr) - } - - FlashIt.FlashRestore -> restoreBoot(onStdout, onStderr) - - FlashIt.FlashUninstall -> uninstallPermanently(onStdout, onStderr) - } -} - -@OptIn(ExperimentalMaterial3Api::class) -@Composable -private fun TopBar( - status: FlashingStatus, - onBack: () -> Unit = {}, - onSave: () -> Unit = {}, - scrollBehavior: TopAppBarScrollBehavior? = null -) { - TopAppBar( - title = { - Text( - stringResource( - when (status) { - FlashingStatus.FLASHING -> R.string.flashing - FlashingStatus.SUCCESS -> R.string.flash_success - FlashingStatus.FAILED -> R.string.flash_failed - } - ), - style = MaterialTheme.typography.titleLarge, - fontWeight = FontWeight.Black, - color = when (status) { - FlashingStatus.FLASHING -> ORANGE - FlashingStatus.SUCCESS -> GREEN - FlashingStatus.FAILED -> RED - } - ) - }, - navigationIcon = { - IconButton( - onClick = { if (status != FlashingStatus.FLASHING) onBack() }, - enabled = status != FlashingStatus.FLASHING - ) { Icon(Icons.AutoMirrored.Filled.ArrowBack, contentDescription = null) } - }, - actions = { - IconButton( - onClick = { if (status != FlashingStatus.FLASHING) onSave() }, - enabled = status != FlashingStatus.FLASHING - ) { - Icon( - imageVector = Icons.Filled.Save, - contentDescription = "Localized description" - ) - } - }, - windowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal), - scrollBehavior = scrollBehavior - ) -} - -@Preview -@Composable -fun InstallPreview() { - InstallScreen(EmptyDestinationsNavigator) -} \ No newline at end of file diff --git a/manager/app/src/main/java/com/rifsxd/ksunext/ui/screen/Home.kt b/manager/app/src/main/java/com/rifsxd/ksunext/ui/screen/Home.kt deleted file mode 100644 index f26be248..00000000 --- a/manager/app/src/main/java/com/rifsxd/ksunext/ui/screen/Home.kt +++ /dev/null @@ -1,885 +0,0 @@ -package com.rifsxd.ksunext.ui.screen - -import android.content.Context -import android.os.Build -import android.os.PowerManager -import android.os.Handler -import android.os.Looper -import android.system.Os -import android.widget.Toast -import androidx.annotation.StringRes -import androidx.compose.animation.core.tween -import androidx.compose.animation.core.animateFloatAsState -import androidx.compose.ui.graphics.graphicsLayer -import androidx.compose.ui.text.font.FontWeight -import androidx.compose.animation.* -import androidx.compose.foundation.clickable -import androidx.compose.foundation.interaction.MutableInteractionSource -import androidx.compose.foundation.layout.* -import androidx.compose.foundation.rememberScrollState -import androidx.compose.foundation.verticalScroll -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.automirrored.filled.* -import androidx.compose.material.icons.filled.* -import androidx.compose.material.icons.outlined.* -import androidx.compose.material3.* -import androidx.compose.runtime.* -import androidx.compose.runtime.saveable.rememberSaveable -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.graphics.painter.Painter -import androidx.compose.ui.graphics.vector.ImageVector -import androidx.compose.ui.input.nestedscroll.nestedScroll -import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.platform.LocalUriHandler -import androidx.compose.ui.res.painterResource -import androidx.compose.ui.res.pluralStringResource -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.text.intl.Locale -import androidx.compose.ui.text.toUpperCase -import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.dp -import androidx.core.content.pm.PackageInfoCompat -import androidx.lifecycle.viewmodel.compose.viewModel -import com.dergoogler.mmrl.ui.component.LabelItem -import com.dergoogler.mmrl.ui.component.LabelItemDefaults -import com.dergoogler.mmrl.ui.component.text.TextRow -import com.ramcosta.composedestinations.annotation.Destination -import com.ramcosta.composedestinations.annotation.RootGraph -import com.ramcosta.composedestinations.generated.destinations.InstallScreenDestination -import com.ramcosta.composedestinations.navigation.DestinationsNavigator -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.withContext -import com.rifsxd.ksunext.* -import com.rifsxd.ksunext.R -import com.rifsxd.ksunext.ui.component.rememberConfirmDialog -import com.rifsxd.ksunext.ui.util.* -import com.rifsxd.ksunext.ui.util.module.LatestVersionInfo -import java.util.* - -@OptIn(ExperimentalMaterial3Api::class) -@Destination(start = true) -@Composable -fun HomeScreen(navigator: DestinationsNavigator) { - val kernelVersion = getKernelVersion() - val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState()) - - val isManager = Natives.becomeManager(ksuApp.packageName) - val ksuVersion = if (isManager) Natives.version else null - - val context = LocalContext.current - val prefs = context.getSharedPreferences("settings", Context.MODE_PRIVATE) - val developerOptionsEnabled = prefs.getBoolean("enable_developer_options", false) - - Scaffold( - topBar = { - TopBar( - kernelVersion, - ksuVersion, - onInstallClick = { - navigator.navigate(InstallScreenDestination) - }, - scrollBehavior = scrollBehavior - ) - }, - contentWindowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal) - ) { innerPadding -> - Column( - modifier = Modifier - .padding(innerPadding) - .nestedScroll(scrollBehavior.nestedScrollConnection) - .verticalScroll(rememberScrollState()) - .padding(horizontal = 16.dp), - verticalArrangement = Arrangement.spacedBy(16.dp) - ) { - val lkmMode = ksuVersion?.let { - if (it >= Natives.MINIMAL_SUPPORTED_KERNEL_LKM && kernelVersion.isGKI()) Natives.isLkmMode else null - } - - StatusCard(kernelVersion, ksuVersion, lkmMode) { - navigator.navigate(InstallScreenDestination) - } - - if (ksuVersion != null && rootAvailable()) { - Row( - modifier = Modifier - .fillMaxWidth() - .height(IntrinsicSize.Min), - horizontalArrangement = Arrangement.spacedBy(14.dp) - ) { - Box(modifier = Modifier.weight(1f)) { SuperuserCard() } - Box(modifier = Modifier.weight(1f)) { ModuleCard() } - } - } - - if (isManager && Natives.requireNewKernel()) { - WarningCard( - stringResource(id = R.string.require_kernel_version).format( - ksuVersion, Natives.MINIMAL_SUPPORTED_KERNEL - ) - ) - } - if (ksuVersion != null && !rootAvailable()) { - WarningCard( - stringResource(id = R.string.grant_root_failed) - ) - } - val checkUpdate = - LocalContext.current.getSharedPreferences("settings", Context.MODE_PRIVATE) - .getBoolean("check_update", false) - if (checkUpdate) { - UpdateCard() - } - //NextCard() - InfoCard(autoExpand = developerOptionsEnabled) - IssueReportCard() - //EXperimentalCard() - Spacer(Modifier) - } - } -} - -@Composable -private fun SuperuserCard() { - val count = getSuperuserCount() - ElevatedCard( - colors = CardDefaults.elevatedCardColors( - containerColor = MaterialTheme.colorScheme.secondaryContainer - ) - ) { - Box( - modifier = Modifier - .fillMaxSize() - .padding(16.dp), - contentAlignment = Alignment.Center - ) { - Column( - horizontalAlignment = Alignment.CenterHorizontally, - verticalArrangement = Arrangement.spacedBy(4.dp) - ) { - Text( - text = if (count <= 1) { - stringResource(R.string.home_superuser_count_singular) - } else { - stringResource(R.string.home_superuser_count_plural) - }, - style = MaterialTheme.typography.bodySmall - ) - Text( - text = count.toString(), - style = MaterialTheme.typography.titleMedium, - fontWeight = FontWeight.SemiBold - ) - } - } - } -} - -@Composable -private fun ModuleCard() { - val count = getModuleCount() - ElevatedCard( - colors = CardDefaults.elevatedCardColors( - containerColor = MaterialTheme.colorScheme.secondaryContainer - ) - ) { - Box( - modifier = Modifier - .fillMaxSize() - .padding(16.dp), - contentAlignment = Alignment.Center - ) { - Column( - horizontalAlignment = Alignment.CenterHorizontally, - verticalArrangement = Arrangement.spacedBy(4.dp) - ) { - Text( - text = if (count <= 1) { - stringResource(R.string.home_module_count_singular) - } else { - stringResource(R.string.home_module_count_plural) - }, - style = MaterialTheme.typography.bodySmall - ) - Text( - text = count.toString(), - style = MaterialTheme.typography.titleMedium, - fontWeight = FontWeight.SemiBold - ) - } - } - } -} - -@Composable -fun UpdateCard() { - val context = LocalContext.current - val latestVersionInfo = LatestVersionInfo() - val newVersion by produceState(initialValue = latestVersionInfo) { - value = withContext(Dispatchers.IO) { - checkNewVersion() - } - } - - val currentVersionCode = getManagerVersion(context).second - val newVersionCode = newVersion.versionCode - val newVersionUrl = newVersion.downloadUrl - val changelog = newVersion.changelog - - val uriHandler = LocalUriHandler.current - val title = stringResource(id = R.string.module_changelog) - val updateText = stringResource(id = R.string.module_update) - - AnimatedVisibility( - visible = newVersionCode > currentVersionCode, - enter = fadeIn() + expandVertically(), - exit = shrinkVertically() + fadeOut() - ) { - val updateDialog = rememberConfirmDialog(onConfirm = { uriHandler.openUri(newVersionUrl) }) - WarningCard( - message = stringResource(id = R.string.new_version_available).format(newVersionCode), - MaterialTheme.colorScheme.outlineVariant - ) { - if (changelog.isEmpty()) { - uriHandler.openUri(newVersionUrl) - } else { - updateDialog.showConfirm( - title = title, - content = changelog, - markdown = true, - confirm = updateText - ) - } - } - } -} - -@Composable -fun RebootDropdownItem(@StringRes id: Int, reason: String = "") { - DropdownMenuItem(text = { - Text(stringResource(id)) - }, onClick = { - reboot(reason) - }) -} - -@Composable -fun getSeasonalIcon(): ImageVector { - val month = Calendar.getInstance().get(Calendar.MONTH) // 0-11 for January-December - return when (month) { - Calendar.DECEMBER, Calendar.JANUARY, Calendar.FEBRUARY -> Icons.Filled.AcUnit // Winter - Calendar.MARCH, Calendar.APRIL, Calendar.MAY -> Icons.Filled.Spa // Spring - Calendar.JUNE, Calendar.JULY, Calendar.AUGUST -> Icons.Filled.WbSunny // Summer - Calendar.SEPTEMBER, Calendar.OCTOBER, Calendar.NOVEMBER -> Icons.Filled.Forest // Fall - else -> Icons.Filled.Whatshot // Fallback icon - } -} - - -@OptIn(ExperimentalMaterial3Api::class) -@Composable -private fun TopBar( - kernelVersion: KernelVersion, - ksuVersion: Int?, - onInstallClick: () -> Unit, - scrollBehavior: TopAppBarScrollBehavior? = null -) { - var isSpinning by remember { mutableStateOf(false) } - val rotation by animateFloatAsState( - targetValue = if (isSpinning) 360f else 0f, - animationSpec = tween(durationMillis = 800), - finishedListener = { - isSpinning = false - } - ) - - LaunchedEffect(Unit) { - isSpinning = true - } - - TopAppBar( - title = { - Row( - verticalAlignment = Alignment.CenterVertically, - modifier = Modifier.clickable( - indication = null, - interactionSource = remember { MutableInteractionSource() } - ) { - if (!isSpinning) isSpinning = true - } - ) { - Icon( - imageVector = getSeasonalIcon(), - contentDescription = null, - modifier = Modifier - .padding(end = 8.dp) - .graphicsLayer { - rotationZ = rotation - } - ) - Text( - text = stringResource(R.string.app_name), - style = MaterialTheme.typography.titleLarge, - fontWeight = FontWeight.Black, - ) - } - }, - actions = { - if (ksuVersion != null) { - if (kernelVersion.isGKI()) { - IconButton(onClick = onInstallClick) { - Icon( - imageVector = Icons.Filled.Archive, - contentDescription = stringResource(id = R.string.install) - ) - } - } - } - - if (ksuVersion != null) { - var showDropdown by remember { mutableStateOf(false) } - IconButton(onClick = { - showDropdown = true - }) { - Icon( - imageVector = Icons.Filled.PowerSettingsNew, - contentDescription = stringResource(id = R.string.reboot) - ) - - DropdownMenu(expanded = showDropdown, onDismissRequest = { - showDropdown = false - }) { - RebootDropdownItem(id = R.string.reboot) - - val pm = - LocalContext.current.getSystemService(Context.POWER_SERVICE) as PowerManager? - @Suppress("DEPRECATION") - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R && pm?.isRebootingUserspaceSupported == true) { - RebootDropdownItem(id = R.string.reboot_userspace, reason = "userspace") - } - RebootDropdownItem(id = R.string.reboot_recovery, reason = "recovery") - RebootDropdownItem(id = R.string.reboot_bootloader, reason = "bootloader") - RebootDropdownItem(id = R.string.reboot_download, reason = "download") - RebootDropdownItem(id = R.string.reboot_edl, reason = "edl") - } - } - } - }, - windowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal), - scrollBehavior = scrollBehavior - ) -} - - -@Composable -private fun StatusCard( - kernelVersion: KernelVersion, - ksuVersion: Int?, - lkmMode: Boolean?, - moduleUpdateCount: Int = 0, - onClickInstall: () -> Unit = {} -) { - val context = LocalContext.current - var tapCount by remember { mutableStateOf(0) } - - ElevatedCard( - colors = CardDefaults.elevatedCardColors(containerColor = run { - if (ksuVersion != null) MaterialTheme.colorScheme.primaryContainer - else MaterialTheme.colorScheme.errorContainer - }) - ) { - Row( - modifier = Modifier - .fillMaxWidth() - .clickable { - tapCount++ - if (tapCount == 5) { - Toast.makeText(context, "What are you doing? 🤔", Toast.LENGTH_SHORT).show() - } else if (tapCount == 10) { - Toast.makeText(context, "Never gonna give you up! 💜", Toast.LENGTH_SHORT).show() - val url = "https://www.youtube.com/watch?v=dQw4w9WgXcQ" - val intent = android.content.Intent(android.content.Intent.ACTION_VIEW, android.net.Uri.parse(url)) - intent.addFlags(android.content.Intent.FLAG_ACTIVITY_NEW_TASK) - if (ksuVersion != null) { - context.startActivity(intent) - } else if (kernelVersion.isGKI()) { - onClickInstall() - } else { - Toast.makeText(context, "Something weird happened... 🤔", Toast.LENGTH_SHORT).show() - } - } else if (ksuVersion == null && kernelVersion.isGKI()) { - onClickInstall() - } - } - .padding(24.dp), verticalAlignment = Alignment.CenterVertically) { - when { - ksuVersion != null -> { - val workingMode = when { - lkmMode == true -> "LKM" - lkmMode == false || kernelVersion.isGKI() -> "GKI2" - lkmMode == null && kernelVersion.isULegacy() -> "U-LEGACY" - lkmMode == null && kernelVersion.isLegacy() -> "LEGACY" - lkmMode == null && kernelVersion.isGKI1() -> "GKI1" - else -> "NON-STANDARD" - } - - Icon( - imageVector = Icons.Filled.CheckCircle, - contentDescription = stringResource(R.string.home_working) - ) - Column( - modifier = Modifier.padding(start = 20.dp), - verticalArrangement = Arrangement.spacedBy(4.dp) - ) { - val labelStyle = LabelItemDefaults.style - TextRow( - trailingContent = { - Row( - verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.spacedBy(6.dp) - ) { - LabelItem( - icon = if (Natives.isSafeMode) { - { - Icon( - tint = labelStyle.contentColor, - imageVector = Icons.Filled.Security, - contentDescription = null - ) - } - } else { - null - }, - text = { - Text( - text = workingMode, - style = labelStyle.textStyle.copy(color = labelStyle.contentColor), - ) - } - ) - if (isSuCompatDisabled()) { - LabelItem( - icon = { - Icon( - tint = labelStyle.contentColor, - imageVector = Icons.Filled.Warning, - contentDescription = null - ) - }, - text = { - Text( - text = stringResource(R.string.sucompat_disabled), - style = labelStyle.textStyle.copy( - color = labelStyle.contentColor, - ) - ) - } - ) - } - } - } - ) { - Text( - text = stringResource(id = R.string.home_working), - style = MaterialTheme.typography.titleMedium, - fontWeight = FontWeight.SemiBold - ) - } - - Text( - text = stringResource(R.string.home_working_version, ksuVersion), - style = MaterialTheme.typography.bodySmall - ) - } - } - - kernelVersion.isGKI() -> { - Icon(Icons.Filled.NewReleases, stringResource(R.string.home_not_installed)) - Column(Modifier.padding(start = 20.dp)) { - Text( - text = stringResource(R.string.home_not_installed), - style = MaterialTheme.typography.titleMedium - ) - Spacer(Modifier.height(4.dp)) - Text( - text = stringResource(R.string.home_click_to_install), - style = MaterialTheme.typography.bodyMedium - ) - } - } - - else -> { - Icon(Icons.Filled.Cancel, stringResource(R.string.home_failure)) - Column(Modifier.padding(start = 20.dp)) { - Text( - text = stringResource(R.string.home_failure), - style = MaterialTheme.typography.titleMedium - ) - Spacer(Modifier.height(4.dp)) - Text( - text = stringResource(R.string.home_failure_tip), - style = MaterialTheme.typography.bodyMedium - ) - } - } - } - } - } -} - -@Composable -fun WarningCard( - message: String, color: Color = MaterialTheme.colorScheme.error, onClick: (() -> Unit)? = null -) { - ElevatedCard( - colors = CardDefaults.elevatedCardColors( - containerColor = color - ) - ) { - Row( - modifier = Modifier - .fillMaxWidth() - .then(onClick?.let { Modifier.clickable { it() } } ?: Modifier) - .padding(24.dp) - ) { - Text( - text = message, style = MaterialTheme.typography.bodyMedium - ) - } - } -} - -@Composable -private fun InfoCard(autoExpand: Boolean = false) { - val context = LocalContext.current - - val prefs = context.getSharedPreferences("settings", Context.MODE_PRIVATE) - - val isManager = Natives.becomeManager(ksuApp.packageName) - val ksuVersion = if (isManager) Natives.version else null - - var expanded by rememberSaveable { mutableStateOf(false) } - - val developerOptionsEnabled = prefs.getBoolean("enable_developer_options", false) - - LaunchedEffect(autoExpand) { - if (autoExpand) { - expanded = true - } - } - - ElevatedCard { - Column( - modifier = Modifier - .fillMaxWidth() - .padding(start = 24.dp, top = 24.dp, end = 24.dp, bottom = 24.dp) - ) { - @Composable - fun InfoCardItem(label: String, content: String, icon: Any? = null) { - Row(verticalAlignment = Alignment.CenterVertically) { - if (icon != null) { - when (icon) { - is ImageVector -> Icon( - imageVector = icon, - contentDescription = null, - modifier = Modifier.padding(end = 20.dp) - ) - is Painter -> Icon( - painter = icon, - contentDescription = null, - modifier = Modifier.padding(end = 20.dp) - ) - } - } - Column { - Text( - text = label, - style = MaterialTheme.typography.titleMedium, - fontWeight = FontWeight.SemiBold - ) - Text( - text = content, - style = MaterialTheme.typography.bodySmall, - modifier = Modifier.padding(top = 4.dp) - ) - } - } - } - - Column { - val managerVersion = getManagerVersion(context) - InfoCardItem( - label = stringResource(R.string.home_manager_version), - content = if ( - developerOptionsEnabled && - Natives.version >= Natives.MINIMAL_SUPPORTED_MANAGER_UID - ) { - "${managerVersion.first} (${managerVersion.second}) | UID: ${Natives.getManagerUid()}" - } else { - "${managerVersion.first} (${managerVersion.second})" - }, - icon = painterResource(R.drawable.ic_ksu_next), - ) - - if (ksuVersion != null && - Natives.version >= Natives.MINIMAL_SUPPORTED_HOOK_MODE) { - - val hookMode = - Natives.getHookMode() - .takeUnless { it.isNullOrBlank() } - ?: stringResource(R.string.unavailable) - - Spacer(Modifier.height(16.dp)) - - InfoCardItem( - label = stringResource(R.string.hook_mode), - content = hookMode, - icon = Icons.Filled.Phishing, - ) - } - - if (ksuVersion != null) { - Spacer(Modifier.height(16.dp)) - InfoCardItem( - label = stringResource(R.string.home_mount_system), - content = currentMountSystem().ifEmpty { stringResource(R.string.unavailable) }, - icon = Icons.Filled.SettingsSuggest, - ) - - - val suSFS = getSuSFS() - if (suSFS == "Supported") { - val isSUS_SU = getSuSFSFeatures() == "CONFIG_KSU_SUSFS_SUS_SU" - val susSUMode = if (isSUS_SU) { - val mode = susfsSUS_SU_Mode() - val modeString = - if (mode == "2") stringResource(R.string.enabled) else stringResource(R.string.disabled) - "| SuS SU: $modeString" - } else "" - Spacer(Modifier.height(16.dp)) - InfoCardItem( - label = stringResource(R.string.home_susfs_version), - content = "${stringResource(R.string.susfs_supported)} | ${getSuSFSVersion()} (${getSuSFSVariant()}) $susSUMode", - icon = painterResource(R.drawable.ic_sus), - ) - } - - if (Natives.isZygiskEnabled()) { - Spacer(Modifier.height(16.dp)) - InfoCardItem( - label = stringResource(R.string.zygisk_status), - content = stringResource(R.string.enabled), - icon = Icons.Filled.Vaccines - ) - } - } - - if (!expanded) { - Spacer(Modifier.height(16.dp)) - Row( - modifier = Modifier - .fillMaxWidth(), - horizontalArrangement = Arrangement.Center - ) { - IconButton( - onClick = { expanded = true }, - modifier = Modifier.size(36.dp) - ) { - Icon( - imageVector = Icons.Filled.KeyboardArrowDown, - contentDescription = "Show more" - ) - } - } - } - - AnimatedVisibility(visible = expanded) { - val uname = Os.uname() - Column { - Spacer(Modifier.height(16.dp)) - InfoCardItem( - label = stringResource(R.string.home_kernel), - content = "${uname.release} (${uname.machine})", - icon = painterResource(R.drawable.ic_linux), - ) - - Spacer(Modifier.height(16.dp)) - InfoCardItem( - label = stringResource(R.string.home_android), - content = "${Build.VERSION.RELEASE} (${Build.VERSION.SDK_INT})", - icon = Icons.Filled.Android, - ) - - Spacer(Modifier.height(16.dp)) - InfoCardItem( - label = stringResource(R.string.home_abi), - content = Build.SUPPORTED_ABIS.joinToString(", "), - icon = Icons.Filled.Memory, - ) - - Spacer(Modifier.height(16.dp)) - InfoCardItem( - label = stringResource(R.string.home_selinux_status), - content = getSELinuxStatus(), - icon = Icons.Filled.Security, - ) - } - } - } - } - } -} - -@Composable -fun NextCard() { - val uriHandler = LocalUriHandler.current - val url = stringResource(R.string.home_next_kernelsu_repo) - - ElevatedCard { - - Row( - modifier = Modifier - .fillMaxWidth() - .clickable { - uriHandler.openUri(url) - } - .padding(24.dp), verticalAlignment = Alignment.CenterVertically) { - Column { - Text( - text = stringResource(R.string.home_next_kernelsu), - style = MaterialTheme.typography.titleSmall - ) - Spacer(Modifier.height(4.dp)) - Text( - text = stringResource(R.string.home_next_kernelsu_body), - style = MaterialTheme.typography.bodyMedium - ) - } - } - } -} - -@Composable -fun EXperimentalCard() { - /*val uriHandler = LocalUriHandler.current - val url = stringResource(R.string.home_experimental_kernelsu_repo) - */ - - ElevatedCard { - - Row( - modifier = Modifier - .fillMaxWidth() - /*.clickable { - uriHandler.openUri(url) - } - */ - .padding(24.dp), verticalAlignment = Alignment.CenterVertically - ) { - Column { - Text( - text = stringResource(R.string.home_experimental_kernelsu), - style = MaterialTheme.typography.titleSmall - ) - Spacer(Modifier.height(4.dp)) - Text( - text = stringResource(R.string.home_experimental_kernelsu_body), - style = MaterialTheme.typography.bodyMedium - ) - Spacer(Modifier.height(4.dp)) - Text( - text = stringResource(R.string.home_experimental_kernelsu_body_point_1), - style = MaterialTheme.typography.bodyMedium - ) - Spacer(Modifier.height(2.dp)) - Text( - text = stringResource(R.string.home_experimental_kernelsu_body_point_2), - style = MaterialTheme.typography.bodyMedium - ) - Spacer(Modifier.height(2.dp)) - Text( - text = stringResource(R.string.home_experimental_kernelsu_body_point_3), - style = MaterialTheme.typography.bodyMedium - ) - } - } - } -} - -@Composable -fun IssueReportCard() { - val uriHandler = LocalUriHandler.current - val githubIssueUrl = stringResource(R.string.issue_report_github_link) - val telegramUrl = stringResource(R.string.issue_report_telegram_link) - - ElevatedCard { - Row( - modifier = Modifier - .fillMaxWidth() - .padding(24.dp), - verticalAlignment = Alignment.CenterVertically - ) { - Column(modifier = Modifier.weight(1f)) { - Text( - text = stringResource(R.string.issue_report_title), - style = MaterialTheme.typography.titleMedium, - fontWeight = FontWeight.SemiBold - ) - Spacer(Modifier.height(4.dp)) - Text( - text = stringResource(R.string.issue_report_body), - style = MaterialTheme.typography.bodySmall - ) - Spacer(Modifier.height(4.dp)) - Text( - text = stringResource(R.string.issue_report_body_2), - style = MaterialTheme.typography.bodySmall - ) - } - Row(horizontalArrangement = Arrangement.spacedBy(10.dp)) { - IconButton(onClick = { uriHandler.openUri(githubIssueUrl) }) { - Icon( - painter = painterResource(R.drawable.ic_github), - contentDescription = stringResource(R.string.issue_report_github), - ) - } - IconButton(onClick = { uriHandler.openUri(telegramUrl) }) { - Icon( - painter = painterResource(R.drawable.ic_telegram), - contentDescription = stringResource(R.string.issue_report_telegram), - ) - } - } - } - } -} - -fun getManagerVersion(context: Context): Pair { - val packageInfo = context.packageManager.getPackageInfo(context.packageName, 0)!! - val versionCode = PackageInfoCompat.getLongVersionCode(packageInfo) - return Pair(packageInfo.versionName!!, versionCode) -} - -@Preview -@Composable -private fun StatusCardPreview() { - Column { - StatusCard(KernelVersion(5, 10, 101), 1, null) - StatusCard(KernelVersion(5, 10, 101), 20000, true) - StatusCard(KernelVersion(5, 10, 101), null, true) - StatusCard(KernelVersion(4, 10, 101), null, false) - } -} - -@Preview -@Composable -private fun WarningCardPreview() { - Column { - WarningCard(message = "Warning message") - WarningCard( - message = "Warning message ", - MaterialTheme.colorScheme.outlineVariant, - onClick = {}) - } -} diff --git a/manager/app/src/main/java/com/rifsxd/ksunext/ui/screen/Install.kt b/manager/app/src/main/java/com/rifsxd/ksunext/ui/screen/Install.kt deleted file mode 100644 index e196aab9..00000000 --- a/manager/app/src/main/java/com/rifsxd/ksunext/ui/screen/Install.kt +++ /dev/null @@ -1,400 +0,0 @@ -package com.rifsxd.ksunext.ui.screen - -import android.app.Activity -import android.content.Intent -import android.net.Uri -import androidx.activity.compose.rememberLauncherForActivityResult -import androidx.activity.result.contract.ActivityResultContracts -import androidx.annotation.StringRes -import androidx.compose.foundation.LocalIndication -import androidx.compose.foundation.interaction.MutableInteractionSource -import androidx.compose.foundation.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 -import androidx.compose.material3.AlertDialog -import androidx.compose.material3.Button -import androidx.compose.material3.ExperimentalMaterial3Api -import androidx.compose.material3.Icon -import androidx.compose.material3.IconButton -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.RadioButton -import androidx.compose.material3.Scaffold -import androidx.compose.material3.Text -import androidx.compose.material3.TextButton -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 -import androidx.compose.runtime.produceState -import androidx.compose.runtime.remember -import androidx.compose.runtime.saveable.rememberSaveable -import androidx.compose.runtime.setValue -import androidx.compose.ui.text.font.FontWeight -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.input.nestedscroll.nestedScroll -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.semantics.Role -import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.dp -import androidx.lifecycle.compose.dropUnlessResumed -import com.maxkeppeker.sheets.core.models.base.Header -import com.maxkeppeker.sheets.core.models.base.rememberUseCaseState -import com.maxkeppeler.sheets.list.ListDialog -import com.maxkeppeler.sheets.list.models.ListOption -import com.maxkeppeler.sheets.list.models.ListSelection -import com.ramcosta.composedestinations.annotation.Destination -import com.ramcosta.composedestinations.annotation.RootGraph -import com.ramcosta.composedestinations.generated.destinations.FlashScreenDestination -import com.ramcosta.composedestinations.navigation.DestinationsNavigator -import com.ramcosta.composedestinations.navigation.EmptyDestinationsNavigator -import com.rifsxd.ksunext.R -import com.rifsxd.ksunext.ui.component.DialogHandle -import com.rifsxd.ksunext.ui.component.rememberConfirmDialog -import com.rifsxd.ksunext.ui.component.rememberCustomDialog -import com.rifsxd.ksunext.ui.util.LkmSelection -import com.rifsxd.ksunext.ui.util.getCurrentKmi -import com.rifsxd.ksunext.ui.util.getSupportedKmis -import com.rifsxd.ksunext.ui.util.isAbDevice -import com.rifsxd.ksunext.ui.util.isInitBoot -import com.rifsxd.ksunext.ui.util.rootAvailable - -/** - * @author weishu - * @date 2024/3/12. - */ -@OptIn(ExperimentalMaterial3Api::class) -@Destination -@Composable -fun InstallScreen(navigator: DestinationsNavigator) { - var showLkmWarning by rememberSaveable { mutableStateOf(true) } - - if (showLkmWarning) { - AlertDialog( - onDismissRequest = { - showLkmWarning = false - navigator.popBackStack() - }, - title = { Text( - text = stringResource(R.string.warning), - style = MaterialTheme.typography.titleLarge, - fontWeight = FontWeight.SemiBold - ) }, - text = { Text(stringResource(R.string.lkm_warning_message)) }, - confirmButton = { - TextButton(onClick = { showLkmWarning = false }) { - Text(stringResource(R.string.proceed)) - } - }, - dismissButton = { - TextButton(onClick = { - showLkmWarning = false - navigator.popBackStack() - }) { - Text(stringResource(R.string.cancel)) - } - } - ) - } - - var installMethod by remember { - mutableStateOf(null) - } - - var lkmSelection by remember { - mutableStateOf(LkmSelection.KmiNone) - } - - val onInstall = { - installMethod?.let { method -> - val flashIt = FlashIt.FlashBoot( - boot = if (method is InstallMethod.SelectFile) method.uri else null, - lkm = lkmSelection, - ota = method is InstallMethod.DirectInstallToInactiveSlot - ) - navigator.navigate(FlashScreenDestination(flashIt)) - } - } - - val currentKmi by produceState(initialValue = "") { value = getCurrentKmi() } - - val selectKmiDialog = rememberSelectKmiDialog { kmi -> - kmi?.let { - lkmSelection = LkmSelection.KmiString(it) - onInstall() - } - } - - val onClickNext = { - if (lkmSelection == LkmSelection.KmiNone && currentKmi.isBlank()) { - // no lkm file selected and cannot get current kmi - selectKmiDialog.show() - } else { - onInstall() - } - } - - val selectLkmLauncher = - rememberLauncherForActivityResult(contract = ActivityResultContracts.StartActivityForResult()) { - if (it.resultCode == Activity.RESULT_OK) { - it.data?.data?.let { uri -> - lkmSelection = LkmSelection.LkmUri(uri) - } - } - } - - val onLkmUpload = { - selectLkmLauncher.launch(Intent(Intent.ACTION_GET_CONTENT).apply { - type = "application/octet-stream" - }) - } - - val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState()) - - Scaffold( - topBar = { - TopBar( - onBack = dropUnlessResumed { navigator.popBackStack() }, - onLkmUpload = onLkmUpload, - scrollBehavior = scrollBehavior - ) - }, - contentWindowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal) - ) { innerPadding -> - Column( - modifier = Modifier - .padding(innerPadding) - .nestedScroll(scrollBehavior.nestedScrollConnection) - .verticalScroll(rememberScrollState()) - ) { - SelectInstallMethod { method -> - installMethod = method - } - - Column( - modifier = Modifier - .fillMaxWidth() - .padding(16.dp) - ) { - (lkmSelection as? LkmSelection.LkmUri)?.let { - Text( - stringResource( - id = R.string.selected_lkm, - it.uri.lastPathSegment ?: "(file)" - ) - ) - } - Button(modifier = Modifier.fillMaxWidth(), - enabled = installMethod != null, - onClick = { - onClickNext() - }) { - Text( - stringResource(id = R.string.install_next), - fontSize = MaterialTheme.typography.bodyMedium.fontSize - ) - } - } - } - } -} - -sealed class InstallMethod { - data class SelectFile( - val uri: Uri? = null, - @StringRes override val label: Int = R.string.select_file, - override val summary: String? - ) : InstallMethod() - - data object DirectInstall : InstallMethod() { - override val label: Int - get() = R.string.direct_install - } - - data object DirectInstallToInactiveSlot : InstallMethod() { - override val label: Int - get() = R.string.install_inactive_slot - } - - abstract val label: Int - open val summary: String? = null -} - -@Composable -private fun SelectInstallMethod(onSelected: (InstallMethod) -> Unit = {}) { - val rootAvailable = rootAvailable() - val isAbDevice = isAbDevice() - val selectFileTip = stringResource( - id = R.string.select_file_tip, if (isInitBoot()) "init_boot/vendor_boot" else "boot" - ) - val radioOptions = - mutableListOf(InstallMethod.SelectFile(summary = selectFileTip)) - if (rootAvailable) { - radioOptions.add(InstallMethod.DirectInstall) - - if (isAbDevice) { - radioOptions.add(InstallMethod.DirectInstallToInactiveSlot) - } - } - - var selectedOption by remember { mutableStateOf(null) } - val selectImageLauncher = rememberLauncherForActivityResult( - contract = ActivityResultContracts.StartActivityForResult() - ) { - if (it.resultCode == Activity.RESULT_OK) { - it.data?.data?.let { uri -> - val option = InstallMethod.SelectFile(uri, summary = selectFileTip) - selectedOption = option - onSelected(option) - } - } - } - - val confirmDialog = rememberConfirmDialog(onConfirm = { - selectedOption = InstallMethod.DirectInstallToInactiveSlot - onSelected(InstallMethod.DirectInstallToInactiveSlot) - }, onDismiss = null) - val dialogTitle = stringResource(id = android.R.string.dialog_alert_title) - val dialogContent = stringResource(id = R.string.install_inactive_slot_warning) - - val onClick = { option: InstallMethod -> - - when (option) { - is InstallMethod.SelectFile -> { - selectImageLauncher.launch(Intent(Intent.ACTION_GET_CONTENT).apply { - type = "application/octet-stream" - }) - } - - is InstallMethod.DirectInstall -> { - selectedOption = option - onSelected(option) - } - - is InstallMethod.DirectInstallToInactiveSlot -> { - confirmDialog.showConfirm(dialogTitle, dialogContent) - } - } - } - - Column { - radioOptions.forEach { option -> - val interactionSource = remember { MutableInteractionSource() } - Row( - verticalAlignment = Alignment.CenterVertically, - modifier = Modifier - .fillMaxWidth() - .toggleable( - value = option.javaClass == selectedOption?.javaClass, - onValueChange = { - onClick(option) - }, - role = Role.RadioButton, - indication = LocalIndication.current, - interactionSource = interactionSource - ) - ) { - RadioButton( - selected = option.javaClass == selectedOption?.javaClass, - onClick = { - onClick(option) - }, - interactionSource = interactionSource - ) - Column( - modifier = Modifier.padding(vertical = 12.dp) - ) { - Text( - text = stringResource(id = option.label), - fontSize = MaterialTheme.typography.titleMedium.fontSize, - fontFamily = MaterialTheme.typography.titleMedium.fontFamily, - fontStyle = MaterialTheme.typography.titleMedium.fontStyle - ) - option.summary?.let { - Text( - text = it, - fontSize = MaterialTheme.typography.bodySmall.fontSize, - fontFamily = MaterialTheme.typography.bodySmall.fontFamily, - fontStyle = MaterialTheme.typography.bodySmall.fontStyle - ) - } - } - } - } - } -} - -@OptIn(ExperimentalMaterial3Api::class) -@Composable -fun rememberSelectKmiDialog(onSelected: (String?) -> Unit): DialogHandle { - return rememberCustomDialog { dismiss -> - val supportedKmi by produceState(initialValue = emptyList()) { - value = getSupportedKmis() - } - val options = supportedKmi.map { value -> - ListOption( - titleText = value - ) - } - - var selection by remember { mutableStateOf(null) } - ListDialog(state = rememberUseCaseState(visible = true, onFinishedRequest = { - onSelected(selection) - }, onCloseRequest = { - dismiss() - }), header = Header.Default( - title = stringResource(R.string.select_kmi), - ), selection = ListSelection.Single( - showRadioButtons = true, - options = options, - ) { _, option -> - selection = option.titleText - }) - } -} - -@OptIn(ExperimentalMaterial3Api::class) -@Composable -private fun TopBar( - onBack: () -> Unit = {}, - onLkmUpload: () -> Unit = {}, - scrollBehavior: TopAppBarScrollBehavior? = null -) { - TopAppBar( - title = { Text( - text = stringResource(R.string.install), - style = MaterialTheme.typography.titleLarge, - fontWeight = FontWeight.Black, - ) }, navigationIcon = { - IconButton( - onClick = onBack - ) { Icon(Icons.AutoMirrored.Filled.ArrowBack, contentDescription = null) } - }, actions = { - IconButton(onClick = onLkmUpload) { - Icon(Icons.Filled.FileUpload, contentDescription = null) - } - }, - windowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal), - scrollBehavior = scrollBehavior - ) -} - -@Composable -@Preview -fun SelectInstallPreview() { - InstallScreen(EmptyDestinationsNavigator) -} \ No newline at end of file diff --git a/manager/app/src/main/java/com/rifsxd/ksunext/ui/screen/Module.kt b/manager/app/src/main/java/com/rifsxd/ksunext/ui/screen/Module.kt deleted file mode 100644 index dd62c0d6..00000000 --- a/manager/app/src/main/java/com/rifsxd/ksunext/ui/screen/Module.kt +++ /dev/null @@ -1,1292 +0,0 @@ -package com.rifsxd.ksunext.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.animation.AnimatedVisibility -import androidx.compose.animation.expandVertically -import androidx.compose.animation.shrinkVertically -import androidx.compose.animation.fadeIn -import androidx.compose.animation.fadeOut -import androidx.compose.foundation.background -import androidx.compose.foundation.Image -import androidx.compose.foundation.clickable -import androidx.compose.foundation.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.fillMaxHeight -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.only -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.safeDrawing -import androidx.compose.foundation.layout.size -import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.foundation.lazy.items -import androidx.compose.foundation.selection.toggleable -import androidx.compose.foundation.isSystemInDarkTheme -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.automirrored.outlined.Wysiwyg -import androidx.compose.material.icons.filled.* -import androidx.compose.material.icons.outlined.* -import androidx.compose.material3.AlertDialog -import androidx.compose.material3.Button -import androidx.compose.material3.ButtonDefaults -import androidx.compose.material3.Checkbox -import androidx.compose.material3.DropdownMenu -import androidx.compose.material3.DropdownMenuItem -import androidx.compose.material3.ElevatedCard -import androidx.compose.material3.ExperimentalMaterial3Api -import androidx.compose.material3.ExtendedFloatingActionButton -import androidx.compose.material3.FilledTonalButton -import androidx.compose.material3.HorizontalDivider -import androidx.compose.material3.Icon -import androidx.compose.material3.IconButton -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.Scaffold -import androidx.compose.material3.SnackbarDuration -import androidx.compose.material3.SnackbarHost -import androidx.compose.material3.SnackbarHostState -import androidx.compose.material3.SnackbarResult -import androidx.compose.material3.Switch -import androidx.compose.material3.Text -import androidx.compose.material3.TextButton -import androidx.compose.material3.TopAppBarDefaults -import androidx.compose.material3.pulltorefresh.PullToRefreshBox -import androidx.compose.material3.rememberTopAppBarState -import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.produceState -import androidx.compose.runtime.remember -import androidx.compose.runtime.rememberCoroutineScope -import androidx.compose.runtime.saveable.rememberSaveable -import androidx.compose.runtime.setValue -import androidx.compose.foundation.lazy.LazyListState -import androidx.compose.foundation.lazy.rememberLazyListState -import androidx.compose.runtime.snapshotFlow -import androidx.compose.animation.scaleIn -import androidx.compose.animation.scaleOut -import androidx.compose.animation.core.tween -import androidx.compose.ui.draw.clip -import androidx.compose.ui.graphics.Brush -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.layout.ContentScale -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.TextOverflow -import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.Dp -import androidx.compose.ui.unit.dp -import androidx.core.net.toUri -import androidx.lifecycle.viewmodel.compose.viewModel -import com.ramcosta.composedestinations.annotation.Destination -import com.ramcosta.composedestinations.annotation.RootGraph -import com.ramcosta.composedestinations.generated.destinations.ExecuteModuleActionScreenDestination -import com.ramcosta.composedestinations.generated.destinations.FlashScreenDestination -import com.ramcosta.composedestinations.navigation.DestinationsNavigator -import com.ramcosta.composedestinations.navigation.EmptyDestinationsNavigator -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.launch -import kotlinx.coroutines.withContext -import coil.compose.AsyncImage -import coil.request.ImageRequest -import com.rifsxd.ksunext.Natives -import com.rifsxd.ksunext.R -import com.rifsxd.ksunext.ksuApp -import com.rifsxd.ksunext.ui.component.ConfirmResult -import com.rifsxd.ksunext.ui.component.rememberConfirmDialog -import com.rifsxd.ksunext.ui.component.rememberLoadingDialog -import com.rifsxd.ksunext.ui.component.SearchAppBar -import com.rifsxd.ksunext.ui.util.* -import com.rifsxd.ksunext.ui.util.DownloadListener -import com.rifsxd.ksunext.ui.util.LocalSnackbarHost -import com.rifsxd.ksunext.ui.util.download -import com.rifsxd.ksunext.ui.util.hasMagisk -import com.rifsxd.ksunext.ui.util.reboot -import com.rifsxd.ksunext.ui.util.toggleModule -import com.rifsxd.ksunext.ui.util.uninstallModule -import com.rifsxd.ksunext.ui.util.restoreModule -import com.rifsxd.ksunext.ui.util.zygiskRequired -import com.rifsxd.ksunext.ui.viewmodel.ModuleViewModel -import com.rifsxd.ksunext.ui.webui.WebUIActivity -import com.dergoogler.mmrl.ui.component.LabelItem -import com.dergoogler.mmrl.ui.component.LabelItemDefaults -import com.topjohnwu.superuser.io.SuFile - -@OptIn(ExperimentalMaterial3Api::class) -@Destination -@Composable -fun ModuleScreen(navigator: DestinationsNavigator) { - val viewModel = viewModel() - val context = LocalContext.current - val snackBarHost = LocalSnackbarHost.current - val scope = rememberCoroutineScope() - val prefs = context.getSharedPreferences("settings", Context.MODE_PRIVATE) - - LaunchedEffect(Unit) { - viewModel.sortAToZ = prefs.getBoolean("module_sort_a_to_z", true) - viewModel.sortZToA = prefs.getBoolean("module_sort_z_to_a", false) - viewModel.sortSizeLowToHigh = prefs.getBoolean("module_sort_size_low_to_high", false) - viewModel.sortSizeHighToLow = prefs.getBoolean("module_sort_size_high_to_low", false) - viewModel.sortEnabledFirst = prefs.getBoolean("module_sort_enabled_first", false) - viewModel.sortActionFirst = prefs.getBoolean("module_sort_action_first", false) - viewModel.sortWebUiFirst = prefs.getBoolean("module_sort_webui_first", false) - - if (viewModel.moduleList.isEmpty() || viewModel.isNeedRefresh) { - viewModel.fetchModuleList() - } - } - - val isSafeMode = Natives.isSafeMode - val hasMagisk = hasMagisk() - - val hideInstallButton = isSafeMode || hasMagisk - - val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState()) - - var zipUri by remember { mutableStateOf(null) } - var zipUris by remember { mutableStateOf>(emptyList()) } - - var showConfirmDialog by remember { mutableStateOf(false) } - - val webUILauncher = rememberLauncherForActivityResult( - contract = ActivityResultContracts.StartActivityForResult() - ) { viewModel.fetchModuleList() } - - val listState = rememberLazyListState() - var showFab by remember { mutableStateOf(true) } - - LaunchedEffect(listState) { - var lastIndex = listState.firstVisibleItemIndex - var lastOffset = listState.firstVisibleItemScrollOffset - - snapshotFlow { listState.firstVisibleItemIndex to listState.firstVisibleItemScrollOffset } - .collect { (currIndex, currOffset) -> - val isScrollingDown = currIndex > lastIndex || - (currIndex == lastIndex && currOffset > lastOffset + 4) - val isScrollingUp = currIndex < lastIndex || - (currIndex == lastIndex && currOffset < lastOffset - 4) - - when { - isScrollingDown && showFab -> showFab = false - isScrollingUp && !showFab -> showFab = true - } - - lastIndex = currIndex - lastOffset = currOffset - } - } - - Scaffold( - topBar = { - SearchAppBar( - title = { Text( - text = stringResource(R.string.module), - style = MaterialTheme.typography.titleLarge, - fontWeight = FontWeight.Black, - ) }, - searchText = viewModel.search, - onSearchTextChange = { viewModel.search = it }, - onClearClick = { viewModel.search = "" }, - dropdownContent = { - var showDropdown by remember { mutableStateOf(false) } - IconButton( - onClick = { showDropdown = true }, - ) { - Icon( - imageVector = Icons.Filled.MoreVert, - contentDescription = stringResource(id = R.string.settings) - ) - DropdownMenu( - expanded = showDropdown, - onDismissRequest = { - showDropdown = false - } - ) { - DropdownMenuItem( - text = { - Text(stringResource(R.string.module_sort_a_to_z)) - }, - trailingIcon = { - Checkbox(checked = viewModel.sortAToZ, onCheckedChange = null) - }, - onClick = { - viewModel.sortAToZ = !viewModel.sortAToZ - viewModel.sortZToA = false - viewModel.sortSizeLowToHigh = false - viewModel.sortSizeHighToLow = false - viewModel.sortEnabledFirst = false - viewModel.sortActionFirst = false - viewModel.sortWebUiFirst = false - prefs.edit() - .putBoolean("module_sort_a_to_z", viewModel.sortAToZ) - .putBoolean("module_sort_z_to_a", false) - .putBoolean("module_sort_size_low_to_high", false) - .putBoolean("module_sort_size_high_to_low", false) - .putBoolean("module_sort_enabled_first", false) - .putBoolean("module_sort_action_first", false) - .putBoolean("module_sort_webui_first", false) - .apply() - scope.launch { - viewModel.fetchModuleList() - } - } - ) - - DropdownMenuItem( - text = { - Text(stringResource(R.string.module_sort_z_to_a)) - }, - trailingIcon = { - Checkbox(checked = viewModel.sortZToA, onCheckedChange = null) - }, - onClick = { - viewModel.sortZToA = !viewModel.sortZToA - viewModel.sortAToZ = false - viewModel.sortSizeLowToHigh = false - viewModel.sortSizeHighToLow = false - viewModel.sortEnabledFirst = false - viewModel.sortActionFirst = false - viewModel.sortWebUiFirst = false - prefs.edit() - .putBoolean("module_sort_z_to_a", viewModel.sortZToA) - .putBoolean("module_sort_a_to_z", false) - .putBoolean("module_sort_size_low_to_high", false) - .putBoolean("module_sort_size_high_to_low", false) - .putBoolean("module_sort_enabled_first", false) - .putBoolean("module_sort_action_first", false) - .putBoolean("module_sort_webui_first", false) - .apply() - scope.launch { - viewModel.fetchModuleList() - } - } - ) - - DropdownMenuItem( - text = { - Text(stringResource(R.string.module_size_low_to_high)) - }, - trailingIcon = { - Checkbox(checked = viewModel.sortSizeLowToHigh, onCheckedChange = null) - }, - onClick = { - viewModel.sortSizeLowToHigh = !viewModel.sortSizeLowToHigh - viewModel.sortAToZ = false - viewModel.sortZToA = false - viewModel.sortSizeHighToLow = false - viewModel.sortEnabledFirst = false - viewModel.sortActionFirst = false - viewModel.sortWebUiFirst = false - prefs.edit() - .putBoolean("module_sort_size_low_to_high", viewModel.sortSizeLowToHigh) - .putBoolean("module_sort_a_to_z", false) - .putBoolean("module_sort_z_to_a", false) - .putBoolean("module_sort_size_high_to_low", false) - .putBoolean("module_sort_enabled_first", false) - .putBoolean("module_sort_action_first", false) - .putBoolean("module_sort_webui_first", false) - .apply() - scope.launch { - viewModel.fetchModuleList() - } - } - ) - - DropdownMenuItem( - text = { - Text(stringResource(R.string.module_size_high_to_low)) - }, - trailingIcon = { - Checkbox(checked = viewModel.sortSizeHighToLow, onCheckedChange = null) - }, - onClick = { - viewModel.sortSizeHighToLow = !viewModel.sortSizeHighToLow - viewModel.sortAToZ = false - viewModel.sortZToA = false - viewModel.sortSizeLowToHigh = false - viewModel.sortEnabledFirst = false - viewModel.sortActionFirst = false - viewModel.sortWebUiFirst = false - prefs.edit() - .putBoolean("module_sort_size_high_to_low", viewModel.sortSizeHighToLow) - .putBoolean("module_sort_a_to_z", false) - .putBoolean("module_sort_z_to_a", false) - .putBoolean("module_sort_size_low_to_high", false) - .putBoolean("module_sort_enabled_first", false) - .putBoolean("module_sort_action_first", false) - .putBoolean("module_sort_webui_first", false) - .apply() - scope.launch { - viewModel.fetchModuleList() - } - } - ) - DropdownMenuItem( - text = { - Text(stringResource(R.string.module_sort_enabled_first)) - }, - trailingIcon = { - Checkbox(checked = viewModel.sortEnabledFirst, onCheckedChange = null) - }, - onClick = { - viewModel.sortEnabledFirst = !viewModel.sortEnabledFirst - viewModel.sortAToZ = false - viewModel.sortZToA = false - viewModel.sortSizeLowToHigh = false - viewModel.sortSizeHighToLow = false - viewModel.sortActionFirst = false - viewModel.sortWebUiFirst = false - prefs.edit() - .putBoolean("module_sort_enabled_first", viewModel.sortEnabledFirst) - .putBoolean("module_sort_a_to_z", false) - .putBoolean("module_sort_z_to_a", false) - .putBoolean("module_sort_size_low_to_high", false) - .putBoolean("module_sort_size_high_to_low", false) - .putBoolean("module_sort_action_first", false) - .putBoolean("module_sort_webui_first", false) - .apply() - scope.launch { - viewModel.fetchModuleList() - } - } - ) - DropdownMenuItem( - text = { - Text(stringResource(R.string.module_sort_action_first)) - }, - trailingIcon = { - Checkbox(checked = viewModel.sortActionFirst, onCheckedChange = null) - }, - onClick = { - viewModel.sortActionFirst = !viewModel.sortActionFirst - viewModel.sortAToZ = false - viewModel.sortZToA = false - viewModel.sortSizeLowToHigh = false - viewModel.sortSizeHighToLow = false - viewModel.sortEnabledFirst = false - viewModel.sortWebUiFirst = false - prefs.edit() - .putBoolean("module_sort_action_first", viewModel.sortActionFirst) - .putBoolean("module_sort_a_to_z", false) - .putBoolean("module_sort_z_to_a", false) - .putBoolean("module_sort_size_low_to_high", false) - .putBoolean("module_sort_size_high_to_low", false) - .putBoolean("module_sort_enabled_first", false) - .putBoolean("module_sort_webui_first", false) - .apply() - scope.launch { - viewModel.fetchModuleList() - } - } - ) - DropdownMenuItem( - text = { - Text(stringResource(R.string.module_sort_webui_first)) - }, - trailingIcon = { - Checkbox(checked = viewModel.sortWebUiFirst, onCheckedChange = null) - }, - onClick = { - viewModel.sortWebUiFirst = !viewModel.sortWebUiFirst - viewModel.sortAToZ = false - viewModel.sortZToA = false - viewModel.sortSizeLowToHigh = false - viewModel.sortSizeHighToLow = false - viewModel.sortEnabledFirst = false - viewModel.sortActionFirst = false - prefs.edit() - .putBoolean("module_sort_webui_first", viewModel.sortWebUiFirst) - .putBoolean("module_sort_a_to_z", false) - .putBoolean("module_sort_z_to_a", false) - .putBoolean("module_sort_size_low_to_high", false) - .putBoolean("module_sort_size_high_to_low", false) - .putBoolean("module_sort_enabled_first", false) - .putBoolean("module_sort_action_first", false) - .apply() - scope.launch { - viewModel.fetchModuleList() - } - } - ) - } - } - }, - scrollBehavior = scrollBehavior - ) - }, - floatingActionButton = { - if (!hideInstallButton) { - AnimatedVisibility( - visible = showFab, - enter = scaleIn( - animationSpec = tween(200), - initialScale = 0.8f - ) + fadeIn(animationSpec = tween(400)), - exit = scaleOut( - animationSpec = tween(200), - targetScale = 0.8f - ) + fadeOut(animationSpec = tween(400)) - ) { - val moduleInstall = stringResource(id = R.string.module_install) - val selectZipLauncher = rememberLauncherForActivityResult( - contract = ActivityResultContracts.StartActivityForResult() - ) { result -> - if (result.resultCode != RESULT_OK) { - return@rememberLauncherForActivityResult - } - val data = result.data ?: return@rememberLauncherForActivityResult - val clipData = data.clipData - - val uris = mutableListOf() - if (clipData != null) { - for (i in 0 until clipData.itemCount) { - clipData.getItemAt(i)?.uri?.let { uris.add(it) } - } - } else { - data.data?.let { uris.add(it) } - } - - if (uris.isEmpty()) return@rememberLauncherForActivityResult - - viewModel.updateZipUris(uris) - - navigator.navigate(FlashScreenDestination(FlashIt.FlashModules(uris))) - viewModel.clearZipUris() - viewModel.markNeedRefresh() - } - - ExtendedFloatingActionButton( - onClick = { - // Select the zip files to install - val intent = Intent(Intent.ACTION_GET_CONTENT).apply { - type = "application/zip" - putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true) - } - selectZipLauncher.launch(intent) - }, - icon = { Icon(Icons.Filled.Add, moduleInstall) }, - text = { Text(text = moduleInstall) }, - ) - } - } - }, - contentWindowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal), - snackbarHost = { SnackbarHost(hostState = snackBarHost) } - ) { innerPadding -> - - when { - hasMagisk -> { - Box( - modifier = Modifier - .fillMaxSize() - .padding(24.dp), - contentAlignment = Alignment.Center - ) { - Text( - stringResource(R.string.module_magisk_conflict), - textAlign = TextAlign.Center, - ) - } - } - - else -> { - ModuleList( - navigator, - viewModel = viewModel, - modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection), - boxModifier = Modifier.padding(innerPadding), - onInstallModule = { - navigator.navigate(FlashScreenDestination(FlashIt.FlashModules(listOf(it)))) - }, - onClickModule = { id, name, hasWebUi -> - if (hasWebUi) { - webUILauncher.launch( - Intent(context, WebUIActivity::class.java) - .setData(Uri.parse("kernelsu://webui/$id")) - .putExtra("id", id) - .putExtra("name", name) - ) - } - }, - context = context, - snackBarHost = snackBarHost, - listState = listState - ) - } - } - } -} - -@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, - context: Context, - snackBarHost: SnackbarHostState, - listState: LazyListState -) { - 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 failedRestore = stringResource(R.string.module_restore_failed) - val successUninstall = stringResource(R.string.module_uninstall_success) - val successRestore = stringResource(R.string.module_restore_success) - val reboot = stringResource(R.string.reboot) - val rebootToApply = stringResource(R.string.reboot_to_apply) - val moduleStr = stringResource(R.string.module) - val uninstall = stringResource(R.string.uninstall) - val uninstalled = stringResource(R.string.uninstalled) - val restore = stringResource(R.string.restore) - val cancel = stringResource(android.R.string.cancel) - val moduleUninstallConfirm = stringResource(R.string.module_uninstall_confirm) - val moduleRestoreConfirm = stringResource(R.string.module_restore_confirm) - val updateText = stringResource(R.string.module_update) - val updateLable = stringResource(R.string.module_update_available) - val changelogText = stringResource(R.string.module_changelog) - val downloadingText = stringResource(R.string.module_downloading) - val startDownloadingText = stringResource(R.string.module_start_downloading) - val fetchChangeLogFailed = stringResource(R.string.module_changelog_failed) - - val prefs = context.getSharedPreferences("settings", Context.MODE_PRIVATE) - - val hasShownWarning = - rememberSaveable { mutableStateOf(prefs.getBoolean("has_shown_warning", false)) } - - val loadingDialog = rememberLoadingDialog() - val confirmDialog = rememberConfirmDialog() - - var expandedModuleId by rememberSaveable { mutableStateOf(null) } - - suspend fun onModuleUpdate( - module: ModuleViewModel.ModuleInfo, - changelogUrl: String, - downloadUrl: String, - fileName: String, - ) { - val changelogResult = loadingDialog.withLoading { - withContext(Dispatchers.IO) { - runCatching { - ksuApp.okhttpClient.newCall( - okhttp3.Request.Builder().url(changelogUrl).build() - ).execute().body!!.string() - } - } - } - - val showToast: suspend (String) -> Unit = { msg -> - withContext(Dispatchers.Main) { - Toast.makeText( - context, - msg, - Toast.LENGTH_SHORT - ).show() - } - } - - val changelog = changelogResult.getOrElse { - showToast(fetchChangeLogFailed.format(it.message)) - return - }.ifBlank { - showToast(fetchChangeLogFailed.format(module.name)) - return - } - - // changelog is not empty, show it and wait for confirm - val confirmResult = confirmDialog.awaitConfirm( - changelogText, - content = changelog, - markdown = true, - confirm = updateText, - ) - - if (confirmResult != ConfirmResult.Confirmed) { - return - } - - showToast(startDownloadingText.format(module.name)) - - val downloading = downloadingText.format(module.name) - withContext(Dispatchers.IO) { - download( - context, - downloadUrl, - fileName, - downloading, - onDownloaded = onInstallModule, - onDownloading = { - launch(Dispatchers.Main) { - Toast.makeText(context, downloading, Toast.LENGTH_SHORT).show() - } - } - ) - } - } - - suspend fun onModuleUninstall(module: ModuleViewModel.ModuleInfo) { - val confirmResult = confirmDialog.awaitConfirm( - moduleStr, - content = moduleUninstallConfirm.format(module.name), - confirm = uninstall, - dismiss = cancel - ) - if (confirmResult != ConfirmResult.Confirmed) { - return - } - - val success = loadingDialog.withLoading { - withContext(Dispatchers.IO) { - uninstallModule(module.dirId) - } - } - - if (success) { - viewModel.fetchModuleList() - } - val message = if (success) { - successUninstall.format(module.name) - } else { - failedUninstall.format(module.name) - } - val actionLabel = if (success) { - reboot - } else { - null - } - val result = snackBarHost.showSnackbar( - message = message, - actionLabel = actionLabel, - duration = SnackbarDuration.Long - ) - if (result == SnackbarResult.ActionPerformed) { - reboot() - } - } - - suspend fun onModuleRestore(module: ModuleViewModel.ModuleInfo) { - val confirmResult = confirmDialog.awaitConfirm( - moduleStr, - content = moduleRestoreConfirm.format(module.name), - confirm = restore, - dismiss = cancel - ) - if (confirmResult != ConfirmResult.Confirmed) { - return - } - - val success = loadingDialog.withLoading { - withContext(Dispatchers.IO) { - restoreModule(module.dirId) - } - } - - if (success) { - viewModel.fetchModuleList() - } - val message = if (success) { - successRestore.format(module.name) - } else { - failedRestore.format(module.name) - } - } - PullToRefreshBox( - modifier = boxModifier, - isRefreshing = viewModel.isRefreshing, - onRefresh = { - viewModel.fetchModuleList() - } - ) { - LazyColumn( - state = listState, - modifier = Modifier - .fillMaxSize() - .nestedScroll(TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState()).nestedScrollConnection), - verticalArrangement = Arrangement.spacedBy(16.dp), - contentPadding = remember { - PaddingValues( - start = 16.dp, - top = 16.dp, - end = 16.dp, - bottom = 16.dp - ) - } - ) { - when { - viewModel.moduleList.isEmpty() -> { - item { - Box( - modifier = Modifier.fillParentMaxSize(), - contentAlignment = Alignment.Center - ) { - Text( - stringResource(R.string.module_empty), - textAlign = TextAlign.Center - ) - } - } - } - else -> { - items(viewModel.moduleList) { module -> - val scope = rememberCoroutineScope() - val updatedModule by produceState(key1 = module.id, initialValue = Triple("", "", "")) { - value = withContext(Dispatchers.IO) { - viewModel.checkUpdate(module) - } - } - - ModuleItem( - navigator = navigator, - module = module, - updateUrl = updatedModule.first, - onUninstall = { - scope.launch { onModuleUninstall(module) } - }, - onRestore = { - scope.launch { onModuleRestore(module) } - }, - onCheckChanged = { - scope.launch { - val success = loadingDialog.withLoading { - withContext(Dispatchers.IO) { - toggleModule(module.dirId, !module.enabled) - } - } - if (success) { - viewModel.fetchModuleList() - - val result = snackBarHost.showSnackbar( - message = rebootToApply, - actionLabel = reboot, - duration = SnackbarDuration.Long - ) - if (result == SnackbarResult.ActionPerformed) { - reboot() - } - } else { - val message = - if (module.enabled) failedDisable else failedEnable - snackBarHost.showSnackbar(message.format(module.name)) - } - } - }, - onUpdate = { - scope.launch { - onModuleUpdate( - module, - updatedModule.third, - updatedModule.first, - "${module.name}-${updatedModule.second}.zip" - ) - viewModel.markNeedRefresh() - } - }, - onClick = { - onClickModule(it.dirId, it.name, it.hasWebUi) - }, - expanded = expandedModuleId == module.id, - onExpandToggle = { - expandedModuleId = if (expandedModuleId == module.id) null else module.id - } - ) - - // fix last item shadow incomplete in LazyColumn - Spacer(Modifier.height(1.dp)) - } - } - } - } - - DownloadListener(context, onInstallModule) - } -} - -@Composable -fun ModuleItem( - navigator: DestinationsNavigator, - module: ModuleViewModel.ModuleInfo, - updateUrl: String, - onUninstall: (ModuleViewModel.ModuleInfo) -> Unit, - onRestore: (ModuleViewModel.ModuleInfo) -> Unit, - onCheckChanged: (Boolean) -> Unit, - onUpdate: (ModuleViewModel.ModuleInfo) -> Unit, - onClick: (ModuleViewModel.ModuleInfo) -> Unit, - expanded: Boolean, - onExpandToggle: () -> Unit, -) { - val viewModel = viewModel() - - ElevatedCard( - modifier = Modifier - .fillMaxWidth() - .clip(MaterialTheme.shapes.medium) - .clickable( - onClick = onExpandToggle - ) - ) { - Box( - modifier = Modifier - .fillMaxWidth() - ) { - val context = LocalContext.current - val prefs = context.getSharedPreferences("settings", Context.MODE_PRIVATE) - - val useBanner = prefs.getBoolean("use_banner", true) - - if (useBanner && module.banner.isNotEmpty()) { - val isDark = isSystemInDarkTheme() - val colorScheme = MaterialTheme.colorScheme - val context = LocalContext.current - val amoledMode = context.getSharedPreferences("settings", Context.MODE_PRIVATE) - .getBoolean("amoled_mode", false) - val isDynamic = colorScheme.primary != colorScheme.secondary - - val fadeColor = when { - amoledMode && isDark -> Color.Black - isDynamic -> colorScheme.surface - isDark -> Color(0xFF222222) - else -> Color.White - } - - Box( - modifier = Modifier - .matchParentSize(), - contentAlignment = Alignment.Center - ) { - if (module.banner.startsWith("https", true) || module.banner.startsWith("http", true)) { - AsyncImage( - model = module.banner, - contentDescription = null, - modifier = Modifier - .fillMaxWidth() - .fillMaxHeight(), - contentScale = ContentScale.Crop, - alpha = 0.18f - ) - } else { - val bannerData = remember(module.banner) { - try { - val file = SuFile("/data/adb/modules/${module.id}/${module.banner}") - file.newInputStream().use { it.readBytes() } - } catch (e: Exception) { - null - } - } - if (bannerData != null) { - AsyncImage( - model = ImageRequest.Builder(context) - .data(bannerData) - .build(), - contentDescription = null, - modifier = Modifier - .fillMaxWidth() - .fillMaxHeight(), - contentScale = ContentScale.Crop, - alpha = 0.18f - ) - } - } - Box( - modifier = Modifier - .fillMaxWidth() - .fillMaxHeight() - .background( - Brush.verticalGradient( - colors = listOf( - fadeColor.copy(alpha = 0.0f), - fadeColor.copy(alpha = 0.8f) - ), - startY = 0f, - endY = Float.POSITIVE_INFINITY - ) - ) - ) - } - } - Column { - val interactionSource = remember { MutableInteractionSource() } - - var developerOptionsEnabled by rememberSaveable { - mutableStateOf( - prefs.getBoolean("enable_developer_options", false) - ) - } - - val filterZygiskModules = Natives.isZygiskEnabled() || !module.zygiskRequired - - LaunchedEffect(Unit) { - developerOptionsEnabled = prefs.getBoolean("enable_developer_options", false) - } - - Column( - modifier = Modifier - .padding(22.dp, 18.dp, 22.dp, 12.dp) - ) { - Row( - modifier = Modifier.fillMaxWidth(), - horizontalArrangement = Arrangement.SpaceBetween, - ) { - val moduleVersion = stringResource(id = R.string.module_version) - val moduleAuthor = stringResource(id = R.string.module_author) - val moduleId = stringResource(id = R.string.module_id) - val moduleVersionCode = stringResource(id = R.string.module_version_code) - val moduleUpdateJson = stringResource(id = R.string.module_update_json) - val moduleUpdateJsonEmpty = stringResource(id = R.string.module_update_json_empty) - - Column( - modifier = Modifier.fillMaxWidth(0.8f) - ) { - Row( - verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.spacedBy(6.dp) - ) { - LabelItem( - text = formatSize(module.size), - style = LabelItemDefaults.style.copy( - containerColor = MaterialTheme.colorScheme.secondaryContainer, - contentColor = MaterialTheme.colorScheme.onSecondaryContainer - ) - ) - if (module.remove) { - LabelItem( - text = stringResource(R.string.uninstalled), - style = LabelItemDefaults.style.copy( - containerColor = MaterialTheme.colorScheme.errorContainer, - contentColor = MaterialTheme.colorScheme.onErrorContainer - ) - ) - } - if (!Natives.isZygiskEnabled() && module.zygiskRequired && !module.remove) { - LabelItem( - text = stringResource(R.string.zygisk_required), - style = LabelItemDefaults.style.copy( - containerColor = MaterialTheme.colorScheme.errorContainer, - contentColor = MaterialTheme.colorScheme.onErrorContainer - ) - ) - } - if (updateUrl.isNotEmpty() && !module.remove && !module.update) { - LabelItem( - text = stringResource(R.string.module_update_available), - style = LabelItemDefaults.style.copy( - containerColor = MaterialTheme.colorScheme.tertiaryContainer, - contentColor = MaterialTheme.colorScheme.onTertiaryContainer - ) - ) - } - if (!module.remove) { - if (module.update) { - LabelItem( - text = stringResource(R.string.module_updated), - style = LabelItemDefaults.style.copy( - containerColor = MaterialTheme.colorScheme.tertiaryContainer, - contentColor = MaterialTheme.colorScheme.onTertiaryContainer - ) - ) - } - } - if (module.enabled && !module.remove) { - if (module.hasWebUi && filterZygiskModules) { - LabelItem( - text = stringResource(R.string.webui), - style = LabelItemDefaults.style.copy( - containerColor = MaterialTheme.colorScheme.primaryContainer, - contentColor = MaterialTheme.colorScheme.onPrimaryContainer - ) - ) - } - if (module.hasActionScript && filterZygiskModules) { - LabelItem( - text = stringResource(R.string.action), - style = LabelItemDefaults.style.copy( - containerColor = MaterialTheme.colorScheme.secondaryContainer, - contentColor = MaterialTheme.colorScheme.onSecondaryContainer - ) - ) - } - } - } - - Spacer(modifier = Modifier.height(8.dp)) - - Text( - text = module.name, - fontSize = MaterialTheme.typography.titleMedium.fontSize, - fontWeight = FontWeight.SemiBold, - lineHeight = MaterialTheme.typography.bodySmall.lineHeight, - fontFamily = MaterialTheme.typography.titleMedium.fontFamily - ) - - Text( - text = "$moduleVersion: ${module.version}", - fontSize = MaterialTheme.typography.bodySmall.fontSize, - lineHeight = MaterialTheme.typography.bodySmall.lineHeight, - fontFamily = MaterialTheme.typography.bodySmall.fontFamily - ) - - Text( - text = "$moduleAuthor: ${module.author}", - fontSize = MaterialTheme.typography.bodySmall.fontSize, - lineHeight = MaterialTheme.typography.bodySmall.lineHeight, - fontFamily = MaterialTheme.typography.bodySmall.fontFamily - ) - - if (developerOptionsEnabled) { - - Text( - text = "$moduleId: ${module.id}", - fontSize = MaterialTheme.typography.bodySmall.fontSize, - lineHeight = MaterialTheme.typography.bodySmall.lineHeight, - fontFamily = MaterialTheme.typography.bodySmall.fontFamily - ) - - Text( - text = "$moduleVersionCode: ${module.versionCode}", - fontSize = MaterialTheme.typography.bodySmall.fontSize, - lineHeight = MaterialTheme.typography.bodySmall.lineHeight, - fontFamily = MaterialTheme.typography.bodySmall.fontFamily - ) - - Text( - text = if (module.updateJson.isNotEmpty()) "$moduleUpdateJson: ${module.updateJson}" else "$moduleUpdateJson: $moduleUpdateJsonEmpty", - fontSize = MaterialTheme.typography.bodySmall.fontSize, - lineHeight = MaterialTheme.typography.bodySmall.lineHeight, - fontFamily = MaterialTheme.typography.bodySmall.fontFamily - ) - } - } - - Spacer(modifier = Modifier.weight(1f)) - - Row( - modifier = Modifier.fillMaxWidth(), - horizontalArrangement = Arrangement.End, - ) { - Switch( - enabled = !module.update, - checked = module.enabled, - onCheckedChange = onCheckChanged, - interactionSource = if (!module.hasWebUi) interactionSource else null - ) - } - } - - Spacer(modifier = Modifier.height(12.dp)) - - Text( - text = module.description, - fontSize = MaterialTheme.typography.bodySmall.fontSize, - fontFamily = MaterialTheme.typography.bodySmall.fontFamily, - lineHeight = MaterialTheme.typography.bodySmall.lineHeight, - fontWeight = MaterialTheme.typography.bodySmall.fontWeight, - overflow = TextOverflow.Ellipsis, - maxLines = 4 - ) - - Spacer(modifier = Modifier.height(2.dp)) - - if (expanded) { - Spacer(modifier = Modifier.height(10.dp)) - } - - AnimatedVisibility( - visible = expanded, - enter = fadeIn() + expandVertically(), - exit = shrinkVertically() + fadeOut() - ) { - Row( - horizontalArrangement = Arrangement.SpaceBetween, - verticalAlignment = Alignment.CenterVertically - ) { - if (module.hasActionScript) { - FilledTonalButton( - modifier = Modifier.defaultMinSize(52.dp, 32.dp), - enabled = !module.remove && module.enabled && filterZygiskModules, - onClick = { - navigator.navigate(ExecuteModuleActionScreenDestination(module.dirId)) - viewModel.markNeedRefresh() - }, - contentPadding = ButtonDefaults.TextButtonContentPadding - ) { - Icon( - modifier = Modifier.size(20.dp), - imageVector = Icons.Outlined.Terminal, - contentDescription = null - ) - if (!module.hasWebUi && updateUrl.isEmpty()) { - Text( - modifier = Modifier.padding(start = 7.dp), - text = stringResource(R.string.action), - fontFamily = MaterialTheme.typography.labelMedium.fontFamily, - fontSize = MaterialTheme.typography.labelMedium.fontSize - ) - } - } - - Spacer(modifier = Modifier.weight(0.1f, true)) - } - - if (module.hasWebUi) { - FilledTonalButton( - modifier = Modifier.defaultMinSize(52.dp, 32.dp), - enabled = !module.remove && module.enabled && filterZygiskModules, - onClick = { onClick(module) }, - interactionSource = interactionSource, - contentPadding = ButtonDefaults.TextButtonContentPadding - ) { - Icon( - modifier = Modifier.size(20.dp), - imageVector = Icons.AutoMirrored.Outlined.Wysiwyg, - contentDescription = null - ) - if (!module.hasActionScript && updateUrl.isEmpty()) { - Text( - modifier = Modifier.padding(start = 7.dp), - fontFamily = MaterialTheme.typography.labelMedium.fontFamily, - fontSize = MaterialTheme.typography.labelMedium.fontSize, - text = stringResource(R.string.open) - ) - } - } - } - - Spacer(modifier = Modifier.weight(1f, true)) - - if (updateUrl.isNotEmpty() && !module.remove && !module.update) { - Button( - modifier = Modifier.defaultMinSize(52.dp, 32.dp), - enabled = !module.remove, - onClick = { onUpdate(module) }, - shape = ButtonDefaults.textShape, - contentPadding = ButtonDefaults.TextButtonContentPadding - ) { - Icon( - modifier = Modifier.size(20.dp), - imageVector = Icons.Outlined.Download, - contentDescription = null - ) - if (!module.hasActionScript || !module.hasWebUi) { - Text( - modifier = Modifier.padding(start = 7.dp), - fontFamily = MaterialTheme.typography.labelMedium.fontFamily, - fontSize = MaterialTheme.typography.labelMedium.fontSize, - text = stringResource(R.string.module_update) - ) - } - } - - Spacer(modifier = Modifier.weight(0.1f, true)) - } - - if (module.remove) { - FilledTonalButton( - modifier = Modifier.defaultMinSize(52.dp, 32.dp), - onClick = { onRestore(module) }, - contentPadding = ButtonDefaults.TextButtonContentPadding - ) { - Icon( - modifier = Modifier.size(20.dp), - imageVector = Icons.Outlined.Restore, - contentDescription = null - ) - if (!module.hasActionScript && !module.hasWebUi && updateUrl.isEmpty()) { - Text( - modifier = Modifier.padding(start = 7.dp), - fontFamily = MaterialTheme.typography.labelMedium.fontFamily, - fontSize = MaterialTheme.typography.labelMedium.fontSize, - text = stringResource(R.string.restore) - ) - } - } - } else { - FilledTonalButton( - modifier = Modifier.defaultMinSize(52.dp, 32.dp), - enabled = true, - onClick = { onUninstall(module) }, - contentPadding = ButtonDefaults.TextButtonContentPadding - ) { - Icon( - modifier = Modifier.size(20.dp), - imageVector = Icons.Outlined.Delete, - contentDescription = null - ) - if (!module.hasActionScript && !module.hasWebUi && updateUrl.isEmpty()) { - Text( - modifier = Modifier.padding(start = 7.dp), - fontFamily = MaterialTheme.typography.labelMedium.fontFamily, - fontSize = MaterialTheme.typography.labelMedium.fontSize, - text = stringResource(R.string.uninstall) - ) - } - } - } - } - } - } - } - } - } -} - -fun formatSize(size: Long): String { - if (size == 0L) return "null" - val kb = 1024 - val mb = kb * 1024 - val gb = mb * 1024 - return when { - size >= gb -> String.format("%.2f GB", size.toDouble() / gb) - size >= mb -> String.format("%.2f MB", size.toDouble() / mb) - size >= kb -> String.format("%.2f KB", size.toDouble() / kb) - else -> "$size B" - } -} - -@Preview -@Composable -fun ModuleItemPreview() { - val module = ModuleViewModel.ModuleInfo( - id = "id", - name = "name", - version = "version", - versionCode = 1, - author = "author", - description = "I am a test module and i do nothing but show a very long description", - enabled = true, - update = true, - remove = false, - updateJson = "", - hasWebUi = false, - hasActionScript = false, - dirId = "dirId", - size = 12345678L, - banner = "", - zygiskRequired = false - ) - ModuleItem(EmptyDestinationsNavigator, module, "", {}, {}, {}, {}, {}, false, {}) -} diff --git a/manager/app/src/main/java/com/rifsxd/ksunext/ui/screen/Settings.kt b/manager/app/src/main/java/com/rifsxd/ksunext/ui/screen/Settings.kt deleted file mode 100644 index 6f4b9aa4..00000000 --- a/manager/app/src/main/java/com/rifsxd/ksunext/ui/screen/Settings.kt +++ /dev/null @@ -1,659 +0,0 @@ -package com.rifsxd.ksunext.ui.screen - -import android.content.Context -import android.content.Intent -import android.net.Uri -import android.widget.Toast -import androidx.activity.compose.rememberLauncherForActivityResult -import androidx.activity.result.contract.ActivityResultContracts -import androidx.compose.foundation.clickable -import androidx.compose.foundation.isSystemInDarkTheme -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 -import androidx.compose.material.icons.automirrored.filled.Undo -import androidx.compose.material.icons.filled.* -import androidx.compose.material3.ExperimentalMaterial3Api -import androidx.compose.material3.Icon -import androidx.compose.material3.IconButton -import androidx.compose.material3.ListItem -import androidx.compose.material3.ModalBottomSheet -import androidx.compose.material3.Scaffold -import androidx.compose.material3.SnackbarHost -import androidx.compose.material3.Text -import androidx.compose.material3.AlertDialog -import androidx.compose.material3.TextButton -import androidx.compose.material3.TopAppBar -import androidx.compose.material3.TopAppBarDefaults -import androidx.compose.material3.TopAppBarScrollBehavior -import androidx.compose.material3.rememberTopAppBarState -import androidx.compose.material3.MaterialTheme -import androidx.compose.runtime.Composable -import androidx.compose.runtime.getValue -import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.rememberCoroutineScope -import androidx.compose.runtime.saveable.rememberSaveable -import androidx.compose.runtime.setValue -import androidx.compose.ui.text.font.FontWeight -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.vector.ImageVector -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 -import androidx.compose.ui.text.style.TextAlign -import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.dp -import androidx.core.content.FileProvider -import com.maxkeppeker.sheets.core.models.base.Header -import com.maxkeppeker.sheets.core.models.base.IconSource -import com.maxkeppeker.sheets.core.models.base.rememberUseCaseState -import com.maxkeppeler.sheets.list.ListDialog -import com.maxkeppeler.sheets.list.models.ListOption -import com.maxkeppeler.sheets.list.models.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.generated.destinations.BackupRestoreScreenDestination -import com.ramcosta.composedestinations.generated.destinations.CustomizationScreenDestination -import com.ramcosta.composedestinations.generated.destinations.DeveloperScreenDestination -import com.ramcosta.composedestinations.navigation.DestinationsNavigator -import com.ramcosta.composedestinations.navigation.EmptyDestinationsNavigator -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.launch -import kotlinx.coroutines.withContext -import com.rifsxd.ksunext.BuildConfig -import com.rifsxd.ksunext.Natives -import com.rifsxd.ksunext.ksuApp -import com.rifsxd.ksunext.R -import com.rifsxd.ksunext.ui.component.AboutDialog -import com.rifsxd.ksunext.ui.component.ConfirmResult -import com.rifsxd.ksunext.ui.component.DialogHandle -import com.rifsxd.ksunext.ui.component.SwitchItem -import com.rifsxd.ksunext.ui.component.rememberConfirmDialog -import com.rifsxd.ksunext.ui.component.rememberCustomDialog -import com.rifsxd.ksunext.ui.component.rememberLoadingDialog -import com.rifsxd.ksunext.ui.util.LocalSnackbarHost -import com.rifsxd.ksunext.ui.util.getBugreportFile -import com.rifsxd.ksunext.ui.util.* -import java.time.LocalDateTime -import java.time.format.DateTimeFormatter - -/** - * @author weishu - * @date 2023/1/1. - */ -@OptIn(ExperimentalMaterial3Api::class) -@Destination -@Composable -fun SettingScreen(navigator: DestinationsNavigator) { - val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState()) - val snackBarHost = LocalSnackbarHost.current - - val isManager = Natives.becomeManager(ksuApp.packageName) - val ksuVersion = if (isManager) Natives.version else null - - Scaffold( - topBar = { - TopBar( - scrollBehavior = scrollBehavior - ) - }, - snackbarHost = { SnackbarHost(snackBarHost) }, - contentWindowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal) - ) { paddingValues -> - val aboutDialog = rememberCustomDialog { - AboutDialog(it) - } - val loadingDialog = rememberLoadingDialog() - val shrinkDialog = rememberConfirmDialog() - - 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) - if (ksuVersion != null) { - ListItem( - leadingContent = { Icon(Icons.Filled.Fence, profileTemplate) }, - headlineContent = { Text( - text = profileTemplate, - style = MaterialTheme.typography.titleMedium, - fontWeight = FontWeight.SemiBold - ) }, - supportingContent = { Text(stringResource(id = R.string.settings_profile_template_summary)) }, - modifier = Modifier.clickable { - navigator.navigate(AppProfileTemplateScreenDestination) - } - ) - } - - var umountChecked by rememberSaveable { - mutableStateOf(Natives.isDefaultUmountModules()) - } - if (ksuVersion != null) { - SwitchItem( - icon = Icons.Filled.FolderDelete, - title = stringResource(id = R.string.settings_umount_modules_default), - summary = stringResource(id = R.string.settings_umount_modules_default_summary), - checked = umountChecked - - ) { - if (Natives.setDefaultUmountModules(it)) { - umountChecked = it - } - } - } - - if (ksuVersion != null) { - if (Natives.version >= Natives.MINIMAL_SUPPORTED_SU_COMPAT) { - var isSuDisabled by rememberSaveable { - mutableStateOf(!Natives.isSuEnabled()) - } - SwitchItem( - icon = Icons.Filled.RemoveModerator, - title = stringResource(id = R.string.settings_disable_su), - summary = stringResource(id = R.string.settings_disable_su_summary), - checked = isSuDisabled - ) { checked -> - val shouldEnable = !checked - if (Natives.setSuEnabled(shouldEnable)) { - isSuDisabled = !shouldEnable - } - } - } - } - - val prefs = context.getSharedPreferences("settings", Context.MODE_PRIVATE) - - val suSFS = getSuSFS() - val isSUS_SU = getSuSFSFeatures() - if (suSFS == "Supported") { - if (isSUS_SU == "CONFIG_KSU_SUSFS_SUS_SU") { - var isEnabled by rememberSaveable { - mutableStateOf(susfsSUS_SU_Mode() == "2") - } - - LaunchedEffect(Unit) { - isEnabled = susfsSUS_SU_Mode() == "2" - } - - SwitchItem( - icon = Icons.Filled.VisibilityOff, - title = stringResource(id = R.string.settings_susfs_toggle), - summary = stringResource(id = R.string.settings_susfs_toggle_summary), - checked = isEnabled - ) { - if (it) { - susfsSUS_SU_2() - } else { - susfsSUS_SU_0() - } - prefs.edit().putBoolean("enable_sus_su", it).apply() - isEnabled = it - } - } - } - - var useOverlayFs by rememberSaveable { - mutableStateOf(readMountSystemFile()) - } - - LaunchedEffect(Unit) { - useOverlayFs = readMountSystemFile() - } - - var showRebootDialog by remember { mutableStateOf(false) } - - val isOverlayAvailable = overlayFsAvailable() - - if (ksuVersion != null && isOverlayAvailable) { - SwitchItem( - icon = Icons.Filled.Build, - title = stringResource(id = R.string.use_overlay_fs), - summary = stringResource(id = R.string.use_overlay_fs_summary), - checked = useOverlayFs - ) { - prefs.edit().putBoolean("use_overlay_fs", it).apply() - useOverlayFs = it - if (useOverlayFs) { - moduleBackup() - updateMountSystemFile(true) - } else { - moduleMigration() - updateMountSystemFile(false) - } - if (isManager) install() - showRebootDialog = true - } - } - - if (showRebootDialog) { - AlertDialog( - onDismissRequest = { showRebootDialog = false }, - title = { Text( - text = stringResource(R.string.reboot_required), - style = MaterialTheme.typography.titleLarge, - fontWeight = FontWeight.SemiBold - ) }, - text = { Text(stringResource(R.string.reboot_message)) }, - confirmButton = { - TextButton(onClick = { - showRebootDialog = false - reboot() - }) { - Text(stringResource(R.string.reboot)) - } - }, - dismissButton = { - TextButton(onClick = { showRebootDialog = false }) { - Text(stringResource(R.string.later)) - } - } - ) - } - - - var checkUpdate by rememberSaveable { - mutableStateOf( - prefs.getBoolean("check_update", false) - ) - } - SwitchItem( - icon = Icons.Filled.Update, - title = stringResource(id = R.string.settings_check_update), - summary = stringResource(id = R.string.settings_check_update_summary), - checked = checkUpdate - ) { - prefs.edit().putBoolean("check_update", it).apply() - checkUpdate = it - } - - if (isOverlayAvailable && useOverlayFs) { - val shrink = stringResource(id = R.string.shrink_sparse_image) - val shrinkMessage = stringResource(id = R.string.shrink_sparse_image_message) - ListItem( - leadingContent = { - Icon( - Icons.Filled.Compress, - shrink - ) - }, - headlineContent = { Text( - text = shrink, - style = MaterialTheme.typography.titleMedium, - fontWeight = FontWeight.SemiBold - ) }, - modifier = Modifier.clickable { - scope.launch { - val result = shrinkDialog.awaitConfirm(title = shrink, content = shrinkMessage) - if (result == ConfirmResult.Confirmed) { - loadingDialog.withLoading { - shrinkModules() - } - } - } - } - ) - } - - val customization = stringResource(id = R.string.customization) - ListItem( - leadingContent = { - Icon( - Icons.Filled.Palette, - customization - ) - }, - headlineContent = { Text( - text = customization, - style = MaterialTheme.typography.titleMedium, - fontWeight = FontWeight.SemiBold - ) }, - modifier = Modifier.clickable { - navigator.navigate(CustomizationScreenDestination) - } - ) - - if (ksuVersion != null) { - val backupRestore = stringResource(id = R.string.backup_restore) - ListItem( - leadingContent = { - Icon( - Icons.Filled.Backup, - backupRestore - ) - }, - headlineContent = { Text( - text = backupRestore, - style = MaterialTheme.typography.titleMedium, - fontWeight = FontWeight.SemiBold - ) }, - modifier = Modifier.clickable { - navigator.navigate(BackupRestoreScreenDestination) - } - ) - } - - val developer = stringResource(id = R.string.developer) - if (ksuVersion != null) { - ListItem( - leadingContent = { - Icon( - Icons.Filled.DeveloperBoard, - developer - ) - }, - headlineContent = { Text( - text = developer, - style = MaterialTheme.typography.titleMedium, - fontWeight = FontWeight.SemiBold - ) }, - modifier = Modifier.clickable { - navigator.navigate(DeveloperScreenDestination) - } - ) - } - - val lkmMode = Natives.version >= Natives.MINIMAL_SUPPORTED_KERNEL_LKM && Natives.isLkmMode - if (lkmMode) { - UninstallItem(navigator) { - loadingDialog.withLoading(it) - } - } - - var showBottomsheet by remember { mutableStateOf(false) } - - ListItem( - leadingContent = { - Icon( - Icons.Filled.BugReport, - stringResource(id = R.string.export_log) - ) - }, - headlineContent = { Text( - text = stringResource(id = R.string.export_log), - style = MaterialTheme.typography.titleMedium, - fontWeight = FontWeight.SemiBold - ) }, - modifier = Modifier.clickable { - showBottomsheet = true - } - ) - if (showBottomsheet) { - ModalBottomSheet( - onDismissRequest = { showBottomsheet = false }, - content = { - Row( - modifier = Modifier - .padding(10.dp) - .align(Alignment.CenterHorizontally) - - ) { - Box { - Column( - modifier = Modifier - .padding(16.dp) - .clickable { - val formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd_HH_mm") - val current = LocalDateTime.now().format(formatter) - exportBugreportLauncher.launch("KernelSU_Next_bugreport_${current}.tar.gz") - showBottomsheet = false - } - ) { - Icon( - Icons.Filled.Save, - contentDescription = null, - modifier = Modifier.align(Alignment.CenterHorizontally) - ) - Text( - text = stringResource(id = R.string.save_log), - modifier = Modifier.padding(top = 16.dp), - textAlign = TextAlign.Center.also { - LineHeightStyle( - alignment = LineHeightStyle.Alignment.Center, - trim = LineHeightStyle.Trim.None - ) - } - - ) - } - } - Box { - Column( - 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 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( - shareIntent, - context.getString(R.string.send_log) - ) - ) - } - } - ) { - Icon( - Icons.Filled.Share, - contentDescription = null, - modifier = Modifier.align(Alignment.CenterHorizontally) - ) - Text( - text = stringResource(id = R.string.send_log), - modifier = Modifier.padding(top = 16.dp), - textAlign = TextAlign.Center.also { - LineHeightStyle( - alignment = LineHeightStyle.Alignment.Center, - trim = LineHeightStyle.Trim.None - ) - } - ) - } - } - } - } - ) - } - - val about = stringResource(id = R.string.about) - ListItem( - leadingContent = { - Icon( - Icons.Filled.ContactPage, - about - ) - }, - headlineContent = { Text( - text = about, - style = MaterialTheme.typography.titleMedium, - fontWeight = FontWeight.SemiBold - ) }, - modifier = Modifier.clickable { - aboutDialog.show() - } - ) - } - } -} - -@Composable -fun UninstallItem( - navigator: DestinationsNavigator, - withLoading: suspend (suspend () -> Unit) -> Unit, -) { - val context = LocalContext.current - val scope = rememberCoroutineScope() - val uninstallConfirmDialog = rememberConfirmDialog() - val showTodo = { - Toast.makeText(context, "TODO", Toast.LENGTH_SHORT).show() - } - val uninstallDialog = rememberUninstallDialog { uninstallType -> - scope.launch { - val result = uninstallConfirmDialog.awaitConfirm( - title = context.getString(uninstallType.title), - content = context.getString(uninstallType.message) - ) - if (result == ConfirmResult.Confirmed) { - withLoading { - when (uninstallType) { - UninstallType.TEMPORARY -> showTodo() - UninstallType.PERMANENT -> navigator.navigate( - FlashScreenDestination(FlashIt.FlashUninstall) - ) - UninstallType.RESTORE_STOCK_IMAGE -> navigator.navigate( - FlashScreenDestination(FlashIt.FlashRestore) - ) - UninstallType.NONE -> Unit - } - } - } - } - } - val uninstall = stringResource(id = R.string.settings_uninstall) - ListItem( - leadingContent = { - Icon( - Icons.Filled.Delete, - uninstall - ) - }, - headlineContent = { Text( - text = uninstall, - style = MaterialTheme.typography.titleMedium, - fontWeight = FontWeight.SemiBold - ) }, - modifier = Modifier.clickable { - uninstallDialog.show() - } - ) -} - -enum class UninstallType(val title: Int, val message: Int, val icon: ImageVector) { - TEMPORARY( - R.string.settings_uninstall_temporary, - R.string.settings_uninstall_temporary_message, - Icons.Filled.Delete - ), - PERMANENT( - R.string.settings_uninstall_permanent, - R.string.settings_uninstall_permanent_message, - Icons.Filled.DeleteForever - ), - RESTORE_STOCK_IMAGE( - R.string.settings_restore_stock_image, - R.string.settings_restore_stock_image_message, - Icons.AutoMirrored.Filled.Undo - ), - NONE(0, 0, Icons.Filled.Delete) -} - -@OptIn(ExperimentalMaterial3Api::class) -@Composable -fun rememberUninstallDialog(onSelected: (UninstallType) -> Unit): DialogHandle { - return rememberCustomDialog { dismiss -> - val options = listOf( - // UninstallType.TEMPORARY, - UninstallType.PERMANENT, - UninstallType.RESTORE_STOCK_IMAGE - ) - val listOptions = options.map { - ListOption( - titleText = stringResource(it.title), - subtitleText = if (it.message != 0) stringResource(it.message) else null, - icon = IconSource(it.icon) - ) - } - - var selection = UninstallType.NONE - ListDialog(state = rememberUseCaseState(visible = true, onFinishedRequest = { - if (selection != UninstallType.NONE) { - onSelected(selection) - } - }, onCloseRequest = { - dismiss() - }), header = Header.Default( - title = stringResource(R.string.settings_uninstall), - ), selection = ListSelection.Single( - showRadioButtons = false, - options = listOptions, - ) { index, _ -> - selection = options[index] - }) - } -} - -@OptIn(ExperimentalMaterial3Api::class) -@Composable -private fun TopBar( - scrollBehavior: TopAppBarScrollBehavior? = null, -) { - TopAppBar( - title = { Text( - text = stringResource(R.string.settings), - style = MaterialTheme.typography.titleLarge, - fontWeight = FontWeight.Black, - ) }, - windowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal), - scrollBehavior = scrollBehavior - ) -} - -@Preview -@Composable -private fun SettingsPreview() { - SettingScreen(EmptyDestinationsNavigator) -} \ No newline at end of file diff --git a/manager/app/src/main/java/com/rifsxd/ksunext/ui/screen/SuperUser.kt b/manager/app/src/main/java/com/rifsxd/ksunext/ui/screen/SuperUser.kt deleted file mode 100644 index 3baa1556..00000000 --- a/manager/app/src/main/java/com/rifsxd/ksunext/ui/screen/SuperUser.kt +++ /dev/null @@ -1,228 +0,0 @@ -package com.rifsxd.ksunext.ui.screen - -import androidx.compose.foundation.background -import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.* -import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.foundation.lazy.rememberLazyListState -import androidx.compose.foundation.lazy.items -import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material.ExperimentalMaterialApi -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.MoreVert -import androidx.compose.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 -import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp -import androidx.compose.ui.text.font.FontWeight -import androidx.lifecycle.viewmodel.compose.viewModel -import coil.compose.AsyncImage -import coil.request.ImageRequest -import com.dergoogler.mmrl.ui.component.LabelItem -import com.dergoogler.mmrl.ui.component.LabelItemDefaults -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 com.rifsxd.ksunext.Natives -import com.rifsxd.ksunext.R -import com.rifsxd.ksunext.ui.component.SearchAppBar -import com.rifsxd.ksunext.ui.viewmodel.SuperUserViewModel - -@OptIn(ExperimentalMaterialApi::class, ExperimentalMaterial3Api::class) -@Destination -@Composable -fun SuperUserScreen(navigator: DestinationsNavigator) { - val viewModel = viewModel() - val scope = rememberCoroutineScope() - val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState()) - val listState = rememberLazyListState() - - LaunchedEffect(navigator) { - viewModel.search = "" - if (viewModel.appList.isEmpty()) { - viewModel.fetchAppList() - } - } - - Scaffold( - topBar = { - SearchAppBar( - title = { Text( - text = stringResource(R.string.superuser), - style = MaterialTheme.typography.titleLarge, - fontWeight = FontWeight.Black, - ) }, - searchText = viewModel.search, - onSearchTextChange = { viewModel.search = it }, - onClearClick = { viewModel.search = "" }, - dropdownContent = { - var showDropdown by remember { mutableStateOf(false) } - - IconButton( - onClick = { showDropdown = true }, - ) { - Icon( - imageVector = Icons.Filled.MoreVert, - contentDescription = stringResource(id = R.string.settings) - ) - - DropdownMenu(expanded = showDropdown, onDismissRequest = { - showDropdown = false - }) { - DropdownMenuItem(text = { - Text(stringResource(R.string.refresh)) - }, onClick = { - scope.launch { - viewModel.fetchAppList() - } - showDropdown = false - }) - DropdownMenuItem(text = { - Text( - if (viewModel.showSystemApps) { - stringResource(R.string.hide_system_apps) - } else { - stringResource(R.string.show_system_apps) - } - ) - }, onClick = { - viewModel.updateShowSystemApps(!viewModel.showSystemApps) - showDropdown = false - }) - } - } - }, - scrollBehavior = scrollBehavior - ) - }, - contentWindowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal) - ) { innerPadding -> - PullToRefreshBox( - modifier = Modifier.padding(innerPadding), - onRefresh = { - scope.launch { viewModel.fetchAppList() } - }, - isRefreshing = viewModel.isRefreshing - ) { - LazyColumn( - state = listState, - modifier = Modifier - .fillMaxSize() - .nestedScroll(scrollBehavior.nestedScrollConnection) - ) { - items(viewModel.appList, key = { it.packageName + it.uid }) { app -> - AppItem(app) { - navigator.navigate(AppProfileScreenDestination(app)) - } - } - } - } - } -} - -@OptIn(ExperimentalLayoutApi::class) -@Composable -private fun AppItem( - app: SuperUserViewModel.AppInfo, - onClickListener: () -> Unit, -) { - ListItem( - modifier = Modifier.clickable(onClick = onClickListener), - headlineContent = { Text( - text = app.label, - style = MaterialTheme.typography.titleMedium, - fontWeight = FontWeight.SemiBold, - ) }, - supportingContent = { - Column { - Text( - text = app.packageName, - style = MaterialTheme.typography.bodySmall - ) - - Spacer(modifier = Modifier.height(4.dp)) - - FlowRow( - horizontalArrangement = Arrangement.spacedBy(4.dp) - ) { - if (app.allowSu) { - LabelItem( - text = "ROOT", - ) - } else { - if (Natives.uidShouldUmount(app.uid)) { - LabelItem( - text = "UMOUNT", - style = LabelItemDefaults.style.copy( - containerColor = MaterialTheme.colorScheme.secondaryContainer, - contentColor = MaterialTheme.colorScheme.onSecondaryContainer - ) - ) - } - } - if (app.hasCustomProfile) { - LabelItem( - text = "CUSTOM", - style = LabelItemDefaults.style.copy( - containerColor = MaterialTheme.colorScheme.tertiaryContainer, - contentColor = MaterialTheme.colorScheme.onTertiaryContainer, - ) - ) - } else if (!app.allowSu && !Natives.uidShouldUmount(app.uid)) { - LabelItem( - text = "DEFAULT", - style = LabelItemDefaults.style.copy( - containerColor = MaterialTheme.colorScheme.primaryContainer, - contentColor = MaterialTheme.colorScheme.onPrimaryContainer, - ) - ) - } - } - } - }, - leadingContent = { - AsyncImage( - model = ImageRequest.Builder(LocalContext.current) - .data(app.packageInfo) - .crossfade(true) - .build(), - contentDescription = app.label, - modifier = Modifier - .padding(4.dp) - .width(48.dp) - .height(48.dp) - ) - }, - ) -} - -@Composable -fun LabelText(label: String) { - Box( - modifier = Modifier - .padding(top = 4.dp, end = 4.dp) - .background( - Color.Black, - shape = RoundedCornerShape(4.dp) - ) - ) { - Text( - text = label, - modifier = Modifier.padding(vertical = 2.dp, horizontal = 5.dp), - style = TextStyle( - fontSize = 8.sp, - color = Color.White, - ) - ) - } -} \ No newline at end of file diff --git a/manager/app/src/main/java/com/rifsxd/ksunext/ui/screen/Template.kt b/manager/app/src/main/java/com/rifsxd/ksunext/ui/screen/Template.kt deleted file mode 100644 index f36baf77..00000000 --- a/manager/app/src/main/java/com/rifsxd/ksunext/ui/screen/Template.kt +++ /dev/null @@ -1,333 +0,0 @@ -package com.rifsxd.ksunext.ui.screen - -import android.widget.Toast -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.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.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.height -import androidx.compose.material.ExperimentalMaterialApi -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.automirrored.filled.ArrowBack -import androidx.compose.material.icons.filled.Add -import androidx.compose.material.icons.filled.ImportExport -import androidx.compose.material.icons.filled.Sync -import androidx.compose.material3.DropdownMenu -import androidx.compose.material3.DropdownMenuItem -import androidx.compose.material3.ExperimentalMaterial3Api -import androidx.compose.material3.ExtendedFloatingActionButton -import androidx.compose.material3.Icon -import androidx.compose.material3.IconButton -import androidx.compose.material3.ListItem -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 -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.rememberCoroutineScope -import androidx.compose.runtime.setValue -import androidx.compose.ui.text.font.FontWeight -import androidx.compose.ui.Modifier -import androidx.compose.ui.input.nestedscroll.nestedScroll -import androidx.compose.ui.platform.LocalClipboardManager -import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.text.AnnotatedString -import androidx.compose.ui.unit.dp -import androidx.lifecycle.compose.dropUnlessResumed -import androidx.lifecycle.viewmodel.compose.viewModel -import com.dergoogler.mmrl.ui.component.LabelItem -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 com.rifsxd.ksunext.R -import com.rifsxd.ksunext.ui.viewmodel.TemplateViewModel - -import androidx.compose.foundation.lazy.rememberLazyListState -import androidx.compose.runtime.snapshotFlow -import androidx.compose.animation.AnimatedVisibility -import androidx.compose.animation.scaleIn -import androidx.compose.animation.scaleOut -import androidx.compose.animation.fadeIn -import androidx.compose.animation.fadeOut -import androidx.compose.animation.core.tween - -/** - * @author weishu - * @date 2023/10/20. - */ - -@OptIn(ExperimentalMaterialApi::class, ExperimentalMaterial3Api::class) -@Destination -@Composable -fun AppProfileTemplateScreen( - navigator: DestinationsNavigator, - resultRecipient: ResultRecipient -) { - val viewModel = viewModel() - val scope = rememberCoroutineScope() - val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState()) - - LaunchedEffect(Unit) { - if (viewModel.templateList.isEmpty()) { - viewModel.fetchTemplates() - } - } - - // handle result from TemplateEditorScreen, refresh if needed - resultRecipient.onNavResult { result -> - if (result.getOr { false }) { - scope.launch { viewModel.fetchTemplates() } - } - } - - val listState = rememberLazyListState() - var showFab by remember { mutableStateOf(true) } - - LaunchedEffect(listState) { - var lastIndex = listState.firstVisibleItemIndex - var lastOffset = listState.firstVisibleItemScrollOffset - - snapshotFlow { listState.firstVisibleItemIndex to listState.firstVisibleItemScrollOffset } - .collect { (currIndex, currOffset) -> - val isScrollingDown = currIndex > lastIndex || - (currIndex == lastIndex && currOffset > lastOffset + 4) - val isScrollingUp = currIndex < lastIndex || - (currIndex == lastIndex && currOffset < lastOffset - 4) - - when { - isScrollingDown && showFab -> showFab = false - isScrollingUp && !showFab -> showFab = true - } - - lastIndex = currIndex - lastOffset = currOffset - } - } - - Scaffold( - topBar = { - val clipboardManager = LocalClipboardManager.current - val context = LocalContext.current - val showToast = fun(msg: String) { - scope.launch(Dispatchers.Main) { - Toast.makeText(context, msg, Toast.LENGTH_SHORT).show() - } - } - TopBar( - onBack = dropUnlessResumed { navigator.popBackStack() }, - onSync = { - scope.launch { viewModel.fetchTemplates(true) } - }, - onImport = { - clipboardManager.getText()?.text?.let { - if (it.isEmpty()) { - showToast(context.getString(R.string.app_profile_template_import_empty)) - return@let - } - scope.launch { - viewModel.importTemplates( - it, { - showToast(context.getString(R.string.app_profile_template_import_success)) - viewModel.fetchTemplates(false) - }, - showToast - ) - } - } - }, - onExport = { - scope.launch { - viewModel.exportTemplates( - { - showToast(context.getString(R.string.app_profile_template_export_empty)) - } - ) { - clipboardManager.setText(AnnotatedString(it)) - } - } - }, - scrollBehavior = scrollBehavior - ) - }, - floatingActionButton = { - AnimatedVisibility( - visible = showFab, - enter = scaleIn( - animationSpec = tween(200), - initialScale = 0.8f - ) + fadeIn(animationSpec = tween(400)), - exit = scaleOut( - animationSpec = tween(200), - targetScale = 0.8f - ) + fadeOut(animationSpec = tween(400)) - ) { - ExtendedFloatingActionButton( - onClick = { - navigator.navigate( - TemplateEditorScreenDestination( - TemplateViewModel.TemplateInfo(), - false - ) - ) - }, - icon = { Icon(Icons.Filled.Add, null) }, - text = { Text(stringResource(id = R.string.app_profile_template_create)) }, - ) - } - }, - contentWindowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal) - ) { innerPadding -> - PullToRefreshBox( - modifier = Modifier.padding(innerPadding), - isRefreshing = viewModel.isRefreshing, - onRefresh = { - scope.launch { viewModel.fetchTemplates() } - } - ) { - LazyColumn( - state = listState, - modifier = Modifier - .fillMaxSize() - .nestedScroll(scrollBehavior.nestedScrollConnection), - contentPadding = remember { - PaddingValues(bottom = 16.dp /* Scaffold Fab Spacing + Fab container height */) - } - ) { - items(viewModel.templateList, key = { it.id }) { app -> - TemplateItem(navigator, app) - } - } - } - } -} - -@OptIn(ExperimentalLayoutApi::class) -@Composable -private fun TemplateItem( - navigator: DestinationsNavigator, - template: TemplateViewModel.TemplateInfo -) { - ListItem( - modifier = Modifier - .clickable { - navigator.navigate(TemplateEditorScreenDestination(template, !template.local)) - }, - headlineContent = { Text( - text = template.name, - style = MaterialTheme.typography.titleMedium, - fontWeight = FontWeight.SemiBold, - ) }, - supportingContent = { - Column { - Text( - text = "${template.id}${if (template.author.isEmpty()) "" else "@${template.author}"}", - style = MaterialTheme.typography.bodySmall, - fontSize = MaterialTheme.typography.bodySmall.fontSize, - ) - Text(template.description) - - Spacer(modifier = Modifier.height(4.dp)) - - FlowRow( - horizontalArrangement = Arrangement.spacedBy(4.dp) - ) { - LabelItem(text = "UID: ${template.uid}") - LabelItem(text = "GID: ${template.gid}") - LabelItem(text = template.context) - if (template.local) { - LabelItem(text = "local") - } else { - LabelItem(text = "remote") - } - } - } - }, - ) -} - -@OptIn(ExperimentalMaterial3Api::class) -@Composable -private fun TopBar( - onBack: () -> Unit, - onSync: () -> Unit = {}, - onImport: () -> Unit = {}, - onExport: () -> Unit = {}, - scrollBehavior: TopAppBarScrollBehavior? = null -) { - TopAppBar( - title = { - Text( - text = stringResource(R.string.settings_profile_template), - style = MaterialTheme.typography.titleLarge, - fontWeight = FontWeight.Black, - ) - }, - navigationIcon = { - IconButton( - onClick = onBack - ) { Icon(Icons.AutoMirrored.Filled.ArrowBack, contentDescription = null) } - }, - actions = { - IconButton(onClick = onSync) { - Icon( - Icons.Filled.Sync, - contentDescription = stringResource(id = R.string.app_profile_template_sync) - ) - } - - var showDropdown by remember { mutableStateOf(false) } - IconButton(onClick = { - showDropdown = true - }) { - Icon( - imageVector = Icons.Filled.ImportExport, - contentDescription = stringResource(id = R.string.app_profile_import_export) - ) - - DropdownMenu(expanded = showDropdown, onDismissRequest = { - showDropdown = false - }) { - DropdownMenuItem(text = { - Text(stringResource(id = R.string.app_profile_import_from_clipboard)) - }, onClick = { - onImport() - showDropdown = false - }) - DropdownMenuItem(text = { - Text(stringResource(id = R.string.app_profile_export_to_clipboard)) - }, onClick = { - onExport() - showDropdown = false - }) - } - } - }, - windowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal), - scrollBehavior = scrollBehavior - ) -} \ No newline at end of file diff --git a/manager/app/src/main/java/com/rifsxd/ksunext/ui/screen/TemplateEditor.kt b/manager/app/src/main/java/com/rifsxd/ksunext/ui/screen/TemplateEditor.kt deleted file mode 100644 index d7f96fab..00000000 --- a/manager/app/src/main/java/com/rifsxd/ksunext/ui/screen/TemplateEditor.kt +++ /dev/null @@ -1,345 +0,0 @@ -package com.rifsxd.ksunext.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 -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.DeleteForever -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.ListItem -import androidx.compose.material3.MaterialTheme -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 -import androidx.compose.runtime.remember -import androidx.compose.runtime.saveable.rememberSaveable -import androidx.compose.runtime.setValue -import androidx.compose.ui.text.font.FontWeight -import androidx.compose.ui.ExperimentalComposeUiApi -import androidx.compose.ui.Modifier -import androidx.compose.ui.input.nestedscroll.nestedScroll -import androidx.compose.ui.input.pointer.pointerInteropFilter -import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.platform.LocalSoftwareKeyboardController -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.text.input.ImeAction -import androidx.compose.ui.text.input.KeyboardType -import androidx.lifecycle.compose.dropUnlessResumed -import com.ramcosta.composedestinations.annotation.Destination -import com.ramcosta.composedestinations.annotation.RootGraph -import com.ramcosta.composedestinations.result.ResultBackNavigator -import com.rifsxd.ksunext.Natives -import com.rifsxd.ksunext.R -import com.rifsxd.ksunext.ui.component.profile.RootProfileConfig -import com.rifsxd.ksunext.ui.util.deleteAppProfileTemplate -import com.rifsxd.ksunext.ui.util.getAppProfileTemplate -import com.rifsxd.ksunext.ui.util.setAppProfileTemplate -import com.rifsxd.ksunext.ui.viewmodel.TemplateViewModel -import com.rifsxd.ksunext.ui.viewmodel.toJSON - -/** - * @author weishu - * @date 2023/10/20. - */ -@OptIn(ExperimentalComposeUiApi::class, ExperimentalMaterial3Api::class) -@Destination -@Composable -fun TemplateEditorScreen( - navigator: ResultBackNavigator, - initialTemplate: TemplateViewModel.TemplateInfo, - readOnly: Boolean = true, -) { - - val isCreation = initialTemplate.id.isBlank() - val autoSave = !isCreation - - var template by rememberSaveable { - mutableStateOf(initialTemplate) - } - - val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState()) - - BackHandler { - navigator.navigateBack(result = !readOnly) - } - - Scaffold( - topBar = { - val author = - if (initialTemplate.author.isNotEmpty()) "@${initialTemplate.author}" else "" - val readOnlyHint = if (readOnly) { - " - ${stringResource(id = R.string.app_profile_template_readonly)}" - } else { - "" - } - val titleSummary = "${initialTemplate.id}$author$readOnlyHint" - val saveTemplateFailed = stringResource(id = R.string.app_profile_template_save_failed) - val context = LocalContext.current - - TopBar( - title = if (isCreation) { - stringResource(R.string.app_profile_template_create) - } else if (readOnly) { - stringResource(R.string.app_profile_template_view) - } else { - stringResource(R.string.app_profile_template_edit) - }, - readOnly = readOnly, - summary = titleSummary, - onBack = dropUnlessResumed { navigator.navigateBack(result = !readOnly) }, - onDelete = { - if (deleteAppProfileTemplate(template.id)) { - navigator.navigateBack(result = true) - } - }, - onSave = { - if (saveTemplate(template, isCreation)) { - navigator.navigateBack(result = true) - } 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 - readOnly - } - ) { - if (isCreation) { - var errorHint by remember { - mutableStateOf("") - } - val idConflictError = stringResource(id = R.string.app_profile_template_id_exist) - val idInvalidError = stringResource(id = R.string.app_profile_template_id_invalid) - TextEdit( - label = stringResource(id = R.string.app_profile_template_id), - text = template.id, - errorHint = errorHint, - isError = errorHint.isNotEmpty() - ) { value -> - errorHint = if (isTemplateExist(value)) { - idConflictError - } else if (!isValidTemplateId(value)) { - idInvalidError - } else { - "" - } - template = template.copy(id = value) - } - } - - TextEdit( - label = stringResource(id = R.string.app_profile_template_name), - text = template.name - ) { value -> - template.copy(name = value).run { - if (autoSave) { - if (!saveTemplate(this)) { - // failed - return@run - } - } - template = this - } - } - TextEdit( - label = stringResource(id = R.string.app_profile_template_description), - text = template.description - ) { value -> - template.copy(description = value).run { - if (autoSave) { - if (!saveTemplate(this)) { - // failed - return@run - } - } - template = this - } - } - - RootProfileConfig(fixedName = true, - profile = toNativeProfile(template), - onProfileChange = { - template.copy( - uid = it.uid, - gid = it.gid, - groups = it.groups, - capabilities = it.capabilities, - context = it.context, - namespace = it.namespace, - rules = it.rules.split("\n") - ).run { - if (autoSave) { - if (!saveTemplate(this)) { - // failed - return@run - } - } - template = this - } - }) - } - } -} - -fun toNativeProfile(templateInfo: TemplateViewModel.TemplateInfo): Natives.Profile { - return Natives.Profile().copy(rootTemplate = templateInfo.id, - uid = templateInfo.uid, - gid = templateInfo.gid, - groups = templateInfo.groups, - capabilities = templateInfo.capabilities, - context = templateInfo.context, - namespace = templateInfo.namespace, - rules = templateInfo.rules.joinToString("\n").ifBlank { "" }) -} - -fun isTemplateValid(template: TemplateViewModel.TemplateInfo): Boolean { - if (template.id.isBlank()) { - return false - } - - if (!isValidTemplateId(template.id)) { - return false - } - - return true -} - -fun saveTemplate(template: TemplateViewModel.TemplateInfo, isCreation: Boolean = false): Boolean { - if (!isTemplateValid(template)) { - return false - } - - if (isCreation && isTemplateExist(template.id)) { - return false - } - - val json = template.toJSON() - json.put("local", true) - return setAppProfileTemplate(template.id, json.toString()) -} - -@OptIn(ExperimentalMaterial3Api::class) -@Composable -private fun TopBar( - title: String, - readOnly: Boolean, - summary: String = "", - onBack: () -> Unit, - onDelete: () -> Unit = {}, - onSave: () -> Unit = {}, - scrollBehavior: TopAppBarScrollBehavior? = null -) { - TopAppBar( - title = { - Column { - Text( - text = title, - style = MaterialTheme.typography.titleLarge, - fontWeight = FontWeight.Black, - ) - 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) - ) - } - 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 -private fun TextEdit( - label: String, - text: String, - errorHint: String = "", - isError: Boolean = false, - onValueChange: (String) -> Unit = {} -) { - ListItem(headlineContent = { - val keyboardController = LocalSoftwareKeyboardController.current - OutlinedTextField( - value = text, - modifier = Modifier.fillMaxWidth(), - label = { Text(label) }, - suffix = { - if (errorHint.isNotBlank()) { - Text( - text = if (isError) errorHint else "", - style = MaterialTheme.typography.bodySmall, - color = MaterialTheme.colorScheme.error - ) - } - }, - isError = isError, - keyboardOptions = KeyboardOptions( - keyboardType = KeyboardType.Ascii, imeAction = ImeAction.Next - ), - keyboardActions = KeyboardActions(onDone = { - keyboardController?.hide() - }), - onValueChange = onValueChange - ) - }) -} - -private fun isValidTemplateId(id: String): Boolean { - return Regex("""^([A-Za-z][A-Za-z\d_]*\.)*[A-Za-z][A-Za-z\d_]*$""").matches(id) -} - -private fun isTemplateExist(id: String): Boolean { - return getAppProfileTemplate(id).isNotBlank() -} \ No newline at end of file diff --git a/manager/app/src/main/java/com/rifsxd/ksunext/ui/theme/Color.kt b/manager/app/src/main/java/com/rifsxd/ksunext/ui/theme/Color.kt deleted file mode 100644 index c26aeb2b..00000000 --- a/manager/app/src/main/java/com/rifsxd/ksunext/ui/theme/Color.kt +++ /dev/null @@ -1,20 +0,0 @@ -package com.rifsxd.ksunext.ui.theme - -import androidx.compose.ui.graphics.Color - -val PRIMARY = Color(0xFF8AADF4) // Catppuccin Blue -val PRIMARY_LIGHT = Color(0xFFB7BDF8) // Catppuccin Lavender -val SECONDARY_LIGHT = Color(0xFFA6DA95) // Catppuccin Green - -val PRIMARY_DARK = Color(0xFF7DC4E4) // Catppuccin Sky -val SECONDARY_DARK = Color(0xFFF5BDE6) // Catppuccin Pink - -val AMOLED_BLACK = Color(0xFF000000) // Pure black for AMOLED - -val DARK_PURPLE = Color(0xFF6E6CB6) // Catppuccin Mauve (dark purple) -val DARK_GREY = Color(0xFF363A4F) // Catppuccin Surface (dark grey) - -val GREEN = Color(0xFF4CAF50) // Green -val RED = Color(0xFFF44336) // Red -val YELLOW = Color(0xFFFFEB3B) // Yellow -val ORANGE = Color(0xFFFF9800) // Orange \ No newline at end of file diff --git a/manager/app/src/main/java/com/rifsxd/ksunext/ui/theme/Theme.kt b/manager/app/src/main/java/com/rifsxd/ksunext/ui/theme/Theme.kt deleted file mode 100644 index 936aeace..00000000 --- a/manager/app/src/main/java/com/rifsxd/ksunext/ui/theme/Theme.kt +++ /dev/null @@ -1,125 +0,0 @@ -package com.rifsxd.ksunext.ui.theme - -import android.os.Build -import androidx.activity.SystemBarStyle -import androidx.activity.ComponentActivity -import androidx.activity.enableEdgeToEdge -import androidx.compose.foundation.isSystemInDarkTheme -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.darkColorScheme -import androidx.compose.material3.dynamicDarkColorScheme -import androidx.compose.material3.dynamicLightColorScheme -import androidx.compose.material3.lightColorScheme -import androidx.compose.runtime.Composable -import androidx.compose.runtime.SideEffect -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.graphics.toArgb -import androidx.compose.ui.platform.LocalContext - -private val DarkColorScheme = darkColorScheme( - primary = PRIMARY, - secondary = PRIMARY_DARK, - tertiary = SECONDARY_DARK -) - -private val LightColorScheme = lightColorScheme( - primary = PRIMARY, - secondary = PRIMARY_LIGHT, - tertiary = SECONDARY_LIGHT -) - -fun Color.blend(other: Color, ratio: Float): Color { - val inverse = 1f - ratio - return Color( - red = red * inverse + other.red * ratio, - green = green * inverse + other.green * ratio, - blue = blue * inverse + other.blue * ratio, - alpha = alpha - ) -} - -@Composable -fun KernelSUTheme( - darkTheme: Boolean = isSystemInDarkTheme(), - // Dynamic color is available on Android 12+ - dynamicColor: Boolean = true, - amoledMode: Boolean = false, - content: @Composable () -> Unit -) { - val colorScheme = when { - amoledMode && darkTheme && dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> { - val context = LocalContext.current - val dynamicScheme = dynamicDarkColorScheme(context) - dynamicScheme.copy( - background = AMOLED_BLACK, - surface = AMOLED_BLACK, - surfaceVariant = dynamicScheme.surfaceVariant.blend(AMOLED_BLACK, 0.6f), - surfaceContainer = dynamicScheme.surfaceContainer.blend(AMOLED_BLACK, 0.6f), - surfaceContainerLow = dynamicScheme.surfaceContainerLow.blend(AMOLED_BLACK, 0.6f), - surfaceContainerLowest = dynamicScheme.surfaceContainerLowest.blend(AMOLED_BLACK, 0.6f), - surfaceContainerHigh = dynamicScheme.surfaceContainerHigh.blend(AMOLED_BLACK, 0.6f), - surfaceContainerHighest = dynamicScheme.surfaceContainerHighest.blend(AMOLED_BLACK, 0.6f), - primaryContainer = dynamicScheme.primaryContainer.blend(AMOLED_BLACK, 0.6f), - secondaryContainer = dynamicScheme.secondaryContainer.blend(AMOLED_BLACK, 0.6f), - tertiaryContainer = dynamicScheme.tertiaryContainer.blend(AMOLED_BLACK, 0.6f) - ) - } - dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> { - val context = LocalContext.current - if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context) - } - amoledMode && darkTheme -> { - DarkColorScheme.copy( - background = AMOLED_BLACK, - surface = AMOLED_BLACK, - surfaceVariant = DARK_GREY.blend(AMOLED_BLACK, 0.8f), - surfaceContainer = DARK_GREY.blend(AMOLED_BLACK, 0.8f), - surfaceContainerLow = DARK_GREY.blend(AMOLED_BLACK, 0.8f), - surfaceContainerLowest = DARK_GREY.blend(AMOLED_BLACK, 0.8f), - surfaceContainerHigh = DARK_GREY.blend(AMOLED_BLACK, 0.8f), - surfaceContainerHighest = DARK_GREY.blend(AMOLED_BLACK, 0.8f), - ) - } - darkTheme -> DarkColorScheme - else -> LightColorScheme - } - - SystemBarStyle( - darkMode = darkTheme - ) - - MaterialTheme( - colorScheme = colorScheme, - typography = Typography, - content = content - ) -} - -@Composable -private fun SystemBarStyle( - darkMode: Boolean, - statusBarScrim: Color = Color.Transparent, - navigationBarScrim: Color = Color.Transparent, -) { - val context = LocalContext.current - val activity = context as ComponentActivity - - SideEffect { - activity.enableEdgeToEdge( - statusBarStyle = SystemBarStyle.auto( - statusBarScrim.toArgb(), - statusBarScrim.toArgb(), - ) { darkMode }, - navigationBarStyle = when { - darkMode -> SystemBarStyle.dark( - navigationBarScrim.toArgb() - ) - - else -> SystemBarStyle.light( - navigationBarScrim.toArgb(), - navigationBarScrim.toArgb(), - ) - } - ) - } -} \ No newline at end of file diff --git a/manager/app/src/main/java/com/rifsxd/ksunext/ui/theme/Type.kt b/manager/app/src/main/java/com/rifsxd/ksunext/ui/theme/Type.kt deleted file mode 100644 index 27794e70..00000000 --- a/manager/app/src/main/java/com/rifsxd/ksunext/ui/theme/Type.kt +++ /dev/null @@ -1,33 +0,0 @@ -package com.rifsxd.ksunext.ui.theme - -import androidx.compose.ui.text.TextStyle -import androidx.compose.ui.text.font.FontFamily -import androidx.compose.ui.text.font.FontWeight -import androidx.compose.ui.unit.sp - -// Set of Material typography styles to start with -val Typography = androidx.compose.material3.Typography( - bodyLarge = TextStyle( - fontFamily = FontFamily.Default, - fontWeight = FontWeight.Normal, - fontSize = 16.sp, - lineHeight = 24.sp, - letterSpacing = 0.5.sp - ) - /* Other default text styles to override - titleLarge = TextStyle( - fontFamily = FontFamily.Default, - fontWeight = FontWeight.Normal, - fontSize = 22.sp, - lineHeight = 28.sp, - letterSpacing = 0.sp - ), - labelSmall = TextStyle( - fontFamily = FontFamily.Default, - fontWeight = FontWeight.Medium, - fontSize = 11.sp, - lineHeight = 16.sp, - letterSpacing = 0.5.sp - ) - */ -) \ No newline at end of file diff --git a/manager/app/src/main/java/com/rifsxd/ksunext/ui/util/CompositionProvider.kt b/manager/app/src/main/java/com/rifsxd/ksunext/ui/util/CompositionProvider.kt deleted file mode 100644 index d0da793c..00000000 --- a/manager/app/src/main/java/com/rifsxd/ksunext/ui/util/CompositionProvider.kt +++ /dev/null @@ -1,8 +0,0 @@ -package com.rifsxd.ksunext.ui.util - -import androidx.compose.material3.SnackbarHostState -import androidx.compose.runtime.compositionLocalOf - -val LocalSnackbarHost = compositionLocalOf { - error("CompositionLocal LocalSnackbarController not present") -} \ No newline at end of file diff --git a/manager/app/src/main/java/com/rifsxd/ksunext/ui/util/Downloader.kt b/manager/app/src/main/java/com/rifsxd/ksunext/ui/util/Downloader.kt deleted file mode 100644 index 70c6af34..00000000 --- a/manager/app/src/main/java/com/rifsxd/ksunext/ui/util/Downloader.kt +++ /dev/null @@ -1,144 +0,0 @@ -package com.rifsxd.ksunext.ui.util - -import android.annotation.SuppressLint -import android.app.DownloadManager -import android.content.BroadcastReceiver -import android.content.Context -import android.content.Intent -import android.content.IntentFilter -import android.net.Uri -import android.os.Environment -import androidx.compose.runtime.Composable -import androidx.compose.runtime.DisposableEffect -import androidx.core.content.ContextCompat -import com.rifsxd.ksunext.ksuApp -import com.rifsxd.ksunext.ui.util.module.LatestVersionInfo - -/** - * @author weishu - * @date 2023/6/22. - */ -@SuppressLint("Range") -fun download( - context: Context, - url: String, - fileName: String, - description: String, - onDownloaded: (Uri) -> Unit = {}, - onDownloading: () -> Unit = {} -) { - 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) - downloadManager.query(query).use { cursor -> - while (cursor.moveToNext()) { - val uri = cursor.getString(cursor.getColumnIndex(DownloadManager.COLUMN_URI)) - val localUri = cursor.getString(cursor.getColumnIndex(DownloadManager.COLUMN_LOCAL_URI)) - val status = cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_STATUS)) - val columnTitle = cursor.getString(cursor.getColumnIndex(DownloadManager.COLUMN_TITLE)) - if (url == uri || fileName == columnTitle) { - if (status == DownloadManager.STATUS_RUNNING || status == DownloadManager.STATUS_PENDING) { - onDownloading() - return - } else if (status == DownloadManager.STATUS_SUCCESSFUL) { - onDownloaded(Uri.parse(localUri)) - return - } - } - } - } - - val request = DownloadManager.Request(Uri.parse(url)) - .setDestinationInExternalPublicDir( - Environment.DIRECTORY_DOWNLOADS, - fileName - ) - .setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED) - .setMimeType("application/zip") - .setTitle(fileName) - .setDescription(description) - - downloadManager.enqueue(request) -} - -fun checkNewVersion(): LatestVersionInfo { - // Next version updates - val url = "https://api.github.com/repos/KernelSU-Next/KernelSU-Next/releases/latest" - // default null value if failed - val defaultValue = LatestVersionInfo() - runCatching { - ksuApp.okhttpClient.newCall(okhttp3.Request.Builder().url(url).build()).execute() - .use { response -> - if (!response.isSuccessful) { - return defaultValue - } - val body = response.body?.string() ?: return defaultValue - val json = org.json.JSONObject(body) - val changelog = json.optString("body") - - val assets = json.getJSONArray("assets") - for (i in 0 until assets.length()) { - val asset = assets.getJSONObject(i) - val name = asset.getString("name") - if (!name.endsWith(".apk")) { - continue - } - - val regex = Regex("v(.+?)_(\\d+)-") - val matchResult = regex.find(name) ?: continue - val versionName = matchResult.groupValues[1] - val versionCode = matchResult.groupValues[2].toInt() - val downloadUrl = asset.getString("browser_download_url") - - return LatestVersionInfo( - versionCode, - downloadUrl, - changelog - ) - } - - } - } - return defaultValue -} - -@Composable -fun DownloadListener(context: Context, onDownloaded: (Uri) -> Unit) { - DisposableEffect(context) { - val receiver = object : BroadcastReceiver() { - @SuppressLint("Range") - override fun onReceive(context: Context?, intent: Intent?) { - if (intent?.action == DownloadManager.ACTION_DOWNLOAD_COMPLETE) { - val id = intent.getLongExtra( - DownloadManager.EXTRA_DOWNLOAD_ID, -1 - ) - val query = DownloadManager.Query().setFilterById(id) - val downloadManager = - context?.getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager - val cursor = downloadManager.query(query) - if (cursor.moveToFirst()) { - val status = cursor.getInt( - cursor.getColumnIndex(DownloadManager.COLUMN_STATUS) - ) - if (status == DownloadManager.STATUS_SUCCESSFUL) { - val uri = cursor.getString( - cursor.getColumnIndex(DownloadManager.COLUMN_LOCAL_URI) - ) - onDownloaded(Uri.parse(uri)) - } - } - } - } - } - ContextCompat.registerReceiver( - context, - receiver, - IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE), - ContextCompat.RECEIVER_EXPORTED - ) - onDispose { - context.unregisterReceiver(receiver) - } - } -} diff --git a/manager/app/src/main/java/com/rifsxd/ksunext/ui/util/HanziToPinyin.java b/manager/app/src/main/java/com/rifsxd/ksunext/ui/util/HanziToPinyin.java deleted file mode 100644 index 0f624708..00000000 --- a/manager/app/src/main/java/com/rifsxd/ksunext/ui/util/HanziToPinyin.java +++ /dev/null @@ -1,576 +0,0 @@ -package com.rifsxd.ksunext.ui.util; -/* - * Copyright (C) 2009 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import android.text.TextUtils; -import android.util.Log; - -import java.text.Collator; -import java.util.ArrayList; -import java.util.Locale; - -/** - * An object to convert Chinese character to its corresponding pinyin string. For characters with - * multiple possible pinyin string, only one is selected according to collator. Polyphone is not - * supported in this implementation. This class is implemented to achieve the best runtime - * performance and minimum runtime resources with tolerable sacrifice of accuracy. This - * implementation highly depends on zh_CN ICU collation data and must be always synchronized with - * ICU. - *

    - * Currently this file is aligned to zh.txt in ICU 4.6 - */ -public class HanziToPinyin { - private static final String TAG = "HanziToPinyin"; - - // Turn on this flag when we want to check internal data structure. - private static final boolean DEBUG = false; - - /** - * Unihans array. - *

    - * Each unihans is the first one within same pinyin when collator is zh_CN. - */ - public static final char[] UNIHANS = { - '\u963f', '\u54ce', '\u5b89', '\u80ae', '\u51f9', '\u516b', - '\u6300', '\u6273', '\u90a6', '\u52f9', '\u9642', '\u5954', - '\u4f3b', '\u5c44', '\u8fb9', '\u706c', '\u618b', '\u6c43', - '\u51ab', '\u7676', '\u5cec', '\u5693', '\u5072', '\u53c2', - '\u4ed3', '\u64a1', '\u518a', '\u5d7e', '\u66fd', '\u66fe', - '\u5c64', '\u53c9', '\u8286', '\u8fbf', '\u4f25', '\u6284', - '\u8f66', '\u62bb', '\u6c88', '\u6c89', '\u9637', '\u5403', - '\u5145', '\u62bd', '\u51fa', '\u6b3b', '\u63e3', '\u5ddb', - '\u5205', '\u5439', '\u65fe', '\u9034', '\u5472', '\u5306', - '\u51d1', '\u7c97', '\u6c46', '\u5d14', '\u90a8', '\u6413', - '\u5491', '\u5446', '\u4e39', '\u5f53', '\u5200', '\u561a', - '\u6265', '\u706f', '\u6c10', '\u55f2', '\u7538', '\u5201', - '\u7239', '\u4e01', '\u4e1f', '\u4e1c', '\u543a', '\u53be', - '\u8011', '\u8968', '\u5428', '\u591a', '\u59b8', '\u8bf6', - '\u5940', '\u97a5', '\u513f', '\u53d1', '\u5e06', '\u531a', - '\u98de', '\u5206', '\u4e30', '\u8985', '\u4ecf', '\u7d11', - '\u4f15', '\u65ee', '\u4f85', '\u7518', '\u5188', '\u768b', - '\u6208', '\u7ed9', '\u6839', '\u522f', '\u5de5', '\u52fe', - '\u4f30', '\u74dc', '\u4e56', '\u5173', '\u5149', '\u5f52', - '\u4e28', '\u5459', '\u54c8', '\u548d', '\u4f44', '\u592f', - '\u8320', '\u8bc3', '\u9ed2', '\u62eb', '\u4ea8', '\u5677', - '\u53ff', '\u9f41', '\u4e6f', '\u82b1', '\u6000', '\u72bf', - '\u5ddf', '\u7070', '\u660f', '\u5419', '\u4e0c', '\u52a0', - '\u620b', '\u6c5f', '\u827d', '\u9636', '\u5dfe', '\u5755', - '\u5182', '\u4e29', '\u51e5', '\u59e2', '\u5658', '\u519b', - '\u5494', '\u5f00', '\u520a', '\u5ffc', '\u5c3b', '\u533c', - '\u808e', '\u52a5', '\u7a7a', '\u62a0', '\u625d', '\u5938', - '\u84af', '\u5bbd', '\u5321', '\u4e8f', '\u5764', '\u6269', - '\u5783', '\u6765', '\u5170', '\u5577', '\u635e', '\u808b', - '\u52d2', '\u5d1a', '\u5215', '\u4fe9', '\u5941', '\u826f', - '\u64a9', '\u5217', '\u62ce', '\u5222', '\u6e9c', '\u56d6', - '\u9f99', '\u779c', '\u565c', '\u5a08', '\u7567', '\u62a1', - '\u7f57', '\u5463', '\u5988', '\u57cb', '\u5ada', '\u7264', - '\u732b', '\u4e48', '\u5445', '\u95e8', '\u753f', '\u54aa', - '\u5b80', '\u55b5', '\u4e5c', '\u6c11', '\u540d', '\u8c2c', - '\u6478', '\u54de', '\u6bea', '\u55ef', '\u62cf', '\u8149', - '\u56e1', '\u56d4', '\u5b6c', '\u7592', '\u5a1e', '\u6041', - '\u80fd', '\u59ae', '\u62c8', '\u5b22', '\u9e1f', '\u634f', - '\u56dc', '\u5b81', '\u599e', '\u519c', '\u7fba', '\u5974', - '\u597b', '\u759f', '\u9ec1', '\u90cd', '\u5594', '\u8bb4', - '\u5991', '\u62cd', '\u7705', '\u4e53', '\u629b', '\u5478', - '\u55b7', '\u5309', '\u4e15', '\u56e8', '\u527d', '\u6c15', - '\u59d8', '\u4e52', '\u948b', '\u5256', '\u4ec6', '\u4e03', - '\u6390', '\u5343', '\u545b', '\u6084', '\u767f', '\u4eb2', - '\u72c5', '\u828e', '\u4e18', '\u533a', '\u5cd1', '\u7f3a', - '\u590b', '\u5465', '\u7a63', '\u5a06', '\u60f9', '\u4eba', - '\u6254', '\u65e5', '\u8338', '\u53b9', '\u909a', '\u633c', - '\u5827', '\u5a51', '\u77a4', '\u637c', '\u4ee8', '\u6be2', - '\u4e09', '\u6852', '\u63bb', '\u95aa', '\u68ee', '\u50e7', - '\u6740', '\u7b5b', '\u5c71', '\u4f24', '\u5f30', '\u5962', - '\u7533', '\u8398', '\u6552', '\u5347', '\u5c38', '\u53ce', - '\u4e66', '\u5237', '\u8870', '\u95e9', '\u53cc', '\u8c01', - '\u542e', '\u8bf4', '\u53b6', '\u5fea', '\u635c', '\u82cf', - '\u72fb', '\u590a', '\u5b59', '\u5506', '\u4ed6', '\u56fc', - '\u574d', '\u6c64', '\u5932', '\u5fd1', '\u71a5', '\u5254', - '\u5929', '\u65eb', '\u5e16', '\u5385', '\u56f2', '\u5077', - '\u51f8', '\u6e4d', '\u63a8', '\u541e', '\u4e47', '\u7a75', - '\u6b6a', '\u5f2f', '\u5c23', '\u5371', '\u6637', '\u7fc1', - '\u631d', '\u4e4c', '\u5915', '\u8672', '\u4eda', '\u4e61', - '\u7071', '\u4e9b', '\u5fc3', '\u661f', '\u51f6', '\u4f11', - '\u5401', '\u5405', '\u524a', '\u5743', '\u4e2b', '\u6079', - '\u592e', '\u5e7a', '\u503b', '\u4e00', '\u56d9', '\u5e94', - '\u54df', '\u4f63', '\u4f18', '\u625c', '\u56e6', '\u66f0', - '\u6655', '\u7b60', '\u7b7c', '\u5e00', '\u707d', '\u5142', - '\u5328', '\u50ae', '\u5219', '\u8d3c', '\u600e', '\u5897', - '\u624e', '\u635a', '\u6cbe', '\u5f20', '\u957f', '\u9577', - '\u4f4b', '\u8707', '\u8d1e', '\u4e89', '\u4e4b', '\u5cd9', - '\u5ea2', '\u4e2d', '\u5dde', '\u6731', '\u6293', '\u62fd', - '\u4e13', '\u5986', '\u96b9', '\u5b92', '\u5353', '\u4e72', - '\u5b97', '\u90b9', '\u79df', '\u94bb', '\u539c', '\u5c0a', - '\u6628', '\u5159', '\u9fc3', '\u9fc4',}; - - /** - * Pinyin array. - *

    - * Each pinyin is corresponding to unihans of same - * offset in the unihans array. - */ - public static final byte[][] PINYINS = { - {65, 0, 0, 0, 0, 0}, {65, 73, 0, 0, 0, 0}, - {65, 78, 0, 0, 0, 0}, {65, 78, 71, 0, 0, 0}, - {65, 79, 0, 0, 0, 0}, {66, 65, 0, 0, 0, 0}, - {66, 65, 73, 0, 0, 0}, {66, 65, 78, 0, 0, 0}, - {66, 65, 78, 71, 0, 0}, {66, 65, 79, 0, 0, 0}, - {66, 69, 73, 0, 0, 0}, {66, 69, 78, 0, 0, 0}, - {66, 69, 78, 71, 0, 0}, {66, 73, 0, 0, 0, 0}, - {66, 73, 65, 78, 0, 0}, {66, 73, 65, 79, 0, 0}, - {66, 73, 69, 0, 0, 0}, {66, 73, 78, 0, 0, 0}, - {66, 73, 78, 71, 0, 0}, {66, 79, 0, 0, 0, 0}, - {66, 85, 0, 0, 0, 0}, {67, 65, 0, 0, 0, 0}, - {67, 65, 73, 0, 0, 0}, {67, 65, 78, 0, 0, 0}, - {67, 65, 78, 71, 0, 0}, {67, 65, 79, 0, 0, 0}, - {67, 69, 0, 0, 0, 0}, {67, 69, 78, 0, 0, 0}, - {67, 69, 78, 71, 0, 0}, {90, 69, 78, 71, 0, 0}, - {67, 69, 78, 71, 0, 0}, {67, 72, 65, 0, 0, 0}, - {67, 72, 65, 73, 0, 0}, {67, 72, 65, 78, 0, 0}, - {67, 72, 65, 78, 71, 0}, {67, 72, 65, 79, 0, 0}, - {67, 72, 69, 0, 0, 0}, {67, 72, 69, 78, 0, 0}, - {83, 72, 69, 78, 0, 0}, {67, 72, 69, 78, 0, 0}, - {67, 72, 69, 78, 71, 0}, {67, 72, 73, 0, 0, 0}, - {67, 72, 79, 78, 71, 0}, {67, 72, 79, 85, 0, 0}, - {67, 72, 85, 0, 0, 0}, {67, 72, 85, 65, 0, 0}, - {67, 72, 85, 65, 73, 0}, {67, 72, 85, 65, 78, 0}, - {67, 72, 85, 65, 78, 71}, {67, 72, 85, 73, 0, 0}, - {67, 72, 85, 78, 0, 0}, {67, 72, 85, 79, 0, 0}, - {67, 73, 0, 0, 0, 0}, {67, 79, 78, 71, 0, 0}, - {67, 79, 85, 0, 0, 0}, {67, 85, 0, 0, 0, 0}, - {67, 85, 65, 78, 0, 0}, {67, 85, 73, 0, 0, 0}, - {67, 85, 78, 0, 0, 0}, {67, 85, 79, 0, 0, 0}, - {68, 65, 0, 0, 0, 0}, {68, 65, 73, 0, 0, 0}, - {68, 65, 78, 0, 0, 0}, {68, 65, 78, 71, 0, 0}, - {68, 65, 79, 0, 0, 0}, {68, 69, 0, 0, 0, 0}, - {68, 69, 78, 0, 0, 0}, {68, 69, 78, 71, 0, 0}, - {68, 73, 0, 0, 0, 0}, {68, 73, 65, 0, 0, 0}, - {68, 73, 65, 78, 0, 0}, {68, 73, 65, 79, 0, 0}, - {68, 73, 69, 0, 0, 0}, {68, 73, 78, 71, 0, 0}, - {68, 73, 85, 0, 0, 0}, {68, 79, 78, 71, 0, 0}, - {68, 79, 85, 0, 0, 0}, {68, 85, 0, 0, 0, 0}, - {68, 85, 65, 78, 0, 0}, {68, 85, 73, 0, 0, 0}, - {68, 85, 78, 0, 0, 0}, {68, 85, 79, 0, 0, 0}, - {69, 0, 0, 0, 0, 0}, {69, 73, 0, 0, 0, 0}, - {69, 78, 0, 0, 0, 0}, {69, 78, 71, 0, 0, 0}, - {69, 82, 0, 0, 0, 0}, {70, 65, 0, 0, 0, 0}, - {70, 65, 78, 0, 0, 0}, {70, 65, 78, 71, 0, 0}, - {70, 69, 73, 0, 0, 0}, {70, 69, 78, 0, 0, 0}, - {70, 69, 78, 71, 0, 0}, {70, 73, 65, 79, 0, 0}, - {70, 79, 0, 0, 0, 0}, {70, 79, 85, 0, 0, 0}, - {70, 85, 0, 0, 0, 0}, {71, 65, 0, 0, 0, 0}, - {71, 65, 73, 0, 0, 0}, {71, 65, 78, 0, 0, 0}, - {71, 65, 78, 71, 0, 0}, {71, 65, 79, 0, 0, 0}, - {71, 69, 0, 0, 0, 0}, {71, 69, 73, 0, 0, 0}, - {71, 69, 78, 0, 0, 0}, {71, 69, 78, 71, 0, 0}, - {71, 79, 78, 71, 0, 0}, {71, 79, 85, 0, 0, 0}, - {71, 85, 0, 0, 0, 0}, {71, 85, 65, 0, 0, 0}, - {71, 85, 65, 73, 0, 0}, {71, 85, 65, 78, 0, 0}, - {71, 85, 65, 78, 71, 0}, {71, 85, 73, 0, 0, 0}, - {71, 85, 78, 0, 0, 0}, {71, 85, 79, 0, 0, 0}, - {72, 65, 0, 0, 0, 0}, {72, 65, 73, 0, 0, 0}, - {72, 65, 78, 0, 0, 0}, {72, 65, 78, 71, 0, 0}, - {72, 65, 79, 0, 0, 0}, {72, 69, 0, 0, 0, 0}, - {72, 69, 73, 0, 0, 0}, {72, 69, 78, 0, 0, 0}, - {72, 69, 78, 71, 0, 0}, {72, 77, 0, 0, 0, 0}, - {72, 79, 78, 71, 0, 0}, {72, 79, 85, 0, 0, 0}, - {72, 85, 0, 0, 0, 0}, {72, 85, 65, 0, 0, 0}, - {72, 85, 65, 73, 0, 0}, {72, 85, 65, 78, 0, 0}, - {72, 85, 65, 78, 71, 0}, {72, 85, 73, 0, 0, 0}, - {72, 85, 78, 0, 0, 0}, {72, 85, 79, 0, 0, 0}, - {74, 73, 0, 0, 0, 0}, {74, 73, 65, 0, 0, 0}, - {74, 73, 65, 78, 0, 0}, {74, 73, 65, 78, 71, 0}, - {74, 73, 65, 79, 0, 0}, {74, 73, 69, 0, 0, 0}, - {74, 73, 78, 0, 0, 0}, {74, 73, 78, 71, 0, 0}, - {74, 73, 79, 78, 71, 0}, {74, 73, 85, 0, 0, 0}, - {74, 85, 0, 0, 0, 0}, {74, 85, 65, 78, 0, 0}, - {74, 85, 69, 0, 0, 0}, {74, 85, 78, 0, 0, 0}, - {75, 65, 0, 0, 0, 0}, {75, 65, 73, 0, 0, 0}, - {75, 65, 78, 0, 0, 0}, {75, 65, 78, 71, 0, 0}, - {75, 65, 79, 0, 0, 0}, {75, 69, 0, 0, 0, 0}, - {75, 69, 78, 0, 0, 0}, {75, 69, 78, 71, 0, 0}, - {75, 79, 78, 71, 0, 0}, {75, 79, 85, 0, 0, 0}, - {75, 85, 0, 0, 0, 0}, {75, 85, 65, 0, 0, 0}, - {75, 85, 65, 73, 0, 0}, {75, 85, 65, 78, 0, 0}, - {75, 85, 65, 78, 71, 0}, {75, 85, 73, 0, 0, 0}, - {75, 85, 78, 0, 0, 0}, {75, 85, 79, 0, 0, 0}, - {76, 65, 0, 0, 0, 0}, {76, 65, 73, 0, 0, 0}, - {76, 65, 78, 0, 0, 0}, {76, 65, 78, 71, 0, 0}, - {76, 65, 79, 0, 0, 0}, {76, 69, 0, 0, 0, 0}, - {76, 69, 73, 0, 0, 0}, {76, 69, 78, 71, 0, 0}, - {76, 73, 0, 0, 0, 0}, {76, 73, 65, 0, 0, 0}, - {76, 73, 65, 78, 0, 0}, {76, 73, 65, 78, 71, 0}, - {76, 73, 65, 79, 0, 0}, {76, 73, 69, 0, 0, 0}, - {76, 73, 78, 0, 0, 0}, {76, 73, 78, 71, 0, 0}, - {76, 73, 85, 0, 0, 0}, {76, 79, 0, 0, 0, 0}, - {76, 79, 78, 71, 0, 0}, {76, 79, 85, 0, 0, 0}, - {76, 85, 0, 0, 0, 0}, {76, 85, 65, 78, 0, 0}, - {76, 85, 69, 0, 0, 0}, {76, 85, 78, 0, 0, 0}, - {76, 85, 79, 0, 0, 0}, {77, 0, 0, 0, 0, 0}, - {77, 65, 0, 0, 0, 0}, {77, 65, 73, 0, 0, 0}, - {77, 65, 78, 0, 0, 0}, {77, 65, 78, 71, 0, 0}, - {77, 65, 79, 0, 0, 0}, {77, 69, 0, 0, 0, 0}, - {77, 69, 73, 0, 0, 0}, {77, 69, 78, 0, 0, 0}, - {77, 69, 78, 71, 0, 0}, {77, 73, 0, 0, 0, 0}, - {77, 73, 65, 78, 0, 0}, {77, 73, 65, 79, 0, 0}, - {77, 73, 69, 0, 0, 0}, {77, 73, 78, 0, 0, 0}, - {77, 73, 78, 71, 0, 0}, {77, 73, 85, 0, 0, 0}, - {77, 79, 0, 0, 0, 0}, {77, 79, 85, 0, 0, 0}, - {77, 85, 0, 0, 0, 0}, {78, 0, 0, 0, 0, 0}, - {78, 65, 0, 0, 0, 0}, {78, 65, 73, 0, 0, 0}, - {78, 65, 78, 0, 0, 0}, {78, 65, 78, 71, 0, 0}, - {78, 65, 79, 0, 0, 0}, {78, 69, 0, 0, 0, 0}, - {78, 69, 73, 0, 0, 0}, {78, 69, 78, 0, 0, 0}, - {78, 69, 78, 71, 0, 0}, {78, 73, 0, 0, 0, 0}, - {78, 73, 65, 78, 0, 0}, {78, 73, 65, 78, 71, 0}, - {78, 73, 65, 79, 0, 0}, {78, 73, 69, 0, 0, 0}, - {78, 73, 78, 0, 0, 0}, {78, 73, 78, 71, 0, 0}, - {78, 73, 85, 0, 0, 0}, {78, 79, 78, 71, 0, 0}, - {78, 79, 85, 0, 0, 0}, {78, 85, 0, 0, 0, 0}, - {78, 85, 65, 78, 0, 0}, {78, 85, 69, 0, 0, 0}, - {78, 85, 78, 0, 0, 0}, {78, 85, 79, 0, 0, 0}, - {79, 0, 0, 0, 0, 0}, {79, 85, 0, 0, 0, 0}, - {80, 65, 0, 0, 0, 0}, {80, 65, 73, 0, 0, 0}, - {80, 65, 78, 0, 0, 0}, {80, 65, 78, 71, 0, 0}, - {80, 65, 79, 0, 0, 0}, {80, 69, 73, 0, 0, 0}, - {80, 69, 78, 0, 0, 0}, {80, 69, 78, 71, 0, 0}, - {80, 73, 0, 0, 0, 0}, {80, 73, 65, 78, 0, 0}, - {80, 73, 65, 79, 0, 0}, {80, 73, 69, 0, 0, 0}, - {80, 73, 78, 0, 0, 0}, {80, 73, 78, 71, 0, 0}, - {80, 79, 0, 0, 0, 0}, {80, 79, 85, 0, 0, 0}, - {80, 85, 0, 0, 0, 0}, {81, 73, 0, 0, 0, 0}, - {81, 73, 65, 0, 0, 0}, {81, 73, 65, 78, 0, 0}, - {81, 73, 65, 78, 71, 0}, {81, 73, 65, 79, 0, 0}, - {81, 73, 69, 0, 0, 0}, {81, 73, 78, 0, 0, 0}, - {81, 73, 78, 71, 0, 0}, {81, 73, 79, 78, 71, 0}, - {81, 73, 85, 0, 0, 0}, {81, 85, 0, 0, 0, 0}, - {81, 85, 65, 78, 0, 0}, {81, 85, 69, 0, 0, 0}, - {81, 85, 78, 0, 0, 0}, {82, 65, 78, 0, 0, 0}, - {82, 65, 78, 71, 0, 0}, {82, 65, 79, 0, 0, 0}, - {82, 69, 0, 0, 0, 0}, {82, 69, 78, 0, 0, 0}, - {82, 69, 78, 71, 0, 0}, {82, 73, 0, 0, 0, 0}, - {82, 79, 78, 71, 0, 0}, {82, 79, 85, 0, 0, 0}, - {82, 85, 0, 0, 0, 0}, {82, 85, 65, 0, 0, 0}, - {82, 85, 65, 78, 0, 0}, {82, 85, 73, 0, 0, 0}, - {82, 85, 78, 0, 0, 0}, {82, 85, 79, 0, 0, 0}, - {83, 65, 0, 0, 0, 0}, {83, 65, 73, 0, 0, 0}, - {83, 65, 78, 0, 0, 0}, {83, 65, 78, 71, 0, 0}, - {83, 65, 79, 0, 0, 0}, {83, 69, 0, 0, 0, 0}, - {83, 69, 78, 0, 0, 0}, {83, 69, 78, 71, 0, 0}, - {83, 72, 65, 0, 0, 0}, {83, 72, 65, 73, 0, 0}, - {83, 72, 65, 78, 0, 0}, {83, 72, 65, 78, 71, 0}, - {83, 72, 65, 79, 0, 0}, {83, 72, 69, 0, 0, 0}, - {83, 72, 69, 78, 0, 0}, {88, 73, 78, 0, 0, 0}, - {83, 72, 69, 78, 0, 0}, {83, 72, 69, 78, 71, 0}, - {83, 72, 73, 0, 0, 0}, {83, 72, 79, 85, 0, 0}, - {83, 72, 85, 0, 0, 0}, {83, 72, 85, 65, 0, 0}, - {83, 72, 85, 65, 73, 0}, {83, 72, 85, 65, 78, 0}, - {83, 72, 85, 65, 78, 71}, {83, 72, 85, 73, 0, 0}, - {83, 72, 85, 78, 0, 0}, {83, 72, 85, 79, 0, 0}, - {83, 73, 0, 0, 0, 0}, {83, 79, 78, 71, 0, 0}, - {83, 79, 85, 0, 0, 0}, {83, 85, 0, 0, 0, 0}, - {83, 85, 65, 78, 0, 0}, {83, 85, 73, 0, 0, 0}, - {83, 85, 78, 0, 0, 0}, {83, 85, 79, 0, 0, 0}, - {84, 65, 0, 0, 0, 0}, {84, 65, 73, 0, 0, 0}, - {84, 65, 78, 0, 0, 0}, {84, 65, 78, 71, 0, 0}, - {84, 65, 79, 0, 0, 0}, {84, 69, 0, 0, 0, 0}, - {84, 69, 78, 71, 0, 0}, {84, 73, 0, 0, 0, 0}, - {84, 73, 65, 78, 0, 0}, {84, 73, 65, 79, 0, 0}, - {84, 73, 69, 0, 0, 0}, {84, 73, 78, 71, 0, 0}, - {84, 79, 78, 71, 0, 0}, {84, 79, 85, 0, 0, 0}, - {84, 85, 0, 0, 0, 0}, {84, 85, 65, 78, 0, 0}, - {84, 85, 73, 0, 0, 0}, {84, 85, 78, 0, 0, 0}, - {84, 85, 79, 0, 0, 0}, {87, 65, 0, 0, 0, 0}, - {87, 65, 73, 0, 0, 0}, {87, 65, 78, 0, 0, 0}, - {87, 65, 78, 71, 0, 0}, {87, 69, 73, 0, 0, 0}, - {87, 69, 78, 0, 0, 0}, {87, 69, 78, 71, 0, 0}, - {87, 79, 0, 0, 0, 0}, {87, 85, 0, 0, 0, 0}, - {88, 73, 0, 0, 0, 0}, {88, 73, 65, 0, 0, 0}, - {88, 73, 65, 78, 0, 0}, {88, 73, 65, 78, 71, 0}, - {88, 73, 65, 79, 0, 0}, {88, 73, 69, 0, 0, 0}, - {88, 73, 78, 0, 0, 0}, {88, 73, 78, 71, 0, 0}, - {88, 73, 79, 78, 71, 0}, {88, 73, 85, 0, 0, 0}, - {88, 85, 0, 0, 0, 0}, {88, 85, 65, 78, 0, 0}, - {88, 85, 69, 0, 0, 0}, {88, 85, 78, 0, 0, 0}, - {89, 65, 0, 0, 0, 0}, {89, 65, 78, 0, 0, 0}, - {89, 65, 78, 71, 0, 0}, {89, 65, 79, 0, 0, 0}, - {89, 69, 0, 0, 0, 0}, {89, 73, 0, 0, 0, 0}, - {89, 73, 78, 0, 0, 0}, {89, 73, 78, 71, 0, 0}, - {89, 79, 0, 0, 0, 0}, {89, 79, 78, 71, 0, 0}, - {89, 79, 85, 0, 0, 0}, {89, 85, 0, 0, 0, 0}, - {89, 85, 65, 78, 0, 0}, {89, 85, 69, 0, 0, 0}, - {89, 85, 78, 0, 0, 0}, {74, 85, 78, 0, 0, 0}, - {89, 85, 78, 0, 0, 0}, {90, 65, 0, 0, 0, 0}, - {90, 65, 73, 0, 0, 0}, {90, 65, 78, 0, 0, 0}, - {90, 65, 78, 71, 0, 0}, {90, 65, 79, 0, 0, 0}, - {90, 69, 0, 0, 0, 0}, {90, 69, 73, 0, 0, 0}, - {90, 69, 78, 0, 0, 0}, {90, 69, 78, 71, 0, 0}, - {90, 72, 65, 0, 0, 0}, {90, 72, 65, 73, 0, 0}, - {90, 72, 65, 78, 0, 0}, {90, 72, 65, 78, 71, 0}, - {67, 72, 65, 78, 71, 0}, {90, 72, 65, 78, 71, 0}, - {90, 72, 65, 79, 0, 0}, {90, 72, 69, 0, 0, 0}, - {90, 72, 69, 78, 0, 0}, {90, 72, 69, 78, 71, 0}, - {90, 72, 73, 0, 0, 0}, {83, 72, 73, 0, 0, 0}, - {90, 72, 73, 0, 0, 0}, {90, 72, 79, 78, 71, 0}, - {90, 72, 79, 85, 0, 0}, {90, 72, 85, 0, 0, 0}, - {90, 72, 85, 65, 0, 0}, {90, 72, 85, 65, 73, 0}, - {90, 72, 85, 65, 78, 0}, {90, 72, 85, 65, 78, 71}, - {90, 72, 85, 73, 0, 0}, {90, 72, 85, 78, 0, 0}, - {90, 72, 85, 79, 0, 0}, {90, 73, 0, 0, 0, 0}, - {90, 79, 78, 71, 0, 0}, {90, 79, 85, 0, 0, 0}, - {90, 85, 0, 0, 0, 0}, {90, 85, 65, 78, 0, 0}, - {90, 85, 73, 0, 0, 0}, {90, 85, 78, 0, 0, 0}, - {90, 85, 79, 0, 0, 0}, {0, 0, 0, 0, 0, 0}, - {83, 72, 65, 78, 0, 0}, {0, 0, 0, 0, 0, 0},}; - - /** - * First and last Chinese character with known Pinyin according to zh collation - */ - private static final String FIRST_PINYIN_UNIHAN = "\u963F"; - private static final String LAST_PINYIN_UNIHAN = "\u9FFF"; - - private static final Collator COLLATOR = Collator.getInstance(Locale.CHINA); - - private static HanziToPinyin sInstance; - private final boolean mHasChinaCollator; - - public static class Token { - /** - * Separator between target string for each source char - */ - public static final String SEPARATOR = " "; - - public static final int LATIN = 1; - public static final int PINYIN = 2; - public static final int UNKNOWN = 3; - - public Token() { - } - - public Token(int type, String source, String target) { - this.type = type; - this.source = source; - this.target = target; - } - - /** - * Type of this token, ASCII, PINYIN or UNKNOWN. - */ - public int type; - /** - * Original string before translation. - */ - public String source; - /** - * Translated string of source. For Han, target is corresponding Pinyin. Otherwise target is - * original string in source. - */ - public String target; - } - - protected HanziToPinyin(boolean hasChinaCollator) { - mHasChinaCollator = hasChinaCollator; - } - - public static HanziToPinyin getInstance() { - synchronized (HanziToPinyin.class) { - if (sInstance != null) { - return sInstance; - } - // Check if zh_CN collation data is available - final Locale[] locale = Collator.getAvailableLocales(); - for (Locale value : locale) { - if (value.equals(Locale.CHINA) || value.getLanguage().contains("zh")) { - // Do self validation just once. - if (DEBUG) { - Log.d(TAG, "Self validation. Result: " + doSelfValidation()); - } - sInstance = new HanziToPinyin(true); - return sInstance; - } - } - if (sInstance == null){//这个判断是用于处理国产ROM的兼容性问题 - if (Locale.CHINA.equals(Locale.getDefault())){ - sInstance = new HanziToPinyin(true); - return sInstance; - } - } - Log.w(TAG, "There is no Chinese collator, HanziToPinyin is disabled"); - sInstance = new HanziToPinyin(false); - return sInstance; - } - } - - /** - * Validate if our internal table has some wrong value. - * - * @return true when the table looks correct. - */ - private static boolean doSelfValidation() { - char lastChar = UNIHANS[0]; - String lastString = Character.toString(lastChar); - for (char c : UNIHANS) { - if (lastChar == c) { - continue; - } - final String curString = Character.toString(c); - int cmp = COLLATOR.compare(lastString, curString); - if (cmp >= 0) { - Log.e(TAG, "Internal error in Unihan table. " + "The last string \"" + lastString - + "\" is greater than current string \"" + curString + "\"."); - return false; - } - lastString = curString; - } - return true; - } - - private Token getToken(char character) { - Token token = new Token(); - final String letter = Character.toString(character); - token.source = letter; - int offset = -1; - int cmp; - if (character < 256) { - token.type = Token.LATIN; - token.target = letter; - return token; - } else { - cmp = COLLATOR.compare(letter, FIRST_PINYIN_UNIHAN); - if (cmp < 0) { - token.type = Token.UNKNOWN; - token.target = letter; - return token; - } else if (cmp == 0) { - token.type = Token.PINYIN; - offset = 0; - } else { - cmp = COLLATOR.compare(letter, LAST_PINYIN_UNIHAN); - if (cmp > 0) { - token.type = Token.UNKNOWN; - token.target = letter; - return token; - } else if (cmp == 0) { - token.type = Token.PINYIN; - offset = UNIHANS.length - 1; - } - } - } - - token.type = Token.PINYIN; - if (offset < 0) { - int begin = 0; - int end = UNIHANS.length - 1; - while (begin <= end) { - offset = (begin + end) / 2; - final String unihan = Character.toString(UNIHANS[offset]); - cmp = COLLATOR.compare(letter, unihan); - if (cmp == 0) { - break; - } else if (cmp > 0) { - begin = offset + 1; - } else { - end = offset - 1; - } - } - } - if (cmp < 0) { - offset--; - } - StringBuilder pinyin = new StringBuilder(); - for (int j = 0; j < PINYINS[offset].length && PINYINS[offset][j] != 0; j++) { - pinyin.append((char) PINYINS[offset][j]); - } - token.target = pinyin.toString(); - if (TextUtils.isEmpty(token.target)) { - token.type = Token.UNKNOWN; - token.target = token.source; - } - return token; - } - - /** - * Convert the input to a array of tokens. The sequence of ASCII or Unknown characters without - * space will be put into a Token, One Hanzi character which has pinyin will be treated as a - * Token. If these is no China collator, the empty token array is returned. - */ - public ArrayList get(final String input) { - ArrayList tokens = new ArrayList<>(); - if (!mHasChinaCollator || TextUtils.isEmpty(input)) { - // return empty tokens. - return tokens; - } - final int inputLength = input.length(); - final StringBuilder sb = new StringBuilder(); - int tokenType = Token.LATIN; - // Go through the input, create a new token when - // a. Token type changed - // b. Get the Pinyin of current charater. - // c. current character is space. - for (int i = 0; i < inputLength; i++) { - final char character = input.charAt(i); - if (character == ' ') { - if (sb.length() > 0) { - addToken(sb, tokens, tokenType); - } - } else if (character < 256) { - if (tokenType != Token.LATIN && sb.length() > 0) { - addToken(sb, tokens, tokenType); - } - tokenType = Token.LATIN; - sb.append(character); - } else { - Token t = getToken(character); - if (t.type == Token.PINYIN) { - if (sb.length() > 0) { - addToken(sb, tokens, tokenType); - } - tokens.add(t); - tokenType = Token.PINYIN; - } else { - if (tokenType != t.type && sb.length() > 0) { - addToken(sb, tokens, tokenType); - } - tokenType = t.type; - sb.append(character); - } - } - } - if (sb.length() > 0) { - addToken(sb, tokens, tokenType); - } - return tokens; - } - - private void addToken( - final StringBuilder sb, final ArrayList tokens, final int tokenType) { - String str = sb.toString(); - tokens.add(new Token(tokenType, str, str)); - sb.setLength(0); - } - - public String toPinyinString(String string) { - if (string == null) { - return null; - } - StringBuilder sb = new StringBuilder(); - ArrayList tokens = get(string); - for (Token token : tokens) { - sb.append(token.target); - } - return sb.toString().toLowerCase(); - } -} \ No newline at end of file diff --git a/manager/app/src/main/java/com/rifsxd/ksunext/ui/util/HyperlinkText.kt b/manager/app/src/main/java/com/rifsxd/ksunext/ui/util/HyperlinkText.kt deleted file mode 100644 index 0c1cf43b..00000000 --- a/manager/app/src/main/java/com/rifsxd/ksunext/ui/util/HyperlinkText.kt +++ /dev/null @@ -1,87 +0,0 @@ -package com.rifsxd.ksunext.ui.util - -import androidx.compose.foundation.gestures.detectTapGestures -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.Text -import androidx.compose.runtime.Composable -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.ui.Modifier -import androidx.compose.ui.input.pointer.pointerInput -import androidx.compose.ui.platform.LocalUriHandler -import androidx.compose.ui.text.SpanStyle -import androidx.compose.ui.text.TextLayoutResult -import androidx.compose.ui.text.buildAnnotatedString -import androidx.compose.ui.text.style.TextDecoration -import java.util.regex.Pattern - -@Composable -fun LinkifyText( - text: String, - modifier: Modifier = Modifier -) { - val uriHandler = LocalUriHandler.current - val layoutResult = remember { - mutableStateOf(null) - } - val linksList = extractUrls(text) - val annotatedString = buildAnnotatedString { - append(text) - linksList.forEach { - addStyle( - style = SpanStyle( - color = MaterialTheme.colorScheme.primary, - textDecoration = TextDecoration.Underline - ), - start = it.start, - end = it.end - ) - addStringAnnotation( - tag = "URL", - annotation = it.url, - start = it.start, - end = it.end - ) - } - } - Text( - text = annotatedString, - modifier = modifier.pointerInput(Unit) { - detectTapGestures { offsetPosition -> - layoutResult.value?.let { - val position = it.getOffsetForPosition(offsetPosition) - annotatedString.getStringAnnotations(position, position).firstOrNull() - ?.let { result -> - if (result.tag == "URL") { - uriHandler.openUri(result.item) - } - } - } - } - }, - onTextLayout = { layoutResult.value = it } - ) -} - -private val urlPattern: Pattern = Pattern.compile( - "(?:^|[\\W])((ht|f)tp(s?):\\/\\/|www\\.)" - + "(([\\w\\-]+\\.){1,}?([\\w\\-.~]+\\/?)*" - + "[\\p{Alnum}.,%_=?&#\\-+()\\[\\]\\*$~@!:/{};']*)", - Pattern.CASE_INSENSITIVE or Pattern.MULTILINE or Pattern.DOTALL -) - -private data class LinkInfo( - val url: String, - val start: Int, - val end: Int -) - -private fun extractUrls(text: String): List = buildList { - val matcher = urlPattern.matcher(text) - while (matcher.find()) { - val matchStart = matcher.start(1) - val matchEnd = matcher.end() - val url = text.substring(matchStart, matchEnd).replaceFirst("http://", "https://") - add(LinkInfo(url, matchStart, matchEnd)) - } -} diff --git a/manager/app/src/main/java/com/rifsxd/ksunext/ui/util/KsuCli.kt b/manager/app/src/main/java/com/rifsxd/ksunext/ui/util/KsuCli.kt deleted file mode 100644 index c8c04baf..00000000 --- a/manager/app/src/main/java/com/rifsxd/ksunext/ui/util/KsuCli.kt +++ /dev/null @@ -1,677 +0,0 @@ -package com.rifsxd.ksunext.ui.util - -import android.content.ContentResolver -import android.content.Context -import android.content.Intent -import android.database.Cursor -import android.net.Uri -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 kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.withContext -import kotlinx.parcelize.Parcelize -import com.rifsxd.ksunext.BuildConfig -import com.rifsxd.ksunext.Natives -import com.rifsxd.ksunext.ksuApp -import org.json.JSONArray -import java.io.File - -/** - * @author weishu - * @date 2023/1/1. - */ -private const val TAG = "KsuCli" -private const val BUSYBOX = "/data/adb/ksu/bin/busybox" - -private fun ksuDaemonMagicPath(): String { - return ksuApp.applicationInfo.nativeLibraryDir + File.separator + "libksud_magic.so" -} - -private fun ksuDaemonOverlayfsPath(): String { - return ksuApp.applicationInfo.nativeLibraryDir + File.separator + "libksud_overlayfs.so" -} - -fun readMountSystemFile(): Boolean { - val shell = getRootShell() - val filePath = "/data/adb/ksu/mount_system" - val result = ShellUtils.fastCmd(shell, "cat $filePath").trim() - return result == "OVERLAYFS" -} - -// Get the path based on the user's choice -fun getKsuDaemonPath(): String { - val useOverlayFs = readMountSystemFile() - - return if (useOverlayFs) { - ksuDaemonOverlayfsPath() - } else { - ksuDaemonMagicPath() - } -} - -fun updateMountSystemFile(useOverlayFs: Boolean) { - val shell = getRootShell() - val filePath = "/data/adb/ksu/mount_system" - if (useOverlayFs) { - ShellUtils.fastCmd(shell, "echo -n OVERLAYFS > $filePath") - } else { - ShellUtils.fastCmd(shell, "echo -n MAGIC_MOUNT > $filePath") - } -} - -data class FlashResult(val code: Int, val err: String, val showReboot: Boolean) { - constructor(result: Shell.Result, showReboot: Boolean) : this(result.code, result.err.joinToString("\n"), showReboot) - constructor(result: Shell.Result) : this(result, result.isSuccess) -} - -object KsuCli { - val SHELL: Shell = createRootShell() - val GLOBAL_MNT_SHELL: Shell = createRootShell(true) -} - -fun getRootShell(globalMnt: Boolean = false): Shell { - return if (globalMnt) KsuCli.GLOBAL_MNT_SHELL else { - KsuCli.SHELL - } -} - -inline fun withNewRootShell( - globalMnt: Boolean = false, - block: Shell.() -> T -): T { - return createRootShell(globalMnt).use(block) -} - -fun Uri.getFileName(context: Context): String? { - var fileName: String? = null - val contentResolver: ContentResolver = context.contentResolver - val cursor: Cursor? = contentResolver.query(this, null, null, null, null) - cursor?.use { - if (it.moveToFirst()) { - fileName = it.getString(it.getColumnIndexOrThrow(OpenableColumns.DISPLAY_NAME)) - } - } - return fileName -} - -fun createRootShell(globalMnt: Boolean = false): Shell { - Shell.enableVerboseLogging = BuildConfig.DEBUG - val builder = Shell.Builder.create() - return try { - if (globalMnt) { - builder.build(ksuDaemonMagicPath(), "debug", "su", "-g") - } else { - builder.build(ksuDaemonMagicPath(), "debug", "su") - } - } catch (e: Throwable) { - Log.w(TAG, "ksu failed: ", e) - try { - if (globalMnt) { - builder.build("su", "-mm") - } else { - builder.build("su") - } - } catch (e: Throwable) { - Log.e(TAG, "su failed: ", e) - builder.build("sh") - } - } -} - -fun execKsud(args: String, newShell: Boolean = false): Boolean { - return if (newShell) { - withNewRootShell { - ShellUtils.fastCmdResult(this, "${getKsuDaemonPath()} $args") - } - } else { - ShellUtils.fastCmdResult(getRootShell(), "${getKsuDaemonPath()} $args") - } -} - -fun install() { - val start = SystemClock.elapsedRealtime() - val magiskboot = File(ksuApp.applicationInfo.nativeLibraryDir, "libmagiskboot.so").absolutePath - val result = execKsud("install --magiskboot $magiskboot", true) - Log.w(TAG, "install result: $result, cost: ${SystemClock.elapsedRealtime() - start}ms") -} - -fun listModules(): String { - val shell = getRootShell() - - val out = - shell.newJob().add("${getKsuDaemonPath()} module list").to(ArrayList(), null).exec().out - return out.joinToString("\n").ifBlank { "[]" } -} - -fun getModuleCount(): Int { - val result = listModules() - runCatching { - val array = JSONArray(result) - return array.length() - }.getOrElse { return 0 } -} - -fun getSuperuserCount(): Int { - return Natives.allowList.size -} - -fun toggleModule(id: String, enable: Boolean): Boolean { - val cmd = if (enable) { - "module enable $id" - } else { - "module disable $id" - } - val result = execKsud(cmd, true) - Log.i(TAG, "$cmd result: $result") - return result -} - -fun uninstallModule(id: String): Boolean { - val cmd = "module uninstall $id" - val result = execKsud(cmd, true) - Log.i(TAG, "uninstall module $id result: $result") - return result -} - -fun restoreModule(id: String): Boolean { - val cmd = "module restore $id" - val result = execKsud(cmd, true) - Log.i(TAG, "restore module $id result: $result") - return result -} - -private fun flashWithIO( - cmd: String, - onStdout: (String) -> Unit, - onStderr: (String) -> Unit -): Shell.Result { - - val stdoutCallback: CallbackList = object : CallbackList() { - override fun onAddElement(s: String?) { - onStdout(s ?: "") - } - } - - val stderrCallback: CallbackList = object : CallbackList() { - override fun onAddElement(s: String?) { - onStderr(s ?: "") - } - } - - return withNewRootShell { - newJob().add(cmd).to(stdoutCallback, stderrCallback).exec() - } -} - -fun flashModule( - uri: Uri, - onStdout: (String) -> Unit, - onStderr: (String) -> Unit -): FlashResult { - val resolver = ksuApp.contentResolver - with(resolver.openInputStream(uri)) { - val file = File(ksuApp.cacheDir, "module.zip") - file.outputStream().use { output -> - this?.copyTo(output) - } - val cmd = "module install ${file.absolutePath}" - val result = flashWithIO("${getKsuDaemonPath()} $cmd", onStdout, onStderr) - Log.i("KernelSU", "install module $uri result: $result") - - file.delete() - - return FlashResult(result) - } -} - -fun runModuleAction( - moduleId: String, onStdout: (String) -> Unit, onStderr: (String) -> Unit -): Boolean { - val shell = createRootShell(true) - - 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( - onStdout: (String) -> Unit, onStderr: (String) -> Unit -): FlashResult { - val magiskboot = File(ksuApp.applicationInfo.nativeLibraryDir, "libmagiskboot.so") - val result = flashWithIO("${getKsuDaemonPath()} boot-restore -f --magiskboot $magiskboot", onStdout, onStderr) - return FlashResult(result) -} - -fun uninstallPermanently( - onStdout: (String) -> Unit, onStderr: (String) -> Unit -): FlashResult { - val magiskboot = File(ksuApp.applicationInfo.nativeLibraryDir, "libmagiskboot.so") - val result = flashWithIO("${getKsuDaemonPath()} uninstall --magiskboot $magiskboot", onStdout, onStderr) - return FlashResult(result) -} - -suspend fun shrinkModules(): Boolean = withContext(Dispatchers.IO) { - execKsud("module shrink", true) -} - -@Parcelize -sealed class LkmSelection : Parcelable { - data class LkmUri(val uri: Uri) : LkmSelection() - data class KmiString(val value: String) : LkmSelection() - data object KmiNone : LkmSelection() -} - -fun installBoot( - bootUri: Uri?, - lkm: LkmSelection, - ota: Boolean, - onStdout: (String) -> Unit, - onStderr: (String) -> Unit, -): FlashResult { - val resolver = ksuApp.contentResolver - - val bootFile = bootUri?.let { uri -> - with(resolver.openInputStream(uri)) { - val bootFile = File(ksuApp.cacheDir, "boot.img") - bootFile.outputStream().use { output -> - this?.copyTo(output) - } - - bootFile - } - } - - val magiskboot = File(ksuApp.applicationInfo.nativeLibraryDir, "libmagiskboot.so") - var cmd = "boot-patch --magiskboot ${magiskboot.absolutePath}" - - cmd += if (bootFile == null) { - // no boot.img, use -f to force install - " -f" - } else { - " -b ${bootFile.absolutePath}" - } - - if (ota) { - cmd += " -u" - } - - var lkmFile: File? = null - when (lkm) { - is LkmSelection.LkmUri -> { - lkmFile = with(resolver.openInputStream(lkm.uri)) { - val file = File(ksuApp.cacheDir, "kernelsu-tmp-lkm.ko") - file.outputStream().use { output -> - this?.copyTo(output) - } - - file - } - cmd += " -m ${lkmFile.absolutePath}" - } - - is LkmSelection.KmiString -> { - cmd += " --kmi ${lkm.value}" - } - - LkmSelection.KmiNone -> { - // do nothing - } - } - - // output dir - val downloadsDir = - Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS) - cmd += " -o $downloadsDir" - - val result = flashWithIO("${getKsuDaemonPath()} $cmd", onStdout, onStderr) - Log.i("KernelSU", "install boot result: ${result.isSuccess}") - - bootFile?.delete() - lkmFile?.delete() - - // if boot uri is empty, it is direct install, when success, we should show reboot button - return FlashResult(result, bootUri == null && result.isSuccess) -} - -fun reboot(reason: String = "") { - val shell = getRootShell() - if (reason == "recovery") { - // KEYCODE_POWER = 26, hide incorrect "Factory data reset" message - ShellUtils.fastCmd(shell, "/system/bin/reboot $reason") - } - ShellUtils.fastCmd(shell, "/system/bin/svc power reboot $reason || /system/bin/reboot $reason") -} - -fun rootAvailable(): Boolean { - val shell = getRootShell() - return shell.isRoot -} - -fun isAbDevice(): Boolean { - val shell = getRootShell() - return ShellUtils.fastCmd(shell, "getprop ro.build.ab_update").trim().toBoolean() -} - -fun isInitBoot(): Boolean { - return !Os.uname().release.contains("android12-") -} - -suspend fun getCurrentKmi(): String = withContext(Dispatchers.IO) { - val shell = getRootShell() - val cmd = "boot-info current-kmi" - ShellUtils.fastCmd(shell, "${getKsuDaemonPath()} $cmd") -} - -suspend fun getSupportedKmis(): List = withContext(Dispatchers.IO) { - val shell = getRootShell() - val cmd = "boot-info supported-kmi" - val out = shell.newJob().add("${getKsuDaemonPath()} $cmd").to(ArrayList(), null).exec().out - out.filter { it.isNotBlank() }.map { it.trim() } -} - -fun overlayFsAvailable(): Boolean { - val shell = getRootShell() - // check /proc/filesystems - return ShellUtils.fastCmdResult(shell, "cat /proc/filesystems | grep overlay") -} - -fun hasMagisk(): Boolean { - val shell = getRootShell(true) - val result = shell.newJob().add("which magisk").exec() - Log.i(TAG, "has magisk: ${result.isSuccess}") - return result.isSuccess -} - -fun isSepolicyValid(rules: String?): Boolean { - if (rules == null) { - return true - } - val shell = getRootShell() - val result = - shell.newJob().add("${getKsuDaemonPath()} sepolicy check '$rules'").to(ArrayList(), null) - .exec() - return result.isSuccess -} - -fun getSepolicy(pkg: String): String { - val shell = getRootShell() - val result = - shell.newJob().add("${getKsuDaemonPath()} profile get-sepolicy $pkg").to(ArrayList(), null) - .exec() - Log.i(TAG, "code: ${result.code}, out: ${result.out}, err: ${result.err}") - return result.out.joinToString("\n") -} - -fun setSepolicy(pkg: String, rules: String): Boolean { - val shell = getRootShell() - val result = shell.newJob().add("${getKsuDaemonPath()} profile set-sepolicy $pkg '$rules'") - .to(ArrayList(), null).exec() - Log.i(TAG, "set sepolicy result: ${result.code}") - return result.isSuccess -} - -fun listAppProfileTemplates(): List { - val shell = getRootShell() - return shell.newJob().add("${getKsuDaemonPath()} profile list-templates").to(ArrayList(), null) - .exec().out -} - -fun getAppProfileTemplate(id: String): String { - val shell = getRootShell() - return shell.newJob().add("${getKsuDaemonPath()} profile get-template '${id}'") - .to(ArrayList(), null).exec().out.joinToString("\n") -} - -fun getFileName(context: Context, uri: Uri): String { - var name = "Unknown Module" - if (uri.scheme == ContentResolver.SCHEME_CONTENT) { - val cursor: Cursor? = context.contentResolver.query(uri, null, null, null, null) - cursor?.use { - if (it.moveToFirst()) { - name = it.getString(it.getColumnIndexOrThrow(OpenableColumns.DISPLAY_NAME)) - } - } - } else if (uri.scheme == "file") { - name = uri.lastPathSegment ?: "Unknown Module" - } - return name -} - -fun moduleBackupDir(): String? { - val shell = getRootShell() - val baseBackupDir = "/data/adb/ksu/backup/modules" - val resultBase = ShellUtils.fastCmd(shell, "mkdir -p $baseBackupDir").trim() - if (resultBase.isNotEmpty()) return null - - val timestamp = ShellUtils.fastCmd(shell, "date +%Y%m%d_%H%M%S").trim() - if (timestamp.isEmpty()) return null - - val newBackupDir = "$baseBackupDir/$timestamp" - val resultNewDir = ShellUtils.fastCmd(shell, "mkdir -p $newBackupDir").trim() - - if (resultNewDir.isEmpty()) return newBackupDir - return null -} - -fun moduleBackup(): Boolean { - val shell = getRootShell() - - val checkEmptyCommand = "if [ -z \"$(ls -A /data/adb/modules)\" ]; then echo 'empty'; fi" - val resultCheckEmpty = ShellUtils.fastCmd(shell, checkEmptyCommand).trim() - if (resultCheckEmpty == "empty") { - return false - } - - val timestamp = ShellUtils.fastCmd(shell, "date +%Y%m%d_%H%M%S").trim() - if (timestamp.isEmpty()) return false - - val tarName = "modules_backup_$timestamp.tar" - val tarPath = "/data/local/tmp/$tarName" - val internalBackupDir = "/data/adb/ksu/backup/modules" - val internalBackupPath = "$internalBackupDir/$tarName" - - val tarCmd = "$BUSYBOX tar -cpf $tarPath -C /data/adb/modules $(ls /data/adb/modules)" - val tarResult = ShellUtils.fastCmd(shell, tarCmd).trim() - if (tarResult.isNotEmpty()) return false - - ShellUtils.fastCmd(shell, "mkdir -p $internalBackupDir") - - val cpResult = ShellUtils.fastCmd(shell, "cp $tarPath $internalBackupPath").trim() - if (cpResult.isNotEmpty()) return false - - ShellUtils.fastCmd(shell, "rm -f $tarPath") - - return true -} - -fun moduleRestore(): Boolean { - val shell = getRootShell() - - val findTarCmd = "ls -t /data/adb/ksu/backup/modules/modules_backup_*.tar 2>/dev/null | head -n 1" - val tarPath = ShellUtils.fastCmd(shell, findTarCmd).trim() - if (tarPath.isEmpty()) return false - - val extractCmd = "$BUSYBOX tar -xpf $tarPath -C /data/adb/modules_update" - val extractResult = ShellUtils.fastCmd(shell, extractCmd).trim() - return extractResult.isEmpty() -} - -fun allowlistBackup(): Boolean { - val shell = getRootShell() - - val checkEmptyCommand = "if [ -z \"$(ls -A /data/adb/ksu/.allowlist)\" ]; then echo 'empty'; fi" - val resultCheckEmpty = ShellUtils.fastCmd(shell, checkEmptyCommand).trim() - if (resultCheckEmpty == "empty") { - return false - } - - val timestamp = ShellUtils.fastCmd(shell, "date +%Y%m%d_%H%M%S").trim() - if (timestamp.isEmpty()) return false - - val tarName = "allowlist_backup_$timestamp.tar" - val tarPath = "/data/local/tmp/$tarName" - val internalBackupDir = "/data/adb/ksu/backup/allowlist" - val internalBackupPath = "$internalBackupDir/$tarName" - - val tarCmd = "$BUSYBOX tar -cpf $tarPath -C /data/adb/ksu .allowlist" - val tarResult = ShellUtils.fastCmd(shell, tarCmd).trim() - if (tarResult.isNotEmpty()) return false - - ShellUtils.fastCmd(shell, "mkdir -p $internalBackupDir") - - val cpResult = ShellUtils.fastCmd(shell, "cp $tarPath $internalBackupPath").trim() - if (cpResult.isNotEmpty()) return false - - ShellUtils.fastCmd(shell, "rm -f $tarPath") - - return true -} - -fun allowlistRestore(): Boolean { - val shell = getRootShell() - - // Find the latest allowlist tar backup in /data/adb/ksu/backup/allowlist - val findTarCmd = "ls -t /data/adb/ksu/backup/allowlist/allowlist_backup_*.tar 2>/dev/null | head -n 1" - val tarPath = ShellUtils.fastCmd(shell, findTarCmd).trim() - if (tarPath.isEmpty()) return false - - // Extract the tar to /data/adb/ksu (restores .allowlist folder with permissions) - val extractCmd = "$BUSYBOX tar -xpf $tarPath -C /data/adb/ksu" - val extractResult = ShellUtils.fastCmd(shell, extractCmd).trim() - return extractResult.isEmpty() -} - -fun moduleMigration(): Boolean { - val shell = getRootShell() - val command = "cp -rp /data/adb/modules/* /data/adb/modules_update" - val result = ShellUtils.fastCmd(shell, command).trim() - - return result.isEmpty() -} - -private fun getSuSFSDaemonPath(): String { - return ksuApp.applicationInfo.nativeLibraryDir + File.separator + "libsusfsd.so" -} - -fun getSuSFS(): String { - val shell = getRootShell() - val result = ShellUtils.fastCmd(shell, "${getSuSFSDaemonPath()} support") - return result -} - -fun getSuSFSVersion(): String { - val shell = getRootShell() - val result = ShellUtils.fastCmd(shell, "${getSuSFSDaemonPath()} version") - return result -} - -fun getSuSFSVariant(): String { - val shell = getRootShell() - val result = ShellUtils.fastCmd(shell, "${getSuSFSDaemonPath()} variant") - return result -} -fun getSuSFSFeatures(): String { - val shell = getRootShell() - val result = ShellUtils.fastCmd(shell, "${getSuSFSDaemonPath()} features") - return result -} - -fun susfsSUS_SU_0(): String { - val shell = getRootShell() - val result = ShellUtils.fastCmd(shell, "${getSuSFSDaemonPath()} sus_su 0") - return result -} - -fun susfsSUS_SU_2(): String { - val shell = getRootShell() - val result = ShellUtils.fastCmd(shell, "${getSuSFSDaemonPath()} sus_su 2") - return result -} - -fun susfsSUS_SU_Mode(): String { - val shell = getRootShell() - val result = ShellUtils.fastCmd(shell, "${getSuSFSDaemonPath()} sus_su mode") - return result -} - -fun currentMountSystem(): String { - val shell = getRootShell() - val cmd = "module mount" - val result = ShellUtils.fastCmd(shell, "${getKsuDaemonPath()} $cmd").trim() - return result.substringAfter(":").substringAfter(" ").trim() -} - -fun getModuleSize(dir: File): Long { - val shell = getRootShell() - val cmd = "$BUSYBOX du -sb '${dir.absolutePath}' | awk '{print \$1}'" - val result = ShellUtils.fastCmd(shell, cmd).trim() - return result.toLongOrNull() ?: 0L -} - -fun isSuCompatDisabled(): Boolean { - return Natives.version >= Natives.MINIMAL_SUPPORTED_SU_COMPAT && !Natives.isSuEnabled() -} - -fun zygiskRequired(dir: File): Boolean { - val shell = getRootShell() - val zygiskLib = "${dir.absolutePath}/zygisk" - val cmd = "ls \"$zygiskLib\"" - val result = ShellUtils.fastCmdResult(shell, cmd) - return result -} - -fun setAppProfileTemplate(id: String, template: String): Boolean { - val shell = getRootShell() - val escapedTemplate = template.replace("\"", "\\\"") - val cmd = """${getKsuDaemonPath()} profile set-template "$id" "$escapedTemplate'"""" - return shell.newJob().add(cmd) - .to(ArrayList(), null).exec().isSuccess -} - -fun deleteAppProfileTemplate(id: String): Boolean { - val shell = getRootShell() - return shell.newJob().add("${getKsuDaemonPath()} profile delete-template '${id}'") - .to(ArrayList(), null).exec().isSuccess -} - -fun forceStopApp(packageName: String) { - val shell = getRootShell() - val result = shell.newJob().add("am force-stop $packageName").exec() - Log.i(TAG, "force stop $packageName result: $result") -} - -fun launchApp(packageName: String) { - - val shell = getRootShell() - val result = - shell.newJob() - .add("cmd package resolve-activity --brief $packageName | tail -n 1 | xargs cmd activity start-activity -n") - .exec() - Log.i(TAG, "launch $packageName result: $result") -} - -fun restartApp(packageName: String) { - forceStopApp(packageName) - launchApp(packageName) -} diff --git a/manager/app/src/main/java/com/rifsxd/ksunext/ui/util/LocaleHelper.kt b/manager/app/src/main/java/com/rifsxd/ksunext/ui/util/LocaleHelper.kt deleted file mode 100644 index 5869afba..00000000 --- a/manager/app/src/main/java/com/rifsxd/ksunext/ui/util/LocaleHelper.kt +++ /dev/null @@ -1,149 +0,0 @@ -package com.rifsxd.ksunext.ui.util - -import android.annotation.TargetApi -import android.app.Activity -import android.content.Context -import android.content.Intent -import android.content.res.Configuration -import android.net.Uri -import android.os.Build -import android.provider.Settings -import java.util.Locale - -object LocaleHelper { - - /** - * Check if should use system language settings (Android 13+) - */ - val useSystemLanguageSettings: Boolean - get() = Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU - - /** - * Launch system app locale settings (Android 13+) - */ - fun launchSystemLanguageSettings(context: Context) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { - try { - val intent = Intent(Settings.ACTION_APP_LOCALE_SETTINGS).apply { - data = Uri.fromParts("package", context.packageName, null) - } - context.startActivity(intent) - } catch (e: Exception) { - // Fallback to app language settings if system settings not available - } - } - } - - /** - * Apply saved language setting to context (for Android < 13) - */ - fun applyLanguage(context: Context): Context { - // On Android 13+, language is handled by system - if (useSystemLanguageSettings) { - return context - } - - val prefs = context.getSharedPreferences("settings", Context.MODE_PRIVATE) - val localeTag = prefs.getString("app_locale", "system") ?: "system" - - return if (localeTag == "system") { - context - } else { - val locale = parseLocaleTag(localeTag) - setLocale(context, locale) - } - } - - /** - * Set locale for context (Android < 13) - */ - private fun setLocale(context: Context, locale: Locale): Context { - return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { - updateResources(context, locale) - } else { - updateResourcesLegacy(context, locale) - } - } - - @TargetApi(Build.VERSION_CODES.N) - private fun updateResources(context: Context, locale: Locale): Context { - val configuration = Configuration() - configuration.setLocale(locale) - configuration.setLayoutDirection(locale) - return context.createConfigurationContext(configuration) - } - - @SuppressWarnings("deprecation") - private fun updateResourcesLegacy(context: Context, locale: Locale): Context { - Locale.setDefault(locale) - val resources = context.resources - val configuration = resources.configuration - configuration.locale = locale - configuration.setLayoutDirection(locale) - resources.updateConfiguration(configuration, resources.displayMetrics) - return context - } - - /** - * Parse locale tag to Locale object - */ - private fun parseLocaleTag(tag: String): Locale { - return try { - if (tag.contains("_")) { - val parts = tag.split("_") - Locale.Builder() - .setLanguage(parts[0]) - .setRegion(parts.getOrNull(1) ?: "") - .build() - } else { - Locale.Builder() - .setLanguage(tag) - .build() - } - } catch (e: Exception) { - Locale.getDefault() - } - } - - /** - * Restart activity to apply language change (Android < 13) - */ - fun restartActivity(context: Context) { - if (context is Activity && !useSystemLanguageSettings) { - context.recreate() - } - } - - /** - * Get current app locale - */ - fun getCurrentAppLocale(context: Context): Locale? { - return if (useSystemLanguageSettings) { - // Android 13+ - get from system app locale settings - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { - try { - val localeManager = context.getSystemService(Context.LOCALE_SERVICE) as? android.app.LocaleManager - val locales = localeManager?.applicationLocales - if (locales != null && !locales.isEmpty) { - locales.get(0) - } else { - null // System default - } - } catch (e: Exception) { - null // System default - } - } else { - null // System default - } - } else { - // Android < 13 - get from SharedPreferences - val prefs = context.getSharedPreferences("settings", Context.MODE_PRIVATE) - val localeTag = prefs.getString("app_locale", "system") ?: "system" - if (localeTag == "system") { - null // System default - } else { - parseLocaleTag(localeTag) - } - } - } -} \ No newline at end of file diff --git a/manager/app/src/main/java/com/rifsxd/ksunext/ui/util/LogEvent.kt b/manager/app/src/main/java/com/rifsxd/ksunext/ui/util/LogEvent.kt deleted file mode 100644 index 643df864..00000000 --- a/manager/app/src/main/java/com/rifsxd/ksunext/ui/util/LogEvent.kt +++ /dev/null @@ -1,112 +0,0 @@ -package com.rifsxd.ksunext.ui.util - -import android.content.Context -import android.os.Build -import android.system.Os -import com.topjohnwu.superuser.ShellUtils -import com.rifsxd.ksunext.Natives -import com.rifsxd.ksunext.ui.screen.getManagerVersion -import java.io.File -import java.io.FileWriter -import java.io.PrintWriter -import java.time.LocalDateTime -import java.time.format.DateTimeFormatter - -fun getBugreportFile(context: Context): File { - - val bugreportDir = File(context.cacheDir, "bugreport") - bugreportDir.mkdirs() - - val dmesgFile = File(bugreportDir, "dmesg.txt") - val logcatFile = File(bugreportDir, "logcat.txt") - val tombstonesFile = File(bugreportDir, "tombstones.tar.gz") - val dropboxFile = File(bugreportDir, "dropbox.tar.gz") - val pstoreFile = File(bugreportDir, "pstore.tar.gz") - // Xiaomi/Readmi devices have diag in /data/vendor/diag - val diagFile = File(bugreportDir, "diag.tar.gz") - val opulsFile = File(bugreportDir, "opuls.tar.gz") - val bootlogFile = File(bugreportDir, "bootlog.tar.gz") - val mountsFile = File(bugreportDir, "mounts.txt") - val fileSystemsFile = File(bugreportDir, "filesystems.txt") - val adbFileTree = File(bugreportDir, "adb_tree.txt") - val adbFileDetails = File(bugreportDir, "adb_details.txt") - val ksuFileSize = File(bugreportDir, "ksu_size.txt") - val appListFile = File(bugreportDir, "packages.txt") - val propFile = File(bugreportDir, "props.txt") - val allowListFile = File(bugreportDir, "allowlist.bin") - val procModules = File(bugreportDir, "proc_modules.txt") - val bootConfig = File(bugreportDir, "boot_config.txt") - val kernelConfig = File(bugreportDir, "defconfig.gz") - - val shell = getRootShell(true) - - shell.newJob().add("dmesg > ${dmesgFile.absolutePath}").exec() - shell.newJob().add("logcat -d > ${logcatFile.absolutePath}").exec() - shell.newJob().add("tar -czf ${tombstonesFile.absolutePath} -C /data/tombstones .").exec() - shell.newJob().add("tar -czf ${dropboxFile.absolutePath} -C /data/system/dropbox .").exec() - shell.newJob().add("tar -czf ${pstoreFile.absolutePath} -C /sys/fs/pstore .").exec() - shell.newJob().add("tar -czf ${diagFile.absolutePath} -C /data/vendor/diag . --exclude=./minidump.gz").exec() - shell.newJob().add("tar -czf ${opulsFile.absolutePath} -C /mnt/oplus/op2/media/log/boot_log/ .").exec() - shell.newJob().add("tar -czf ${bootlogFile.absolutePath} -C /data/adb/ksu/log .").exec() - - shell.newJob().add("cat /proc/1/mountinfo > ${mountsFile.absolutePath}").exec() - shell.newJob().add("cat /proc/filesystems > ${fileSystemsFile.absolutePath}").exec() - shell.newJob().add("busybox tree /data/adb > ${adbFileTree.absolutePath}").exec() - shell.newJob().add("ls -alRZ /data/adb > ${adbFileDetails.absolutePath}").exec() - shell.newJob().add("du -sh /data/adb/ksu/* > ${ksuFileSize.absolutePath}").exec() - shell.newJob().add("cp /data/system/packages.list ${appListFile.absolutePath}").exec() - shell.newJob().add("getprop > ${propFile.absolutePath}").exec() - shell.newJob().add("cp /data/adb/ksu/.allowlist ${allowListFile.absolutePath}").exec() - shell.newJob().add("cp /proc/modules ${procModules.absolutePath}").exec() - shell.newJob().add("cp /proc/bootconfig ${bootConfig.absolutePath}").exec() - shell.newJob().add("cp /proc/config.gz ${kernelConfig.absolutePath}").exec() - - val selinux = ShellUtils.fastCmd(shell, "getenforce") - - // basic information - val buildInfo = File(bugreportDir, "basic.txt") - PrintWriter(FileWriter(buildInfo)).use { pw -> - pw.println("Kernel: ${System.getProperty("os.version")}") - pw.println("BRAND: " + Build.BRAND) - pw.println("MODEL: " + Build.MODEL) - pw.println("PRODUCT: " + Build.PRODUCT) - pw.println("MANUFACTURER: " + Build.MANUFACTURER) - pw.println("ANDROID: " + Build.VERSION.RELEASE) - pw.println("SDK: " + Build.VERSION.SDK_INT) - pw.println("PREVIEW_SDK: " + Build.VERSION.PREVIEW_SDK_INT) - pw.println("FINGERPRINT: " + Build.FINGERPRINT) - pw.println("DEVICE: " + Build.DEVICE) - pw.println("Manager: " + getManagerVersion(context)) - pw.println("SELinux: $selinux") - - val uname = Os.uname() - pw.println("KernelRelease: ${uname.release}") - pw.println("KernelVersion: ${uname.version}") - pw.println("Machine: ${uname.machine}") - pw.println("Nodename: ${uname.nodename}") - pw.println("Sysname: ${uname.sysname}") - - val ksuKernel = Natives.version - pw.println("KernelSU: $ksuKernel") - val safeMode = Natives.isSafeMode - pw.println("SafeMode: $safeMode") - val lkmMode = Natives.isLkmMode - pw.println("LKM: $lkmMode") - } - - // modules - val modulesFile = File(bugreportDir, "modules.json") - modulesFile.writeText(listModules()) - - val formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd_HH_mm") - val current = LocalDateTime.now().format(formatter) - - val targetFile = File(context.cacheDir, "KernelSU_Next_bugreport_${current}.tar.gz") - - shell.newJob().add("tar czf ${targetFile.absolutePath} -C ${bugreportDir.absolutePath} .").exec() - shell.newJob().add("rm -rf ${bugreportDir.absolutePath}").exec() - shell.newJob().add("chmod 0644 ${targetFile.absolutePath}").exec() - - return targetFile -} - diff --git a/manager/app/src/main/java/com/rifsxd/ksunext/ui/util/SELinuxChecker.kt b/manager/app/src/main/java/com/rifsxd/ksunext/ui/util/SELinuxChecker.kt deleted file mode 100644 index f29e3bbd..00000000 --- a/manager/app/src/main/java/com/rifsxd/ksunext/ui/util/SELinuxChecker.kt +++ /dev/null @@ -1,32 +0,0 @@ -package com.rifsxd.ksunext.ui.util - -import androidx.compose.runtime.Composable -import androidx.compose.ui.res.stringResource -import com.topjohnwu.superuser.Shell -import com.rifsxd.ksunext.R - -@Composable -fun getSELinuxStatus(): String { - val shell = Shell.getShell() // Get the default shell instance - val list = ArrayList() - val result = shell.newJob() - .add("getenforce") - .to(list, list) - .exec() - val output = list.joinToString("\n").trim() - - if (result.isSuccess) { - return when (output) { - "Enforcing" -> stringResource(R.string.selinux_status_enforcing) - "Permissive" -> stringResource(R.string.selinux_status_permissive) - "Disabled" -> stringResource(R.string.selinux_status_disabled) - else -> stringResource(R.string.selinux_status_unknown) - } - } - - return if (output.endsWith("Permission denied")) { - stringResource(R.string.selinux_status_enforcing) - } else { - stringResource(R.string.selinux_status_unknown) - } -} diff --git a/manager/app/src/main/java/com/rifsxd/ksunext/ui/util/module/LatestVersionInfo.kt b/manager/app/src/main/java/com/rifsxd/ksunext/ui/util/module/LatestVersionInfo.kt deleted file mode 100644 index 1fc40314..00000000 --- a/manager/app/src/main/java/com/rifsxd/ksunext/ui/util/module/LatestVersionInfo.kt +++ /dev/null @@ -1,7 +0,0 @@ -package com.rifsxd.ksunext.ui.util.module - -data class LatestVersionInfo( - val versionCode : Int = 0, - val downloadUrl : String = "", - val changelog : String = "" -) diff --git a/manager/app/src/main/java/com/rifsxd/ksunext/ui/viewmodel/ModuleViewModel.kt b/manager/app/src/main/java/com/rifsxd/ksunext/ui/viewmodel/ModuleViewModel.kt deleted file mode 100644 index c5e3d30e..00000000 --- a/manager/app/src/main/java/com/rifsxd/ksunext/ui/viewmodel/ModuleViewModel.kt +++ /dev/null @@ -1,218 +0,0 @@ -package com.rifsxd.ksunext.ui.viewmodel - -import android.net.Uri -import android.os.SystemClock -import android.util.Log -import androidx.compose.runtime.derivedStateOf -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.setValue -import androidx.lifecycle.ViewModel -import androidx.lifecycle.viewModelScope -import kotlinx.coroutines.delay -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.launch -import kotlinx.coroutines.withContext -import kotlinx.coroutines.withTimeoutOrNull -import java.io.File -import java.text.Collator -import java.util.Locale -import com.rifsxd.ksunext.ksuApp -import com.rifsxd.ksunext.ui.util.HanziToPinyin -import com.rifsxd.ksunext.ui.util.listModules -import com.rifsxd.ksunext.ui.util.getModuleSize -import com.rifsxd.ksunext.ui.util.zygiskRequired -import org.json.JSONArray -import org.json.JSONObject - -class ModuleViewModel : ViewModel() { - - companion object { - private const val TAG = "ModuleViewModel" - private var modules by mutableStateOf>(emptyList()) - } - - class ModuleInfo( - val id: String, - val name: String, - val author: String, - val version: String, - val versionCode: Int, - val description: String, - val enabled: Boolean, - val update: Boolean, - val remove: Boolean, - val updateJson: String, - val hasWebUi: Boolean, - val hasActionScript: Boolean, - val dirId: String, - val size: Long, - val banner: String, - val zygiskRequired: Boolean - ) - - data class ModuleUpdateInfo( - val version: String, - val versionCode: Int, - val zipUrl: String, - val changelog: String, - ) - - var isRefreshing by mutableStateOf(false) - private set - - var search by mutableStateOf("") - - var sortAToZ by mutableStateOf(false) - var sortZToA by mutableStateOf(false) - var sortSizeLowToHigh by mutableStateOf(false) - var sortSizeHighToLow by mutableStateOf(false) - var sortEnabledFirst by mutableStateOf(false) - var sortActionFirst by mutableStateOf(false) - var sortWebUiFirst by mutableStateOf(false) - - val moduleList by derivedStateOf { - val comparator = when { - sortWebUiFirst -> compareByDescending { it.hasWebUi } - sortEnabledFirst -> compareByDescending { it.enabled } - sortActionFirst -> compareByDescending { it.hasActionScript } - sortAToZ -> compareBy { it.name.lowercase() } - sortZToA -> compareByDescending { it.name.lowercase() } - sortSizeLowToHigh -> compareBy { it.size } - sortSizeHighToLow -> compareByDescending { it.size } - else -> compareBy { it.dirId } - }.thenBy(Collator.getInstance(Locale.getDefault()), ModuleInfo::id) - - modules.filter { - it.id.contains(search, ignoreCase = true) || - it.name.contains(search, ignoreCase = true) || - HanziToPinyin.getInstance().toPinyinString(it.name).contains(search, ignoreCase = true) - }.sortedWith(comparator).also { - isRefreshing = false - } - } - - - var isNeedRefresh by mutableStateOf(false) - private set - - fun markNeedRefresh() { - isNeedRefresh = true - } - - var zipUris by mutableStateOf>(emptyList()) - - fun updateZipUris(uris: List) { - zipUris = uris - } - - fun clearZipUris() { - zipUris = emptyList() - } - - fun fetchModuleList() { - - viewModelScope.launch { - - isRefreshing = true - - withContext(Dispatchers.IO) { - val start = SystemClock.elapsedRealtime() - val oldModuleList = modules - - kotlin.runCatching { - val result = listModules() - Log.i(TAG, "result: $result") - - val array = JSONArray(result) - modules = (0 until array.length()) - .asSequence() - .map { array.getJSONObject(it) } - .map { obj -> - val id = obj.getString("id") - val dirId = obj.getString("dir_id") - val moduleDir = File("/data/adb/modules/$dirId") - val size = getModuleSize(moduleDir) - val zygiskRequired = zygiskRequired(moduleDir) - - ModuleInfo( - id, - obj.optString("name"), - obj.optString("author", "Unknown"), - obj.optString("version", "Unknown"), - obj.optInt("versionCode", 0), - obj.optString("description"), - obj.getBoolean("enabled"), - obj.getBoolean("update"), - obj.getBoolean("remove"), - obj.optString("updateJson"), - obj.optBoolean("web"), - obj.optBoolean("action"), - dirId, - size, - obj.optString("banner"), - zygiskRequired - ) - }.toList() - isNeedRefresh = false - }.onFailure { e -> - Log.e(TAG, "fetchModuleList: ", e) - isRefreshing = false - } - - // when both old and new is kotlin.collections.EmptyList - // moduleList update will don't trigger - if (oldModuleList === modules) { - isRefreshing = false - } - - Log.i(TAG, "load cost: ${SystemClock.elapsedRealtime() - start}, modules: $modules") - } - } - } - - private fun sanitizeVersionString(version: String): String { - return version.replace(Regex("[^a-zA-Z0-9.\\-_]"), "_") - } - - fun checkUpdate(m: ModuleInfo): Triple { - val empty = Triple("", "", "") - if (m.updateJson.isEmpty() || m.remove || m.update || !m.enabled) { - return empty - } - // download updateJson - val result = kotlin.runCatching { - val url = m.updateJson - Log.i(TAG, "checkUpdate url: $url") - val response = ksuApp.okhttpClient.newCall( - okhttp3.Request.Builder().url(url).build() - ).execute() - Log.d(TAG, "checkUpdate code: ${response.code}") - if (response.isSuccessful) { - response.body?.string() ?: "" - } else { - "" - } - }.getOrDefault("") - Log.i(TAG, "checkUpdate result: $result") - - if (result.isEmpty()) { - return empty - } - - val updateJson = kotlin.runCatching { - JSONObject(result) - }.getOrNull() ?: return empty - - var version = updateJson.optString("version", "") - version = sanitizeVersionString(version) - val versionCode = updateJson.optInt("versionCode", 0) - val zipUrl = updateJson.optString("zipUrl", "") - val changelog = updateJson.optString("changelog", "") - if (versionCode <= m.versionCode || zipUrl.isEmpty()) { - return empty - } - - return Triple(zipUrl, version, changelog) - } -} diff --git a/manager/app/src/main/java/com/rifsxd/ksunext/ui/viewmodel/SuperUserViewModel.kt b/manager/app/src/main/java/com/rifsxd/ksunext/ui/viewmodel/SuperUserViewModel.kt deleted file mode 100644 index 28ddf140..00000000 --- a/manager/app/src/main/java/com/rifsxd/ksunext/ui/viewmodel/SuperUserViewModel.kt +++ /dev/null @@ -1,184 +0,0 @@ -package com.rifsxd.ksunext.ui.viewmodel - -import android.content.ComponentName -import android.content.Context -import android.content.Intent -import android.content.ServiceConnection -import android.content.pm.ApplicationInfo -import android.content.pm.PackageInfo -import android.os.IBinder -import android.os.Parcelable -import android.os.SystemClock -import android.util.Log -import android.widget.Toast -import androidx.compose.runtime.derivedStateOf -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.setValue -import androidx.lifecycle.ViewModel -import com.topjohnwu.superuser.Shell -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.withContext -import kotlinx.parcelize.Parcelize -import com.rifsxd.ksunext.IKsuInterface -import com.rifsxd.ksunext.Natives -import com.rifsxd.ksunext.ksuApp -import com.rifsxd.ksunext.ui.KsuService -import com.rifsxd.ksunext.ui.util.HanziToPinyin -import com.rifsxd.ksunext.ui.util.KsuCli -import kotlinx.coroutines.delay -import kotlinx.coroutines.withTimeoutOrNull -import java.text.Collator -import java.util.* -import kotlin.coroutines.resume -import kotlin.coroutines.suspendCoroutine -import androidx.core.content.edit - -class SuperUserViewModel : ViewModel() { - - companion object { - private const val TAG = "SuperUserViewModel" - private var apps by mutableStateOf>(emptyList()) - private var profileOverrides by mutableStateOf>(emptyMap()) - } - - @Parcelize - data class AppInfo( - val label: String, - val packageInfo: PackageInfo, - val profile: Natives.Profile?, - ) : Parcelable { - val packageName: String - get() = packageInfo.packageName - val uid: Int - get() = packageInfo.applicationInfo!!.uid - - val allowSu: Boolean - get() = profile != null && profile.allowSu - val hasCustomProfile: Boolean - get() { - if (profile == null) { - return false - } - - return if (profile.allowSu) { - !profile.rootUseDefault - } else { - !profile.nonRootUseDefault - } - } - } - - private val prefs = ksuApp.getSharedPreferences("settings", Context.MODE_PRIVATE)!! - - var search by mutableStateOf("") - var showSystemApps by mutableStateOf(prefs.getBoolean("show_system_apps", false)) - private set - var isRefreshing by mutableStateOf(false) - private set - - fun updateShowSystemApps(newValue: Boolean) { - showSystemApps = newValue - prefs.edit { putBoolean("show_system_apps", newValue) } - } - - private val sortedList by derivedStateOf { - val comparator = compareBy { - when { - it.profile != null && it.profile.allowSu -> 0 - it.profile != null && ( - if (it.profile.allowSu) !it.profile.rootUseDefault else !it.profile.nonRootUseDefault - ) -> 1 - else -> 2 - } - }.then(compareBy(Collator.getInstance(Locale.getDefault()), AppInfo::label)) - apps.sortedWith(comparator).also { - isRefreshing = false - } - } - - val appList by derivedStateOf { - sortedList.map { app -> - profileOverrides[app.packageName]?.let { app.copy(profile = it) } ?: app - }.filter { - it.label.contains(search, true) || it.packageName.contains( - search, - true - ) || HanziToPinyin.getInstance() - .toPinyinString(it.label).contains(search, true) - }.filter { - it.uid == 2000 // Always show shell - || showSystemApps || it.packageInfo.applicationInfo!!.flags.and(ApplicationInfo.FLAG_SYSTEM) == 0 - } - } - - fun updateAppProfile(packageName: String, newProfile: Natives.Profile) { - profileOverrides = profileOverrides.toMutableMap().apply { - put(packageName, newProfile) - } - } - - private suspend inline fun connectKsuService( - crossinline onDisconnect: () -> Unit = {} - ): Pair = suspendCoroutine { - val connection = object : ServiceConnection { - override fun onServiceDisconnected(name: ComponentName?) { - onDisconnect() - } - - override fun onServiceConnected(name: ComponentName?, binder: IBinder?) { - it.resume(binder as IBinder to this) - } - } - - val intent = Intent(ksuApp, KsuService::class.java) - - val task = KsuService.bindOrTask( - intent, - Shell.EXECUTOR, - connection, - ) - val shell = KsuCli.SHELL - task?.let { it1 -> shell.execTask(it1) } - } - - private fun stopKsuService() { - val intent = Intent(ksuApp, KsuService::class.java) - KsuService.stop(intent) - } - - suspend fun fetchAppList() { - - isRefreshing = true - - val result = connectKsuService { - Log.w(TAG, "KsuService disconnected") - } - - withContext(Dispatchers.IO) { - val pm = ksuApp.packageManager - val start = SystemClock.elapsedRealtime() - - val binder = result.first - val allPackages = IKsuInterface.Stub.asInterface(binder).getPackages(0) - - withContext(Dispatchers.Main) { - stopKsuService() - } - - val packages = allPackages.list - - apps = packages.map { - val appInfo = it.applicationInfo - val uid = appInfo!!.uid - val profile = Natives.getAppProfile(it.packageName, uid) - AppInfo( - label = appInfo.loadLabel(pm).toString(), - packageInfo = it, - profile = profile, - ) - }.filter { it.packageName != ksuApp.packageName } - Log.i(TAG, "load cost: ${SystemClock.elapsedRealtime() - start}") - } - } -} diff --git a/manager/app/src/main/java/com/rifsxd/ksunext/ui/viewmodel/TemplateViewModel.kt b/manager/app/src/main/java/com/rifsxd/ksunext/ui/viewmodel/TemplateViewModel.kt deleted file mode 100644 index b7a134b3..00000000 --- a/manager/app/src/main/java/com/rifsxd/ksunext/ui/viewmodel/TemplateViewModel.kt +++ /dev/null @@ -1,321 +0,0 @@ -package com.rifsxd.ksunext.ui.viewmodel - -import android.os.Parcelable -import android.util.Log -import androidx.compose.runtime.derivedStateOf -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.setValue -import androidx.lifecycle.ViewModel -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.withContext -import kotlinx.parcelize.Parcelize -import com.rifsxd.ksunext.Natives -import com.rifsxd.ksunext.ksuApp -import com.rifsxd.ksunext.profile.Capabilities -import com.rifsxd.ksunext.profile.Groups -import com.rifsxd.ksunext.ui.util.getAppProfileTemplate -import com.rifsxd.ksunext.ui.util.listAppProfileTemplates -import com.rifsxd.ksunext.ui.util.setAppProfileTemplate -import okhttp3.Request -import org.json.JSONArray -import org.json.JSONObject -import java.text.Collator -import java.util.Locale - - -/** - * @author weishu - * @date 2023/10/20. - */ -const val TEMPLATE_INDEX_URL = "https://kernelsu.org/templates/index.json" -const val TEMPLATE_URL = "https://kernelsu.org/templates/%s" - -const val TAG = "TemplateViewModel" - -class TemplateViewModel : ViewModel() { - companion object { - - private var templates by mutableStateOf>(emptyList()) - } - - @Parcelize - data class TemplateInfo( - val id: String = "", - val name: String = "", - val description: String = "", - val author: String = "", - val local: Boolean = true, - - val namespace: Int = Natives.Profile.Namespace.INHERITED.ordinal, - val uid: Int = Natives.ROOT_UID, - val gid: Int = Natives.ROOT_GID, - val groups: List = mutableListOf(), - val capabilities: List = mutableListOf(), - val context: String = Natives.KERNEL_SU_DOMAIN, - val rules: List = mutableListOf(), - ) : Parcelable - - var isRefreshing by mutableStateOf(false) - private set - - val templateList by derivedStateOf { - val comparator = compareBy(TemplateInfo::local).reversed().then( - compareBy( - Collator.getInstance(Locale.getDefault()), TemplateInfo::id - ) - ) - templates.sortedWith(comparator).apply { - isRefreshing = false - } - } - - suspend fun fetchTemplates(sync: Boolean = false) { - isRefreshing = true - withContext(Dispatchers.IO) { - val localTemplateIds = listAppProfileTemplates() - Log.i(TAG, "localTemplateIds: $localTemplateIds") - if (localTemplateIds.isEmpty() || sync) { - // if no templates, fetch remote templates - fetchRemoteTemplates() - } - - // fetch templates again - templates = listAppProfileTemplates().mapNotNull(::getTemplateInfoById) - - isRefreshing = false - } - } - - suspend fun importTemplates( - templates: String, - onSuccess: suspend () -> Unit, - onFailure: suspend (String) -> Unit - ) { - withContext(Dispatchers.IO) { - runCatching { - JSONArray(templates) - }.getOrElse { - runCatching { - val json = JSONObject(templates) - JSONArray().apply { put(json) } - }.getOrElse { - onFailure("invalid templates: $templates") - return@withContext - } - }.let { - 0.until(it.length()).forEach { i -> - runCatching { - val template = it.getJSONObject(i) - val id = template.getString("id") - template.put("local", true) - setAppProfileTemplate(id, template.toString()) - }.onFailure { e -> - Log.e(TAG, "ignore invalid template: $it", e) - } - } - onSuccess() - } - } - } - - suspend fun exportTemplates(onTemplateEmpty: () -> Unit, callback: (String) -> Unit) { - withContext(Dispatchers.IO) { - val templates = listAppProfileTemplates().mapNotNull(::getTemplateInfoById).filter { - it.local - } - templates.ifEmpty { - onTemplateEmpty() - return@withContext - } - JSONArray(templates.map { - it.toJSON() - }).toString().let(callback) - } - } -} - -private fun fetchRemoteTemplates() { - runCatching { - ksuApp.okhttpClient.newCall( - Request.Builder().url(TEMPLATE_INDEX_URL).build() - ).execute().use { response -> - if (!response.isSuccessful) { - return - } - val remoteTemplateIds = JSONArray(response.body!!.string()) - Log.i(TAG, "fetchRemoteTemplates: $remoteTemplateIds") - 0.until(remoteTemplateIds.length()).forEach { i -> - val id = remoteTemplateIds.getString(i) - Log.i(TAG, "fetch template: $id") - val templateJson = ksuApp.okhttpClient.newCall( - Request.Builder().url(TEMPLATE_URL.format(id)).build() - ).runCatching { - execute().use { response -> - if (!response.isSuccessful) { - return@forEach - } - response.body!!.string() - } - }.getOrNull() ?: return@forEach - Log.i(TAG, "template: $templateJson") - - // validate remote template - runCatching { - val json = JSONObject(templateJson) - fromJSON(json)?.let { - // force local template - json.put("local", false) - setAppProfileTemplate(id, json.toString()) - } - }.onFailure { - Log.e(TAG, "ignore invalid template: $it", it) - return@forEach - } - } - } - }.onFailure { Log.e(TAG, "fetchRemoteTemplates: $it", it) } -} - -@Suppress("UNCHECKED_CAST") -private fun JSONArray.mapCatching( - transform: (T) -> R, onFail: (Throwable) -> Unit -): List { - return List(length()) { i -> get(i) as T }.mapNotNull { element -> - runCatching { - transform(element) - }.onFailure(onFail).getOrNull() - } -} - -private inline fun > getEnumOrdinals( - jsonArray: JSONArray?, enumClass: Class -): List { - return jsonArray?.mapCatching({ name -> - enumValueOf(name.uppercase()) - }, { - Log.e(TAG, "ignore invalid enum ${enumClass.simpleName}: $it", it) - }).orEmpty() -} - -fun getTemplateInfoById(id: String): TemplateViewModel.TemplateInfo? { - return runCatching { - fromJSON(JSONObject(getAppProfileTemplate(id))) - }.onFailure { - Log.e(TAG, "ignore invalid template: $it", it) - }.getOrNull() -} - -private fun getLocaleString(json: JSONObject, key: String): String { - val fallback = json.getString(key) - val locale = Locale.getDefault() - val localeKey = "${locale.language}_${locale.country}" - json.optJSONObject("locales")?.let { - // check locale first - it.optJSONObject(localeKey)?.let { json-> - return json.optString(key, fallback) - } - // fallback to language - it.optJSONObject(locale.language)?.let { json-> - return json.optString(key, fallback) - } - } - return fallback -} - -private fun fromJSON(templateJson: JSONObject): TemplateViewModel.TemplateInfo? { - return runCatching { - val groupsJsonArray = templateJson.optJSONArray("groups") - val capabilitiesJsonArray = templateJson.optJSONArray("capabilities") - val context = templateJson.optString("context").takeIf { it.isNotEmpty() } - ?: Natives.KERNEL_SU_DOMAIN - val namespace = templateJson.optString("namespace").takeIf { it.isNotEmpty() } - ?: Natives.Profile.Namespace.INHERITED.name - - val rulesJsonArray = templateJson.optJSONArray("rules") - val templateInfo = TemplateViewModel.TemplateInfo( - id = templateJson.getString("id"), - name = getLocaleString(templateJson, "name"), - description = getLocaleString(templateJson, "description"), - author = templateJson.optString("author"), - local = templateJson.optBoolean("local"), - namespace = Natives.Profile.Namespace.valueOf( - namespace.uppercase() - ).ordinal, - uid = templateJson.optInt("uid", Natives.ROOT_UID), - gid = templateJson.optInt("gid", Natives.ROOT_GID), - groups = getEnumOrdinals(groupsJsonArray, Groups::class.java).map { it.gid }, - capabilities = getEnumOrdinals( - capabilitiesJsonArray, Capabilities::class.java - ).map { it.cap }, - context = context, - rules = rulesJsonArray?.mapCatching({ it }, { - Log.e(TAG, "ignore invalid rule: $it", it) - }).orEmpty() - ) - templateInfo - }.onFailure { - Log.e(TAG, "ignore invalid template: $it", it) - }.getOrNull() -} - -fun TemplateViewModel.TemplateInfo.toJSON(): JSONObject { - val template = this - return JSONObject().apply { - - put("id", template.id) - put("name", template.name.ifBlank { template.id }) - put("description", template.description.ifBlank { template.id }) - if (template.author.isNotEmpty()) { - put("author", template.author) - } - put("namespace", Natives.Profile.Namespace.entries[template.namespace].name) - put("uid", template.uid) - put("gid", template.gid) - - if (template.groups.isNotEmpty()) { - put("groups", JSONArray( - Groups.entries.filter { - template.groups.contains(it.gid) - }.map { - it.name - } - )) - } - - if (template.capabilities.isNotEmpty()) { - put("capabilities", JSONArray( - Capabilities.entries.filter { - template.capabilities.contains(it.cap) - }.map { - it.name - } - )) - } - - if (template.context.isNotEmpty()) { - put("context", template.context) - } - - if (template.rules.isNotEmpty()) { - put("rules", JSONArray(template.rules)) - } - } -} - -@Suppress("unused") -fun generateTemplates() { - val templateJson = JSONObject() - templateJson.put("id", "com.example") - templateJson.put("name", "Example") - templateJson.put("description", "This is an example template") - templateJson.put("local", true) - templateJson.put("namespace", Natives.Profile.Namespace.INHERITED.name) - templateJson.put("uid", 0) - templateJson.put("gid", 0) - - templateJson.put("groups", JSONArray().apply { put(Groups.INET.name) }) - templateJson.put("capabilities", JSONArray().apply { put(Capabilities.CAP_NET_RAW.name) }) - templateJson.put("context", "u:r:su:s0") - Log.i(TAG, "$templateJson") -} \ No newline at end of file diff --git a/manager/app/src/main/java/com/rifsxd/ksunext/ui/webui/MimeUtil.java b/manager/app/src/main/java/com/rifsxd/ksunext/ui/webui/MimeUtil.java deleted file mode 100644 index e02ac462..00000000 --- a/manager/app/src/main/java/com/rifsxd/ksunext/ui/webui/MimeUtil.java +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright 2023 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.rifsxd.ksunext.ui.webui; - -import java.net.URLConnection; - -class MimeUtil { - - public static String getMimeFromFileName(String fileName) { - if (fileName == null) { - return null; - } - - // Copying the logic and mapping that Chromium follows. - // First we check against the OS (this is a limited list by default) - // but app developers can extend this. - // We then check against a list of hardcoded mime types above if the - // OS didn't provide a result. - String mimeType = URLConnection.guessContentTypeFromName(fileName); - - if (mimeType != null) { - return mimeType; - } - - return guessHardcodedMime(fileName); - } - - // We should keep this map in sync with the lists under - // //net/base/mime_util.cc in Chromium. - // A bunch of the mime types don't really apply to Android land - // like word docs so feel free to filter out where necessary. - private static String guessHardcodedMime(String fileName) { - int finalFullStop = fileName.lastIndexOf('.'); - if (finalFullStop == -1) { - return null; - } - - final String extension = fileName.substring(finalFullStop + 1).toLowerCase(); - - return switch (extension) { - case "webm" -> "video/webm"; - case "mpeg", "mpg" -> "video/mpeg"; - case "mp3" -> "audio/mpeg"; - case "wasm" -> "application/wasm"; - case "xhtml", "xht", "xhtm" -> "application/xhtml+xml"; - case "flac" -> "audio/flac"; - case "ogg", "oga", "opus" -> "audio/ogg"; - case "wav" -> "audio/wav"; - case "m4a" -> "audio/x-m4a"; - case "gif" -> "image/gif"; - case "jpeg", "jpg", "jfif", "pjpeg", "pjp" -> "image/jpeg"; - case "png" -> "image/png"; - case "apng" -> "image/apng"; - case "svg", "svgz" -> "image/svg+xml"; - case "webp" -> "image/webp"; - case "mht", "mhtml" -> "multipart/related"; - case "css" -> "text/css"; - case "html", "htm", "shtml", "shtm", "ehtml" -> "text/html"; - case "js", "mjs" -> "application/javascript"; - case "xml" -> "text/xml"; - case "mp4", "m4v" -> "video/mp4"; - case "ogv", "ogm" -> "video/ogg"; - case "ico" -> "image/x-icon"; - case "woff" -> "application/font-woff"; - case "gz", "tgz" -> "application/gzip"; - case "json" -> "application/json"; - case "pdf" -> "application/pdf"; - case "zip" -> "application/zip"; - case "bmp" -> "image/bmp"; - case "tiff", "tif" -> "image/tiff"; - default -> null; - }; - } -} diff --git a/manager/app/src/main/java/com/rifsxd/ksunext/ui/webui/MonetColorsProvider.kt b/manager/app/src/main/java/com/rifsxd/ksunext/ui/webui/MonetColorsProvider.kt deleted file mode 100644 index f16c0ab9..00000000 --- a/manager/app/src/main/java/com/rifsxd/ksunext/ui/webui/MonetColorsProvider.kt +++ /dev/null @@ -1,156 +0,0 @@ -package com.rifsxd.ksunext.ui.webui - -import android.content.Context -import android.content.res.Configuration - -import androidx.compose.material3.dynamicDarkColorScheme -import androidx.compose.material3.dynamicLightColorScheme -import androidx.compose.material3.surfaceColorAtElevation -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.graphics.toArgb -import androidx.compose.ui.unit.dp - -import com.rifsxd.ksunext.ui.theme.AMOLED_BLACK - -/** - * @author rifsxd - * @date 2025/6/2. - */ -object MonetColorsProvider { - fun getColorsCss(context: Context): String { - - val isDark = (context.resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES - - val prefs = context.getSharedPreferences("settings", Context.MODE_PRIVATE) - val amoledMode = prefs.getBoolean("enable_amoled", false) - - val colorScheme = if (isDark) { - dynamicDarkColorScheme(context) - } else { - dynamicLightColorScheme(context) - } - - fun blend(c1: Color, c2: Color, ratio: Float): Color { - val inv = 1f - ratio - return Color( - red = c1.red * inv + c2.red * ratio, - green = c1.green * inv + c2.green * ratio, - blue = c1.blue * inv + c2.blue * ratio, - alpha = c1.alpha - ) - } - - val monetColors = if (isDark && amoledMode) { - mapOf( - "primary" to colorScheme.primary.toArgb().toHex(), - "onPrimary" to colorScheme.onPrimary.toArgb().toHex(), - "primaryContainer" to blend(colorScheme.primaryContainer, AMOLED_BLACK, 0.6f).toArgb().toHex(), - "onPrimaryContainer" to colorScheme.onPrimaryContainer.toArgb().toHex(), - "inversePrimary" to colorScheme.inversePrimary.toArgb().toHex(), - "secondary" to colorScheme.secondary.toArgb().toHex(), - "onSecondary" to colorScheme.onSecondary.toArgb().toHex(), - "secondaryContainer" to blend(colorScheme.secondaryContainer, AMOLED_BLACK, 0.6f).toArgb().toHex(), - "onSecondaryContainer" to colorScheme.onSecondaryContainer.toArgb().toHex(), - "tertiary" to colorScheme.tertiary.toArgb().toHex(), - "onTertiary" to colorScheme.onTertiary.toArgb().toHex(), - "tertiaryContainer" to blend(colorScheme.tertiaryContainer, AMOLED_BLACK, 0.6f).toArgb().toHex(), - "onTertiaryContainer" to colorScheme.onTertiaryContainer.toArgb().toHex(), - "background" to AMOLED_BLACK.toArgb().toHex(), - "onBackground" to colorScheme.onBackground.toArgb().toHex(), - "surface" to AMOLED_BLACK.toArgb().toHex(), - "tonalSurface" to blend(colorScheme.surfaceColorAtElevation(1.dp), AMOLED_BLACK, 0.6f).toArgb().toHex(), - "onSurface" to colorScheme.onSurface.toArgb().toHex(), - "surfaceVariant" to blend(colorScheme.surfaceVariant, AMOLED_BLACK, 0.6f).toArgb().toHex(), - "onSurfaceVariant" to colorScheme.onSurfaceVariant.toArgb().toHex(), - "surfaceTint" to colorScheme.surfaceTint.toArgb().toHex(), - "inverseSurface" to colorScheme.inverseSurface.toArgb().toHex(), - "inverseOnSurface" to colorScheme.inverseOnSurface.toArgb().toHex(), - "error" to colorScheme.error.toArgb().toHex(), - "onError" to colorScheme.onError.toArgb().toHex(), - "errorContainer" to colorScheme.errorContainer.toArgb().toHex(), - "onErrorContainer" to colorScheme.onErrorContainer.toArgb().toHex(), - "outline" to colorScheme.outline.toArgb().toHex(), - "outlineVariant" to colorScheme.outlineVariant.toArgb().toHex(), - "scrim" to colorScheme.scrim.toArgb().toHex(), - "surfaceBright" to blend(colorScheme.surfaceBright, AMOLED_BLACK, 0.6f).toArgb().toHex(), - "surfaceDim" to blend(colorScheme.surfaceDim, AMOLED_BLACK, 0.6f).toArgb().toHex(), - "surfaceContainer" to blend(colorScheme.surfaceContainer, AMOLED_BLACK, 0.6f).toArgb().toHex(), - "surfaceContainerHigh" to blend(colorScheme.surfaceContainerHigh, AMOLED_BLACK, 0.6f).toArgb().toHex(), - "surfaceContainerHighest" to blend(colorScheme.surfaceContainerHighest, AMOLED_BLACK, 0.6f).toArgb().toHex(), - "surfaceContainerLow" to blend(colorScheme.surfaceContainerLow, AMOLED_BLACK, 0.6f).toArgb().toHex(), - "surfaceContainerLowest" to blend(colorScheme.surfaceContainerLowest, AMOLED_BLACK, 0.6f).toArgb().toHex(), - "filledTonalButtonContentColor" to colorScheme.onPrimaryContainer.toArgb().toHex(), - "filledTonalButtonContainerColor" to blend(colorScheme.secondaryContainer, AMOLED_BLACK, 0.6f).toArgb().toHex(), - "filledTonalButtonDisabledContentColor" to colorScheme.onSurfaceVariant.toArgb().toHex(), - "filledTonalButtonDisabledContainerColor" to blend(colorScheme.surfaceVariant, AMOLED_BLACK, 0.6f).toArgb().toHex(), - "filledCardContentColor" to colorScheme.onPrimaryContainer.toArgb().toHex(), - "filledCardContainerColor" to blend(colorScheme.primaryContainer, AMOLED_BLACK, 0.6f).toArgb().toHex(), - "filledCardDisabledContentColor" to colorScheme.onSurfaceVariant.toArgb().toHex(), - "filledCardDisabledContainerColor" to blend(colorScheme.surfaceVariant, AMOLED_BLACK, 0.6f).toArgb().toHex() - ) - } else { - mapOf( - "primary" to colorScheme.primary.toArgb().toHex(), - "onPrimary" to colorScheme.onPrimary.toArgb().toHex(), - "primaryContainer" to colorScheme.primaryContainer.toArgb().toHex(), - "onPrimaryContainer" to colorScheme.onPrimaryContainer.toArgb().toHex(), - "inversePrimary" to colorScheme.inversePrimary.toArgb().toHex(), - "secondary" to colorScheme.secondary.toArgb().toHex(), - "onSecondary" to colorScheme.onSecondary.toArgb().toHex(), - "secondaryContainer" to colorScheme.secondaryContainer.toArgb().toHex(), - "onSecondaryContainer" to colorScheme.onSecondaryContainer.toArgb().toHex(), - "tertiary" to colorScheme.tertiary.toArgb().toHex(), - "onTertiary" to colorScheme.onTertiary.toArgb().toHex(), - "tertiaryContainer" to colorScheme.tertiaryContainer.toArgb().toHex(), - "onTertiaryContainer" to colorScheme.onTertiaryContainer.toArgb().toHex(), - "background" to colorScheme.background.toArgb().toHex(), - "onBackground" to colorScheme.onBackground.toArgb().toHex(), - "surface" to colorScheme.surface.toArgb().toHex(), - "tonalSurface" to colorScheme.surfaceColorAtElevation(1.dp).toArgb().toHex(), - "onSurface" to colorScheme.onSurface.toArgb().toHex(), - "surfaceVariant" to colorScheme.surfaceVariant.toArgb().toHex(), - "onSurfaceVariant" to colorScheme.onSurfaceVariant.toArgb().toHex(), - "surfaceTint" to colorScheme.surfaceTint.toArgb().toHex(), - "inverseSurface" to colorScheme.inverseSurface.toArgb().toHex(), - "inverseOnSurface" to colorScheme.inverseOnSurface.toArgb().toHex(), - "error" to colorScheme.error.toArgb().toHex(), - "onError" to colorScheme.onError.toArgb().toHex(), - "errorContainer" to colorScheme.errorContainer.toArgb().toHex(), - "onErrorContainer" to colorScheme.onErrorContainer.toArgb().toHex(), - "outline" to colorScheme.outline.toArgb().toHex(), - "outlineVariant" to colorScheme.outlineVariant.toArgb().toHex(), - "scrim" to colorScheme.scrim.toArgb().toHex(), - "surfaceBright" to colorScheme.surfaceBright.toArgb().toHex(), - "surfaceDim" to colorScheme.surfaceDim.toArgb().toHex(), - "surfaceContainer" to colorScheme.surfaceContainer.toArgb().toHex(), - "surfaceContainerHigh" to colorScheme.surfaceContainerHigh.toArgb().toHex(), - "surfaceContainerHighest" to colorScheme.surfaceContainerHighest.toArgb().toHex(), - "surfaceContainerLow" to colorScheme.surfaceContainerLow.toArgb().toHex(), - "surfaceContainerLowest" to colorScheme.surfaceContainerLowest.toArgb().toHex(), - "filledTonalButtonContentColor" to colorScheme.onPrimaryContainer.toArgb().toHex(), - "filledTonalButtonContainerColor" to colorScheme.secondaryContainer.toArgb().toHex(), - "filledTonalButtonDisabledContentColor" to colorScheme.onSurfaceVariant.toArgb().toHex(), - "filledTonalButtonDisabledContainerColor" to colorScheme.surfaceVariant.toArgb().toHex(), - "filledCardContentColor" to colorScheme.onPrimaryContainer.toArgb().toHex(), - "filledCardContainerColor" to colorScheme.primaryContainer.toArgb().toHex(), - "filledCardDisabledContentColor" to colorScheme.onSurfaceVariant.toArgb().toHex(), - "filledCardDisabledContainerColor" to colorScheme.surfaceVariant.toArgb().toHex() - ) - } - return monetColors.toCssVars() - } - - private fun Map.toCssVars(): String { - return buildString { - append(":root {\n") - for ((k, v) in this@toCssVars) { - append(" --$k: $v;\n") - } - append("}\n") - } - } - - private fun Int.toHex(): String { - return String.format("#%06X", 0xFFFFFF and this) - } -} \ No newline at end of file diff --git a/manager/app/src/main/java/com/rifsxd/ksunext/ui/webui/SuFilePathHandler.java b/manager/app/src/main/java/com/rifsxd/ksunext/ui/webui/SuFilePathHandler.java deleted file mode 100644 index 3db807f2..00000000 --- a/manager/app/src/main/java/com/rifsxd/ksunext/ui/webui/SuFilePathHandler.java +++ /dev/null @@ -1,209 +0,0 @@ -package com.rifsxd.ksunext.ui.webui; - -import android.content.Context; -import android.os.Build; -import android.util.Log; -import android.webkit.WebResourceResponse; - -import androidx.annotation.NonNull; -import androidx.annotation.WorkerThread; -import androidx.webkit.WebViewAssetLoader; - -import com.topjohnwu.superuser.Shell; -import com.topjohnwu.superuser.io.SuFile; -import com.topjohnwu.superuser.io.SuFileInputStream; - -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.io.ByteArrayInputStream; -import java.nio.charset.StandardCharsets; -import java.util.zip.GZIPInputStream; - -import com.rifsxd.ksunext.ui.webui.MonetColorsProvider; - -/** - * Handler class to open files from file system by root access - * For more information about android storage please refer to - * Android Developers - * Docs: Data and file storage overview. - *

    - * To avoid leaking user or app data to the web, make sure to choose {@code directory} - * carefully, and assume any file under this directory could be accessed by any web page subject - * to same-origin rules. - *

    - * A typical usage would be like: - *

    - * File publicDir = new File(context.getFilesDir(), "public");
    - * // Host "files/public/" in app's data directory under:
    - * // http://appassets.androidplatform.net/public/...
    - * WebViewAssetLoader assetLoader = new WebViewAssetLoader.Builder()
    - *          .addPathHandler("/public/", new InternalStoragePathHandler(context, publicDir))
    - *          .build();
    - * 
    - */ -public final class SuFilePathHandler implements WebViewAssetLoader.PathHandler { - private static final String TAG = "SuFilePathHandler"; - - /** - * Default value to be used as MIME type if guessing MIME type failed. - */ - public static final String DEFAULT_MIME_TYPE = "text/plain"; - - /** - * Forbidden subdirectories of {@link Context#getDataDir} that cannot be exposed by this - * handler. They are forbidden as they often contain sensitive information. - *

    - * Note: Any future addition to this list will be considered breaking changes to the API. - */ - private static final String[] FORBIDDEN_DATA_DIRS = - new String[] {"/data/data", "/data/system"}; - - @NonNull - private final File mDirectory; - - private final Shell mShell; - - /** - * Creates PathHandler for app's internal storage. - * The directory to be exposed must be inside either the application's internal data - * directory {@link Context#getDataDir} or cache directory {@link Context#getCacheDir}. - * External storage is not supported for security reasons, as other apps with - * {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE} may be able to modify the - * files. - *

    - * Exposing the entire data or cache directory is not permitted, to avoid accidentally - * exposing sensitive application files to the web. Certain existing subdirectories of - * {@link Context#getDataDir} are also not permitted as they are often sensitive. - * These files are ({@code "app_webview/"}, {@code "databases/"}, {@code "lib/"}, - * {@code "shared_prefs/"} and {@code "code_cache/"}). - *

    - * The application should typically use a dedicated subdirectory for the files it intends to - * expose and keep them separate from other files. - * - * @param context {@link Context} that is used to access app's internal storage. - * @param directory the absolute path of the exposed app internal storage directory from - * which files can be loaded. - * @throws IllegalArgumentException if the directory is not allowed. - */ - private final Context mContext; - - public SuFilePathHandler(@NonNull Context context, @NonNull File directory, Shell rootShell) { - try { - mContext = context; - mDirectory = new File(getCanonicalDirPath(directory)); - if (!isAllowedInternalStorageDir(context)) { - throw new IllegalArgumentException("The given directory \"" + directory - + "\" doesn't exist under an allowed app internal storage directory"); - } - mShell = rootShell; - } catch (IOException e) { - throw new IllegalArgumentException( - "Failed to resolve the canonical path for the given directory: " - + directory.getPath(), e); - } - } - - private boolean isAllowedInternalStorageDir(@NonNull Context context) throws IOException { - String dir = getCanonicalDirPath(mDirectory); - - for (String forbiddenPath : FORBIDDEN_DATA_DIRS) { - if (dir.startsWith(forbiddenPath)) { - return false; - } - } - return true; - } - - /** - * Opens the requested file from the exposed data directory. - *

    - * The matched prefix path used shouldn't be a prefix of a real web path. Thus, if the - * requested file cannot be found or is outside the mounted directory a - * {@link WebResourceResponse} object with a {@code null} {@link InputStream} will be - * returned instead of {@code null}. This saves the time of falling back to network and - * trying to resolve a path that doesn't exist. A {@link WebResourceResponse} with - * {@code null} {@link InputStream} will be received as an HTTP response with status code - * {@code 404} and no body. - *

    - * The MIME type for the file will be determined from the file's extension using - * {@link java.net.URLConnection#guessContentTypeFromName}. Developers should ensure that - * files are named using standard file extensions. If the file does not have a - * recognised extension, {@code "text/plain"} will be used by default. - * - * @param path the suffix path to be handled. - * @return {@link WebResourceResponse} for the requested file. - */ - @Override - @WorkerThread - @NonNull - public WebResourceResponse handle(@NonNull String path) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { - if ("internal/colors.css".equals(path)) { - String css = MonetColorsProvider.INSTANCE.getColorsCss(mContext); - return new WebResourceResponse( - "text/css", - "utf-8", - new ByteArrayInputStream(css.getBytes(StandardCharsets.UTF_8)) - ); - } - } - try { - File file = getCanonicalFileIfChild(mDirectory, path); - if (file != null) { - InputStream is = openFile(file, mShell); - String mimeType = guessMimeType(path); - return new WebResourceResponse(mimeType, null, is); - } else { - Log.e(TAG, String.format( - "The requested file: %s is outside the mounted directory: %s", path, - mDirectory)); - } - } catch (IOException e) { - Log.e(TAG, "Error opening the requested path: " + path, e); - } - return new WebResourceResponse(null, null, null); - } - - public static String getCanonicalDirPath(@NonNull File file) throws IOException { - String canonicalPath = file.getCanonicalPath(); - if (!canonicalPath.endsWith("/")) canonicalPath += "/"; - return canonicalPath; - } - - public static File getCanonicalFileIfChild(@NonNull File parent, @NonNull String child) - throws IOException { - String parentCanonicalPath = getCanonicalDirPath(parent); - String childCanonicalPath = new File(parent, child).getCanonicalPath(); - if (childCanonicalPath.startsWith(parentCanonicalPath)) { - return new File(childCanonicalPath); - } - return null; - } - - @NonNull - private static InputStream handleSvgzStream(@NonNull String path, - @NonNull InputStream stream) throws IOException { - return path.endsWith(".svgz") ? new GZIPInputStream(stream) : stream; - } - - public static InputStream openFile(@NonNull File file, @NonNull Shell shell) throws IOException { - SuFile suFile = new SuFile(file.getAbsolutePath()); - suFile.setShell(shell); - InputStream fis = SuFileInputStream.open(suFile); - return handleSvgzStream(file.getPath(), fis); - } - - /** - * Use {@link MimeUtil#getMimeFromFileName} to guess MIME type or return the - * {@link #DEFAULT_MIME_TYPE} if it can't guess. - * - * @param filePath path of the file to guess its MIME type. - * @return MIME type guessed from file extension or {@link #DEFAULT_MIME_TYPE}. - */ - @NonNull - public static String guessMimeType(@NonNull String filePath) { - String mimeType = MimeUtil.getMimeFromFileName(filePath); - return mimeType == null ? DEFAULT_MIME_TYPE : mimeType; - } -} diff --git a/manager/app/src/main/java/com/rifsxd/ksunext/ui/webui/WebUIActivity.kt b/manager/app/src/main/java/com/rifsxd/ksunext/ui/webui/WebUIActivity.kt deleted file mode 100644 index f0228202..00000000 --- a/manager/app/src/main/java/com/rifsxd/ksunext/ui/webui/WebUIActivity.kt +++ /dev/null @@ -1,122 +0,0 @@ -package com.rifsxd.ksunext.ui.webui - -import android.annotation.SuppressLint -import android.app.ActivityManager -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 com.rifsxd.ksunext.ui.util.createRootShell -import java.io.File - -@SuppressLint("SetJavaScriptEnabled") -class WebUIActivity : ComponentActivity() { - private lateinit var webviewInterface: WebViewInterface - - private var rootShell: Shell? = null - - fun erudaConsole(context: android.content.Context): String { - return context.assets.open("eruda.min.js").bufferedReader().use { it.readText() } - } - - override fun onCreate(savedInstanceState: Bundle?) { - - // Enable edge to edge - enableEdgeToEdge() - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { - window.isNavigationBarContrastEnforced = false - } - - super.onCreate(savedInstanceState) - - val moduleId = intent.getStringExtra("id")!! - val name = intent.getStringExtra("name")!! - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) { - @Suppress("DEPRECATION") - setTaskDescription(ActivityManager.TaskDescription("WebUI-Next | $name")) - } else { - val taskDescription = ActivityManager.TaskDescription.Builder().setLabel("WebUI-Next | $name").build() - setTaskDescription(taskDescription) - } - - val prefs = getSharedPreferences("settings", MODE_PRIVATE) - val developerOptionsEnabled = prefs.getBoolean("enable_developer_options", false) - val enableWebDebugging = prefs.getBoolean("enable_web_debugging", false) - - WebView.setWebContentsDebuggingEnabled(developerOptionsEnabled && enableWebDebugging) - - 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") - .addPathHandler( - "/", - SuFilePathHandler(this, webRoot, rootShell) - ) - .build() - - val webViewClient = object : WebViewClient() { - override fun shouldInterceptRequest( - view: WebView, - request: WebResourceRequest - ): WebResourceResponse? { - return webViewAssetLoader.shouldInterceptRequest(request.url) - } - } - - 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, moduleDir) - addJavascriptInterface(webviewInterface, "ksu") - setWebViewClient(object : WebViewClient() { - override fun shouldInterceptRequest( - view: WebView, - request: WebResourceRequest - ): WebResourceResponse? { - return webViewAssetLoader.shouldInterceptRequest(request.url) - } - override fun onPageFinished(view: WebView?, url: String?) { - super.onPageFinished(view, url) - if (developerOptionsEnabled && enableWebDebugging) { - view?.evaluateJavascript( - erudaConsole(this@WebUIActivity), - null - ) - view?.evaluateJavascript("eruda.init();", null) - } - } - }) - loadUrl("https://mui.kernelsu.org/index.html") - } - - setContentView(webView) - } - - override fun onDestroy() { - super.onDestroy() - runCatching { rootShell?.close() } - } -} \ No newline at end of file diff --git a/manager/app/src/main/java/com/rifsxd/ksunext/ui/webui/WebViewInterface.kt b/manager/app/src/main/java/com/rifsxd/ksunext/ui/webui/WebViewInterface.kt deleted file mode 100644 index 9eb41cb7..00000000 --- a/manager/app/src/main/java/com/rifsxd/ksunext/ui/webui/WebViewInterface.kt +++ /dev/null @@ -1,360 +0,0 @@ -package com.rifsxd.ksunext.ui.webui - -import android.graphics.Bitmap -import android.graphics.Canvas -import android.graphics.drawable.BitmapDrawable -import android.graphics.drawable.Drawable -import android.util.Base64 -import android.app.Activity -import android.content.Context -import android.content.pm.ApplicationInfo -import android.os.Handler -import android.os.Looper -import android.text.TextUtils -import android.view.Window -import android.webkit.JavascriptInterface -import android.webkit.WebView -import android.widget.Toast -import androidx.core.view.WindowInsetsCompat -import androidx.core.view.WindowInsetsControllerCompat -import com.topjohnwu.superuser.CallbackList -import com.topjohnwu.superuser.ShellUtils -import com.topjohnwu.superuser.internal.UiThreadHandler -import com.rifsxd.ksunext.ui.util.createRootShell -import com.rifsxd.ksunext.ui.util.listModules -import com.rifsxd.ksunext.ui.util.withNewRootShell -import org.json.JSONArray -import org.json.JSONObject -import java.io.File -import java.util.concurrent.CompletableFuture - -class WebViewInterface( - val context: Context, - private val webView: WebView, - private val modDir: String -) { - - @JavascriptInterface - fun exec(cmd: String): String { - return withNewRootShell(true) { ShellUtils.fastCmd(this, cmd) } - } - - @JavascriptInterface - fun exec(cmd: String, callbackFunc: String) { - exec(cmd, null, callbackFunc) - } - - private fun processOptions(sb: StringBuilder, options: String?) { - val opts = if (options == null) JSONObject() else { - JSONObject(options) - } - - val cwd = opts.optString("cwd") - if (!TextUtils.isEmpty(cwd)) { - sb.append("cd ${cwd};") - } - - opts.optJSONObject("env")?.let { env -> - env.keys().forEach { key -> - sb.append("export ${key}=${env.getString(key)};") - } - } - } - - @JavascriptInterface - fun exec( - cmd: String, - options: String?, - callbackFunc: String - ) { - val finalCommand = StringBuilder() - processOptions(finalCommand, options) - finalCommand.append(cmd) - - val result = withNewRootShell(true) { - newJob().add(finalCommand.toString()).to(ArrayList(), ArrayList()).exec() - } - val stdout = result.out.joinToString(separator = "\n") - val stderr = result.err.joinToString(separator = "\n") - - val jsCode = - "javascript: (function() { try { ${callbackFunc}(${result.code}, ${ - JSONObject.quote( - stdout - ) - }, ${JSONObject.quote(stderr)}); } catch(e) { console.error(e); } })();" - webView.post { - webView.loadUrl(jsCode) - } - } - - @JavascriptInterface - fun spawn(command: String, args: String, options: String?, callbackFunc: String) { - val finalCommand = StringBuilder() - - processOptions(finalCommand, options) - - if (!TextUtils.isEmpty(args)) { - finalCommand.append(command).append(" ") - JSONArray(args).let { argsArray -> - for (i in 0 until argsArray.length()) { - finalCommand.append(argsArray.getString(i)) - finalCommand.append(" ") - } - } - } else { - finalCommand.append(command) - } - - val shell = createRootShell(true) - - val emitData = fun(name: String, data: String) { - val jsCode = - "javascript: (function() { try { ${callbackFunc}.${name}.emit('data', ${ - JSONObject.quote( - data - ) - }); } catch(e) { console.error('emitData', e); } })();" - webView.post { - webView.loadUrl(jsCode) - } - } - - val stdout = object : CallbackList(UiThreadHandler::runAndWait) { - override fun onAddElement(s: String) { - emitData("stdout", s) - } - } - - val stderr = object : CallbackList(UiThreadHandler::runAndWait) { - override fun onAddElement(s: String) { - emitData("stderr", s) - } - } - - val future = shell.newJob().add(finalCommand.toString()).to(stdout, stderr).enqueue() - val completableFuture = CompletableFuture.supplyAsync { - future.get() - } - - completableFuture.thenAccept { result -> - val emitExitCode = - "javascript: (function() { try { ${callbackFunc}.emit('exit', ${result.code}); } catch(e) { console.error(`emitExit error: \${e}`); } })();" - webView.post { - webView.loadUrl(emitExitCode) - } - - if (result.code != 0) { - val emitErrCode = - "javascript: (function() { try { var err = new Error(); err.exitCode = ${result.code}; err.message = ${ - JSONObject.quote( - result.err.joinToString( - "\n" - ) - ) - };${callbackFunc}.emit('error', err); } catch(e) { console.error('emitErr', e); } })();" - webView.post { - webView.loadUrl(emitErrCode) - } - } - }.whenComplete { _, _ -> - runCatching { shell.close() } - } - } - - @JavascriptInterface - fun toast(msg: String) { - webView.post { - Toast.makeText(context, msg, Toast.LENGTH_SHORT).show() - } - } - - @JavascriptInterface - fun fullScreen(enable: Boolean) { - if (context is Activity) { - Handler(Looper.getMainLooper()).post { - if (enable) { - hideSystemUI(context.window) - } else { - showSystemUI(context.window) - } - } - } - } - - @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() - } - - @JavascriptInterface - fun listSystemPackages(): String { - val pm = context.packageManager - val packages = pm.getInstalledPackages(0) - val packageNames = packages - .mapNotNull { pkg -> - val appInfo = pkg.applicationInfo - if (appInfo != null && (appInfo.flags and ApplicationInfo.FLAG_SYSTEM) != 0) { - pkg.packageName - } else null - } - .sorted() - val jsonArray = JSONArray() - for (pkgName in packageNames) { - jsonArray.put(pkgName) - } - return jsonArray.toString() - } - - @JavascriptInterface - fun listUserPackages(): String { - val pm = context.packageManager - val packages = pm.getInstalledPackages(0) - val packageNames = packages - .mapNotNull { pkg -> - val appInfo = pkg.applicationInfo - if (appInfo != null && (appInfo.flags and ApplicationInfo.FLAG_SYSTEM) == 0) { - pkg.packageName - } else null - } - .sorted() - val jsonArray = JSONArray() - for (pkgName in packageNames) { - jsonArray.put(pkgName) - } - return jsonArray.toString() - } - - @JavascriptInterface - fun listAllPackages(): String { - val pm = context.packageManager - val packages = pm.getInstalledPackages(0) - val packageNames = packages.map { it.packageName }.sorted() - val jsonArray = JSONArray() - for (pkgName in packageNames) { - jsonArray.put(pkgName) - } - return jsonArray.toString() - } - - @JavascriptInterface - fun getPackagesInfo(packageNamesJson: String): String { - val pm = context.packageManager - val packageNames = JSONArray(packageNamesJson) - val jsonArray = JSONArray() - for (i in 0 until packageNames.length()) { - val pkgName = packageNames.getString(i) - try { - val pkg = pm.getPackageInfo(pkgName, 0) - val appInfo = pkg.applicationInfo - val obj = JSONObject() - obj.put("packageName", pkg.packageName) - obj.put("versionName", pkg.versionName ?: "") - obj.put("versionCode", pkg.longVersionCode) - obj.put("appLabel", if (appInfo != null) pm.getApplicationLabel(appInfo).toString() else "") - obj.put("isSystem", appInfo != null && (appInfo.flags and ApplicationInfo.FLAG_SYSTEM) != 0) - obj.put("uid", appInfo?.uid ?: JSONObject.NULL) - jsonArray.put(obj) - } catch (e: Exception) { - val obj = JSONObject() - obj.put("packageName", pkgName) - obj.put("error", "Package not found or inaccessible") - jsonArray.put(obj) - } - } - return jsonArray.toString() - } - - private val packageIconCache = HashMap() - - @JavascriptInterface - fun cacheAllPackageIcons(size: Int) { - val pm = context.packageManager - val packages = pm.getInstalledPackages(0) - val outputStream = java.io.ByteArrayOutputStream() - for (pkg in packages) { - val pkgName = pkg.packageName - if (packageIconCache.containsKey(pkgName)) continue - try { - val appInfo = pm.getApplicationInfo(pkgName, 0) - val drawable = pm.getApplicationIcon(appInfo) - val bitmap = drawableToBitmap(drawable, size) - outputStream.reset() - bitmap.compress(Bitmap.CompressFormat.PNG, 100, outputStream) - val byteArray = outputStream.toByteArray() - val iconBase64 = "data:image/png;base64," + Base64.encodeToString(byteArray, Base64.NO_WRAP) - packageIconCache[pkgName] = iconBase64 - } catch (_: Exception) { - packageIconCache[pkgName] = "" - } - } - } - - @JavascriptInterface - fun getPackagesIcons(packageNamesJson: String, size: Int): String { - val pm = context.packageManager - val packageNames = JSONArray(packageNamesJson) - val jsonArray = JSONArray() - val outputStream = java.io.ByteArrayOutputStream() - for (i in 0 until packageNames.length()) { - val pkgName = packageNames.getString(i) - val obj = JSONObject() - obj.put("packageName", pkgName) - var iconBase64 = packageIconCache[pkgName] - if (iconBase64 == null) { - try { - val appInfo = pm.getApplicationInfo(pkgName, 0) - val drawable = pm.getApplicationIcon(appInfo) - val bitmap = drawableToBitmap(drawable, size) - outputStream.reset() - bitmap.compress(Bitmap.CompressFormat.PNG, 100, outputStream) - val byteArray = outputStream.toByteArray() - iconBase64 = "data:image/png;base64," + Base64.encodeToString(byteArray, Base64.NO_WRAP) - } catch (_: Exception) { - iconBase64 = "" - } - packageIconCache[pkgName] = iconBase64 - } - obj.put("icon", iconBase64) - jsonArray.put(obj) - } - return jsonArray.toString() - } -} - -fun drawableToBitmap(drawable: Drawable, size: Int): Bitmap { - if (drawable is BitmapDrawable && drawable.bitmap.width == size && drawable.bitmap.height == size) { - return drawable.bitmap - } - val bitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888) - val canvas = Canvas(bitmap) - drawable.setBounds(0, 0, size, size) - drawable.draw(canvas) - return bitmap -} - -fun hideSystemUI(window: Window) = - WindowInsetsControllerCompat(window, window.decorView).let { controller -> - controller.hide(WindowInsetsCompat.Type.systemBars()) - controller.systemBarsBehavior = WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE - } - -fun showSystemUI(window: Window) = - WindowInsetsControllerCompat(window, window.decorView).show(WindowInsetsCompat.Type.systemBars()) \ No newline at end of file diff --git a/manager/app/src/main/jniLibs/.gitignore b/manager/app/src/main/jniLibs/.gitignore deleted file mode 100644 index 27356efb..00000000 --- a/manager/app/src/main/jniLibs/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -libksud_overlayfs.so -libksud_magic.so -libsusfsd.so \ No newline at end of file diff --git a/manager/app/src/main/jniLibs/arm64-v8a/libmagiskboot.so b/manager/app/src/main/jniLibs/arm64-v8a/libmagiskboot.so deleted file mode 100644 index ce7478d6..00000000 Binary files a/manager/app/src/main/jniLibs/arm64-v8a/libmagiskboot.so and /dev/null differ diff --git a/manager/app/src/main/jniLibs/armeabi-v7a/libmagiskboot.so b/manager/app/src/main/jniLibs/armeabi-v7a/libmagiskboot.so deleted file mode 100644 index 157133c0..00000000 Binary files a/manager/app/src/main/jniLibs/armeabi-v7a/libmagiskboot.so and /dev/null differ diff --git a/manager/app/src/main/jniLibs/x86_64/libmagiskboot.so b/manager/app/src/main/jniLibs/x86_64/libmagiskboot.so deleted file mode 100644 index 095ffe57..00000000 Binary files a/manager/app/src/main/jniLibs/x86_64/libmagiskboot.so and /dev/null differ diff --git a/manager/app/src/main/res/drawable/ic_github.xml b/manager/app/src/main/res/drawable/ic_github.xml deleted file mode 100644 index 4d321852..00000000 --- a/manager/app/src/main/res/drawable/ic_github.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - \ No newline at end of file diff --git a/manager/app/src/main/res/drawable/ic_ksu_next.xml b/manager/app/src/main/res/drawable/ic_ksu_next.xml deleted file mode 100644 index 5a3dd0a9..00000000 --- a/manager/app/src/main/res/drawable/ic_ksu_next.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - diff --git a/manager/app/src/main/res/drawable/ic_linux.xml b/manager/app/src/main/res/drawable/ic_linux.xml deleted file mode 100644 index 046bc6ef..00000000 --- a/manager/app/src/main/res/drawable/ic_linux.xml +++ /dev/null @@ -1,38 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/manager/app/src/main/res/drawable/ic_sus.xml b/manager/app/src/main/res/drawable/ic_sus.xml deleted file mode 100644 index ea794670..00000000 --- a/manager/app/src/main/res/drawable/ic_sus.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - \ No newline at end of file diff --git a/manager/app/src/main/res/drawable/ic_telegram.xml b/manager/app/src/main/res/drawable/ic_telegram.xml deleted file mode 100644 index f9c308a4..00000000 --- a/manager/app/src/main/res/drawable/ic_telegram.xml +++ /dev/null @@ -1,9 +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 deleted file mode 100644 index 71547337..00000000 --- a/manager/app/src/main/res/mipmap-anydpi/ic_launcher.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/manager/app/src/main/res/mipmap-hdpi/ic_launcher.png b/manager/app/src/main/res/mipmap-hdpi/ic_launcher.png deleted file mode 100644 index 20ebf833..00000000 Binary files a/manager/app/src/main/res/mipmap-hdpi/ic_launcher.png and /dev/null differ diff --git a/manager/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png b/manager/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png deleted file mode 100644 index 56fd7d1b..00000000 Binary files a/manager/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png and /dev/null differ diff --git a/manager/app/src/main/res/mipmap-hdpi/ic_launcher_monochrome.png b/manager/app/src/main/res/mipmap-hdpi/ic_launcher_monochrome.png deleted file mode 100644 index 56fd7d1b..00000000 Binary files a/manager/app/src/main/res/mipmap-hdpi/ic_launcher_monochrome.png and /dev/null differ diff --git a/manager/app/src/main/res/mipmap-mdpi/ic_launcher.png b/manager/app/src/main/res/mipmap-mdpi/ic_launcher.png deleted file mode 100644 index 365a84ce..00000000 Binary files a/manager/app/src/main/res/mipmap-mdpi/ic_launcher.png and /dev/null differ diff --git a/manager/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png b/manager/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png deleted file mode 100644 index 9ab2d5f0..00000000 Binary files a/manager/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png and /dev/null differ diff --git a/manager/app/src/main/res/mipmap-mdpi/ic_launcher_monochrome.png b/manager/app/src/main/res/mipmap-mdpi/ic_launcher_monochrome.png deleted file mode 100644 index 9ab2d5f0..00000000 Binary files a/manager/app/src/main/res/mipmap-mdpi/ic_launcher_monochrome.png and /dev/null differ diff --git a/manager/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/manager/app/src/main/res/mipmap-xhdpi/ic_launcher.png deleted file mode 100644 index d33ab295..00000000 Binary files a/manager/app/src/main/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ diff --git a/manager/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png b/manager/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png deleted file mode 100644 index a1598b53..00000000 Binary files a/manager/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png and /dev/null differ diff --git a/manager/app/src/main/res/mipmap-xhdpi/ic_launcher_monochrome.png b/manager/app/src/main/res/mipmap-xhdpi/ic_launcher_monochrome.png deleted file mode 100644 index a1598b53..00000000 Binary files a/manager/app/src/main/res/mipmap-xhdpi/ic_launcher_monochrome.png and /dev/null differ diff --git a/manager/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/manager/app/src/main/res/mipmap-xxhdpi/ic_launcher.png deleted file mode 100644 index 64f994cf..00000000 Binary files a/manager/app/src/main/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ diff --git a/manager/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png b/manager/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png deleted file mode 100644 index 2be3aaba..00000000 Binary files a/manager/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png and /dev/null differ diff --git a/manager/app/src/main/res/mipmap-xxhdpi/ic_launcher_monochrome.png b/manager/app/src/main/res/mipmap-xxhdpi/ic_launcher_monochrome.png deleted file mode 100644 index 2be3aaba..00000000 Binary files a/manager/app/src/main/res/mipmap-xxhdpi/ic_launcher_monochrome.png and /dev/null differ diff --git a/manager/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/manager/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png deleted file mode 100644 index 30efeeb6..00000000 Binary files a/manager/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ diff --git a/manager/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png b/manager/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png deleted file mode 100644 index debca7a1..00000000 Binary files a/manager/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png and /dev/null differ diff --git a/manager/app/src/main/res/mipmap-xxxhdpi/ic_launcher_monochrome.png b/manager/app/src/main/res/mipmap-xxxhdpi/ic_launcher_monochrome.png deleted file mode 100644 index debca7a1..00000000 Binary files a/manager/app/src/main/res/mipmap-xxxhdpi/ic_launcher_monochrome.png and /dev/null differ diff --git a/manager/app/src/main/res/resources.properties b/manager/app/src/main/res/resources.properties deleted file mode 100644 index d5a3ddc9..00000000 --- a/manager/app/src/main/res/resources.properties +++ /dev/null @@ -1 +0,0 @@ -unqualifiedResLocale=en-US \ No newline at end of file diff --git a/manager/app/src/main/res/values-ar-rSA/strings.xml b/manager/app/src/main/res/values-ar-rSA/strings.xml deleted file mode 100644 index 30102b9c..00000000 --- a/manager/app/src/main/res/values-ar-rSA/strings.xml +++ /dev/null @@ -1,236 +0,0 @@ - - - هل لديك مشكلة؟ - هل واجهت خطأ أو لديك تعليقات؟ - أبلغ عن ذلك في أقرب وقت ممكن! - ابلغ عنه في github - التواصل عبر تليجرام - https://github.com/KernelSU-Next/KernelSU-Next/issues - https://t.me/ksunext - تأكيد - KernelSU Next - الصفحة الرئيسية - غير مثبت - إضغط للتثبيت - وضع LKM قد تم إهماله! - قم بتثبيت نواة GKI أو دمج KernelSU Next في جهازك. - جارِ العمل - الإصدار: %d - تحديثات: %d - لم يتم العثور على توقيع KernelSU Next v2 في النواة ! [ !KSU_NEXT <unk> != الحجم/الهاش ] - اطلب من مطور kernel الخاص بك دمج KernelSU Next! - اصدار النواة - وضع الخطاف - تمكين - تعطيل - مفعّل - معطَّل - مدعوم - SuSFS: %s - إصدار SuSFS - SuS SU - إصدار الـ Android - إصدار المدير - ABI - ‏حالة SELinux - معطَّل - مفروض - مسموح - غير معروف - مستخدم خارق - فشل في تمكين الإضافة: %s - فشل في تمكين الإضافة: %s - لا توجد وحدة مثبتة - وحدة - سيتم تثبيت الوحدات التالية: %1$s - الترتيب (A → Z) - الترتيب (A → Z) - الترتيب (منخفض → عالي) - الترتيب (عالي → منخفض) - إلغاء التثبيت - استعادة - تثبيت - تثبيت - إعادة التشغيل - غير مثبت - الإعدادت - إعادة تشغيل سريع - الاقلاع في وضع الاسترداد - الاقلاع في وضع البوتلودر - الاقلاع في وضع التحميل - الاقلاع في وضع EDL - عن التطبيق - هل انت متأكد بأنك تريد إلغاء تثبيت الوحدة %s؟ - تم إلغاء تثبيت %s - فشل إلغاء تثبيت: %s - هل انت متأكد من أنك تريد استعادة وحدة %s ؟ - تم إستعادة %s - فشل في إستعادة: %s - الإصدار - المطور - معرف الوحدة - رقم الإصدار - UpdateJson - فارغ - تفعيل خيارات المطور - إظهار الإعدادات المخفية و معلومات التصحيح الخاصة بالمطورين فقط. - الوحدات غير متوفرة لان ال OverlayFS معطل بواسطة ال Kernel! - تحديث - إظهار تطبيقات النظام - إخفاء تطبيقات النظام - تصدير السجلات - الوضع الآمن - أعد التشغيل لتطبيق التغييرات - الوحدات غير متاحة بسبب تعارضها مع Magisk! - تركيب النظام - تركيب النظام - OverlayFS - غير متاح - استخدام OverlayFS - التبديل بين استخدام OverlayFS و Magic Mount لتركيب نظام KernelSU Next. - مطلوب إعادة التشغيل - ستصبح التغييرات سارية المفعول بعد إعادة تشغيل النظام. هل تريد إعادة التشغيل الآن؟ - استعادة الوحدة - استعادة الوحدات من النسخة الاحتياطية الأخيرة. - النسخ الاحتياطي والاستعادة - نسخ احتياطي للوحدات - استعادة القائمة البيضاء - استعادة الوحدات النمطية من النسخة الاحتياطية الأخيرة. - نسخ احتياطي للقائمة البيضاء - النسخ الاحتياطي للقائمة البيضاء المُعدة حاليا. - تحذير - هذه الميزة لا تزال في المرحلة التجريبية و قيد التطوير. الرجاء التأكد من النسخ الاحتياطي لوحداتك قبل المتابعة. استخدم هذه الميزة فقط إذا كنت تفهم المخاطر المحتملة. المتابعة بحذر. - متابعة - إلغاء - لاحقاً - تصحيح LKM يعتمد على مكونات مغلقة المصدر. هل تريد الاستمرار؟ - 🔥 الاصدار التالي - https://github.com/KernelSU-Next/KernelSU-Next - الفرع التجريبي التالي. تحقق من ذلك على GitHub! - ⚠️ تحذير التطوير التجريبي! - 127.0.0.1 - KernelSU Next هي نسخة غير رسمية تخضع دائماً للتطوير التجريبي النشط. وهي تقدم كما هي، بدون ضمانات للاستقرار، أو الأداء، أو الموثوقية. - • استخدمه على مسؤوليتك الخاصة: قد تحدث أعطال، أو سلوك غير متوقع، أو مشاكل في النظام. - • لا ضمان: المطورون غير مسؤولين عن أي خسارة في البيانات أو تلف للنظام، أو أي عواقب أخرى ناجمة عن استخدامها. - • لأغراض الاختبار فقط: موجهة للمستعملين الذين يفهمون المخاطر ويواجهون مشاكل مريحة في استكشاف الأخطاء وإصلاحها. - عرض الكود المصدر في %1$s - ملف تعريف التطبيق - الافتراضي - قالب - مخصص - اسم ملف التعريف - تحميل ال namespace - متوارث - عام - فردي - مجموعات - المُؤَهّلات - سياق SELinux - الغاء تحميل الوحدات - فشل تحديث ملف تعريف التطبيق لـ %s - الإصدار KernelSU Next الحالي %1$d منخفض جدا لكي يعمل المدير بشكل صحيح. الرجاء الترقية إلى الإصدار %2$d أو أعلى! - الغاء تحميل الإضافات - القيمة الافتراضية العامة لـ\"إلغاء تحميل الإضافات\" في ملفات تعريف التطبيقات. إذا تم تمكينه، إزالة جميع تعديلات الإضافات على النظام للتطبيقات التي لا تحتوي على مجموعة ملف تعريف. - أخفاء خطاف kprobes - يعطل هذا الخيار خطاف kprobes الذي أنشأه المدير وبدلا من ذلك، يقوم بتفعيل الخطاف المدمج، تنفيذ نفس الوظيفة التي سيتم تطبيقها على كيرنيل غير GKI ، الذي لا يدعم kprobes. - تمكين هذا الخيار سيسمح KernelSU Next لاستعادة أي ملفات معدلة بواسطة وحدات هذا التطبيق. - النطاق - القواعد - تحديث - تحديث - محدث - جاري تنزيل الوحدة: %s - بدء التنزيل: %s - الإصدار الجديد: %s متاح ، انقر للتحديث. - تشغيل - إغلاق - ايقاف اجباري - إعادة تشغيل - وضع AMOLED - تمكين سمة سوداء نقية مفيدة لشاشات AMOLED لتقليل إجهاد العين وحفظ البطارية. - مطلوب إعادة التشغيل - يحتاج التطبيق إلى إعادة تشغيل حتى يصبح هذا التغيير ساري المفعول. - فشل تحديث قواعد SELinux لـ %s - تعذر منح المستخدم الخارق الوصول إلى %s - سِجل التغييرات - قالب الملف الشخصي للتطبيق - إدارة قالب محلي وعلى الإنترنت لملف التعريف - إنشاء قالب - تحرير القالب - الرقم التعريفي - معرف القالب غير صالح - الاسم - الوصف - حفظ - حذف - عرض القالب - للقراءة فقط - معرف القالب موجود بالفعل! - إستيراد/تصدير - استيراد من الحافظة - تصدير إلى الحافظة - لا يمكن العثور على القالب المحلي للتصدير! - تم الاستيراد بنجاح - مزامنة القوالب عبر الإنترنت - فشل في حفظ القالب - الحافظة فارغة! - فشل في جلب سجل التغيير: %s - التحقق من وجود تحديثات - التحقق تلقائيًا من وجود تحديثات عند فتح التطبيق - فشل في منح صلاحية الجذر! - إجراء - WebUI - فتح - تمكين تصحيح أخطاء WebView - يمكن استخدامه لتصحيح أخطاء WebUI، يرجى تمكينه فقط عند الحاجة. - تثبيت مباشر (مستحسن) - اختر ملفا - تثبيت إلى خانة غير نشطة (بعد OTA) - سيتم **إجبار** جهازك على الاقلاع إلى الفتحة غير النشطة الحالية بعد إعادة التشغيل! -\nاستخدم هذا الخيار فقط بعد انتهاء التحديث. -\nأستمرار؟ - التالي - يوصي بصورة القسم %1$s - تحديد KMI - تقليص ال sparse image - غيّر حجم ال sparse image التي توجد بها الوحدة إلى حجمها الحقيقي. يُرجى ملاحظة أن هذا قد يُسبب خللاً في عمل الوحدة، لذا يُرجى استخدامها فقط عند الضرورة (مثل النسخ الاحتياطي). - إلغاء التثبيت - إلغاء تثبيت مؤقت - إلغاء التثبيت نهائياً - استعادة الصورة الأصلية - الغاء تثبيت KernelSU Next مؤقتا، استعادة الحالة الاصلية بعد اعادة التشغيل. - إلغاء تثبيت KernelSU Next (الرووت و جميع الوحدات) بشكلٍ كامل و نهائياً. - استعادة صورة المصنع الاصلية (لو النسخة الاحتياطية موجودة)، تستخدم عادةً قبل OTA; ان كنت تريد الغاء تثبيت KernelSU Next، الرجاء استخدام \"الغاء التثبيت نهائياً\". - جاري الحرق. - تم الحرق بنجاح - فشل التركيب - تحديد LKM: %s - حفظ السجلات - تم حفظ السجلات - مشاركة السجلات - تعطيل وضع التكامل مع المستخدم الخارق - تعطيل قدرة اي تطبيق على الحصول على إذن الرووت عن طريق الامر su (عمليات الرووت الحالية لن تتاثر). - اللغة - افتراضيات النظام - استخدام واجهة المستخدم القديمة - التبديل الى شكل واجهة المستخدم السابقة. - تمكين اللافتات - اظهار خلفيات اللافتات للوحدات. - إستخدام WebUI X - إستخدام WebUI X بدلاً من WebUI، التي تدعم المزيد من ال APIs. - حقن Eruda في WebUI X - حقن وحدة التصحيح في WebUI X لجعل تصحيح الأخطاء أسهل. يتطلب تصحيح أخطاء الويب ن يكون مفعل. - تخصيص - المطور - تعطيل العرض - Zygisk مطلوب - حقن Zygisk - مستخدمون خارقون - مستخدم خارق - وحدة - وحدات - نسخ احتياطي للوحدات المثبتة حالياً. - Sort (Enabled first) - Sort (Action first) - Sort (WebUI first) - diff --git a/manager/app/src/main/res/values-bg-rBG/strings.xml b/manager/app/src/main/res/values-bg-rBG/strings.xml deleted file mode 100644 index 7db90b53..00000000 --- a/manager/app/src/main/res/values-bg-rBG/strings.xml +++ /dev/null @@ -1,234 +0,0 @@ - - - Имате проблеми? - Открихте грешка или имате отзив? - Докладвайте го възможно най-скоро! - Докладвайте в GitHub - Свържете се чрез Telegram - https://github.com/KernelSU-Next/KernelSU-Next/issues - https://t.me/ksunext - Потвърди - KernelSU Next - Начало - Не е инсталирано - Кликнете, за да инсталирате - Режимът LKM вече е остарял! - Инсталирайте GKI ядро или интегрирайте KernelSU Next към вашето устройство. - Работи - Версия: %d - Актуализации: %d - Подписът на KernelSU Next v2 не е намерен в ядрото! [ !KSU_NEXT || != size/hash ] - Помолете разработчика на вашето ядро да интегрира KernelSU Next! - Версия на ядрото - Режим на закачане - Активиране - Деактивиране - Активиран - Деактивиран - Поддържан - SuSFS: %s - Версия на SuSFS - SuS SU - Версия на Андроид - Версия на мениджъра - ABI - Състояние на SELinux - Деактивиран - Прилагащ - Разрешителен - Непознат - Суперпотребител - Неуспешно активиране на модула: %s - Неуспешно деактивиране на модула: %s - Няма инсталиран модул - Модул - Следните модули ще бъдат инсталирани: %1$s - Сортиране (A → Z) - Сортиране (Z → A) - Сортиране (Ниско → Високо) - Сортиране (Високо → Ниско) - Деинсталирай - Възстанови - Инсталирай - Инсталирай - Рестартиране - Деинсталиран - Настройки - Софт рестарт - Рестартиране до Recovery - Рестартиране до Bootloader - Рестартиране до Download - Рестартиране до EDL - Относно - Сигурни ли сте, че искате да деинсталирате модул %s? - %s деинсталиран - Неуспешно деинсталиране: %s - Сигурни ли сте, че искате да възстановите модул %s? - %s възстановен - Неуспешно възстановяване: %s - Версия - Автор - Идентификатор - Код - Json за актуализации - Празен - Активиране на опциите за разработчици - Показване на скрити настройки и информация за отстраняване на грешки, отнасящи се само за разработчици. - Модулите не са налични, тъй като OverlayFS е деактивиран от ядрото! - Опресни - Покажи системни приложения - Скрий системните приложения - Експорт на логове - Безопасен режим - Рестартирайте, за да влезе в сила - Модулите не са налични поради конфликт с Magisk! - Модулна система - Magic Mount - OverlayFS - Неналично - Използвай OverlayFS - Превключване между използване на OverlayFS или Magic Mount за модулната система на KernelSU Next. - Нужен е рестарт - Промените ще влязат в сила след рестартирането на системата. Искате ли да рестартирате сега? - Възстановяване на модула - Възстановяване на модули от скорошно архивиране. - Архивиране & Възстановяване - Архивирай модул - Възстановяване на списъка с разрешения - Възстановяване на списъка с разрешения от скорошно архивиране. - Архивиране на списъка с разрешения - Архивиране на текущо конфигурирания списък с разрешения. - Предупреждение - Тази функция все още е в бета фаза и е в процес на разработка. Моля, уверете се, че сте направили резервно копие на модулите си, преди да продължите. Използвайте тази функция само ако разбирате потенциалните рискове. Действайте внимателно. - Продължи - Отказ - По-късно - Пачът за LKM разчита на компоненти със затворен код. Искате ли да продължите? - 🔥Следваща версия - https://github.com/KernelSU-Next/KernelSU-Next - Експериментален клон Next. Вижте го в GitHub! - ⚠️ Предупреждение за експериментална разработка! - 127.0.0.1 - KernelSU Next е неофициална версия, която е в процес на активна експериментална разработка. Предоставя се „както е“, без гаранции за стабилност, производителност или надеждност. - • Използвайте на свой собствен риск: могат да възникнат сривове, неочаквано поведение или системни проблеми. - • Без гаранция: разработчиците не носят отговорност за загуба на данни, системни повреди или други последици, произтичащи от използването му. - • Само за тестови цели: предназначено за потребители, които разбират рисковете и са умели да отстраняват проблеми. - Вижте изходния код в %1$s - Профил на приложението - Стандартно - Шаблон - По избор - Име на профила - Монтиране на namespace - Наследен - Глобален - Индивидуален - Групи - Способности - SELinux контекст - Демонтиране на модули - Актуализирането на профила на приложението за %s не бе успешно - Текущата версия %1$d на KernelSU Next е твърде ниска, за да работи мениджърът правилно. Моля, актуализирайте до версия %2$d или по-висока! - Демонтиране на модули - Глобалната стойност по подразбиране за „Демонтиране на модули“ в профила на приложението. Ако е активирана, ще премахне всички модификации на модулите в системата за приложения, които нямат зададен профил. - Скриване на закачването на kprobes - Тази опция деактивира kprobes hook-а, създаден от ksu, и вместо това активира вградения non-kprobes hook, реализирайки същата функционалност, която би била приложена към ядро, различно от GKI, което не поддържа kprobe. - Активирането на тази опция ще позволи на KernelSU Next да възстанови всички модифицирани файлове от модулите за това приложение. - Домейн - Правила - Актуализиране - Актуализация - Актуализиран - Изтегляне на модул: %s - Започнете изтеглянето: %s - Налична е нова версия %s, кликнете за актуализация. - Стартиране - Затвори - Принудително спиране - Рестартиране - AMOLED режим - Активирайте чисто черна тема, полезна за AMOLED екрани, за да намалите напрежението в очите и да пестите батерия. - Нужен е рестарт - Приложението трябва да се рестартира, за да влезе в сила тази промяна. - Неуспешно актуализиране на правилата на SELinux за %s - Не можа да се предостави достъп на суперпотребител на %s - Списък на промените - Шаблон за профил на приложение - Управление на локален и онлайн шаблон на профил на приложението - Създаване на шаблон - Редактиране на Шаблон - Идентификатор - Некоректен идентификатор на шаблона - Име - Описание - Запази - Изтрий - Преглед на шаблона - Само за четене - Идентификаторът на шаблона вече съществува! - Импорт/Експорт - Импортиране от клипборда - Експортирай в клипборда - Не може да се намери локален шаблон за експортиране! - Импортирано успешно - Синхронизиране на онлайн шаблони - Запазването на шаблона не бе успешно - Клипбордът е празен! - Извличането на списъка с промени не бе успешно: %s - Проверка за актуализации - Автоматична проверка за актуализации при отваряне на приложението - Неуспешно предоставяне на root достъп! - Действие - WebUI - Отваряне - Активиране на WebView дебъгване - Може да се използва за дебъгване на WebUI. Моля активирайте само когато е нужно. - Директно инсталиране (препоръчително) - Избери файл - Инсталиране в неактивен слот (след OTA) - Устройството ви ще бъде **ПРИНУДЕНО** да се стартира от текущия неактивен слот след рестартиране!\nИзползвайте тази опция само след като приключи OTA.\nИскате ли да продължите? - Следващ - Препоръчва се образ на дял %1$s - Изберете KMI - Минимизиране на разреденото изображение - Преоразмерете разреденото изображение, където се намира модулът, до действителния му размер. Обърнете внимание, че това може да доведе до неправилна работа на модула, така че, моля, използвайте го само когато е необходимо (например за архивиране). - Деинсталирай - Деинсталирай временно - Деинсталирай завинаги - Възстановяване на базово изображение - Временно деинсталирайте KernelSU Next, възстановете първоначалното състояние след следващото рестартиране. - Пълно и окончателно деинсталиране на KernelSU Next (Root и всички модули). - Възстановете базовото копие (ако съществува резервно копие), обикновено използвано преди OTA; ако трябва да деинсталирате KernelSU Next, моля, използвайте „Деинсталирай завинаги“. - Записване - Успешно записване - Неуспешно записване - Избран LKM: %s - Запазване на лог файлове - Лог файлове запазени - Сподели лог файлове - Деактивиране на съвместимостта със su - Временно деактивирайте възможността на всяко приложение да получава root права чрез командата ⁠su (съществуващите root процеси няма да бъдат засегнати). - Език - По подразбиране - Използване на стария потребителски интерфейс - Превключване към предишния стил на потребителския интерфейс. - Активиране на банери - Показване на фонови банери за модули. - Използване на WebUI X - Използване на WebUI X вместо WebUI, което поддържа повече API. - Инжектиране на Eruda в WebUI X - Инжектиране на конзола за дебъгване в WebUI X за да се олесни дебъгването. Нужно е Уеб Дебъгване да е активирано. - Персонализация - Разработчик - SUCOMPAT Е ДЕАКТИВИРАН - Zygisk е задължителен - Инжекция на Zygisk - Суперпотребител - Суперпотребители - Модул - Модули - Архивиране на текущо инсталираните модули. - Сортиране (първо активирани) - Сортиране (първо действие) - Сортиране (първо WebUI) - diff --git a/manager/app/src/main/res/values-bn-rBD/strings.xml b/manager/app/src/main/res/values-bn-rBD/strings.xml deleted file mode 100644 index a3edfc0e..00000000 --- a/manager/app/src/main/res/values-bn-rBD/strings.xml +++ /dev/null @@ -1,234 +0,0 @@ - - - সমস্যা হচ্ছে? - কোনো ত্রুটির সম্মুখীন অথবা প্রতিক্রিয়া পেয়েছেন? - ইহা দ্রুত রিপোর্ট করুন! - গিটহাবে রিপোর্ট করুন - টেলিগ্রামে যোগাযোগ করুন - https://github.com/KernelSU-Next/KernelSU-Next/issues - https://t.me/ksunext - নিশ্চিত করুন - KernelSU Next - হোম - ইন্সটল হয়নি - ইন্সটলের জন্য ক্লিক করুন - LKM মোড এখন অকার্যকর! - GKI কার্নেল ইন্সটল করুন অথবা KernelSU ইন্টিগ্রেড করুন।. - কার্যরত - Version: %d - আপডেটস: %d - KernelSU Next v2 signature পাওয়া যায়নি কার্নেলে! [ !KSU_NEXT || != size/hash ] - আপনার কার্নেল ডেভেলপারকে বলুন KernelSU Next ইন্টিগ্রেট করতে! - কার্নেল ভার্সন - হুক মোড - সক্রিয় - নিষ্ক্রিয় - সক্রিয় করা হয়েছে - নিষ্ক্রিয় করা হয়েছে - সমর্থিত - SuSFS: %s - SuSFS ভার্সন - SuS SU - এন্ড্রয়েড ভার্সন - ম্যানেজার ভার্সন - ABI - SELinux এর অবস্থা - অকার্যকর - বলবৎ করা - অনুমতিমূলক - অজানা - সুপারইউজার - মডিউল সক্রিয় করতে ব্যর্থ: %s - মডিউল নিষ্ক্রিয় করতে ব্যর্থ: %s - কোনো মডিউল ইন্সটল করা নেই - মডিউল - নিম্নলিখিত মডিউলগুলি ইনস্টল করা হবে: %1$s - সাজান (A → Z) - সাজান (Z → A) - সাজান (নিম্ন → উচ্চ) - সাজান (উচ্চ → নিম্ন) - আনইন্সটল - পুনরুদ্ধার করা - ইন্সটল - ইন্সটল - রিবুট - আনইনস্টল করা হয়েছে - সেটিংস - সফট রিবুট - রিকভারি জন্য রিবুট করুন - Reboot to Bootloader - Reboot to Download - Reboot to EDL - About - Are you sure you want to uninstall module %s? - %s uninstalled - Failed to uninstall: %s - Are you sure you want to restore module %s? - %s restored - Failed to restore: %s - Version - Author - ID - কোড - UpdateJson - খালি - Enable developer options - Show hidden settings and debug info relevant only for developers. - Modules are unavailable as OverlayFS is disabled by the kernel! - Refresh - Show system apps - Hide system apps - Export logs - Safe mode - Reboot to take effect - Modules are unavailable due to a conflict with Magisk! - Mount system - Magic Mount - OverlayFS - Unavailable - Use OverlayFS - Toggle between using OverlayFS over Magic Mount for KernelSU Next\'s mount system. - Reboot required - The changes will take effect after the system restart. Do you want to reboot now? - Restore module - Restore modules from recent backup. - Backup & Restore - Backup module - Restore allowlist - Restore allowlist from recent backup. - Backup allowlist - Backup currently configured allowlist. - Warning - এই বৈশিষ্ট্যটি এখনও বিটা পর্যায়ে রয়েছে এবং বিকাশের অধীনে রয়েছে। এগিয়ে যাওয়ার আগে দয়া করে আপনার মডিউলগুলির ব্যাকআপ নিন। সম্ভাব্য ঝুঁকিগুলি বুঝতে পারলেই কেবল এই বৈশিষ্ট্যটি ব্যবহার করুন। সাবধানতার সাথে এগিয়ে যান। - এগিয়ে যান - বাতিল করুন - পরে - LKM প্যাচটি ক্লোজড সোর্স উপাদানের উপর নির্ভর করে। আপনি কি চালিয়ে যেতে চান? - 🔥 পরবর্তী বিল্ড - https://github.com/KernelSU-Next/KernelSU-Next - Next experimental branch. Check it out on GitHub! - ⚠️পরীক্ষামূলক উন্নয়নের সতর্কতা! - 127.0.0.1 - KernelSU Next একটি বেসরকারী সংস্করণ যা সর্বদা সক্রিয় পরীক্ষামূলক বিকাশের অধীনে থাকে। এটি যেমন আছে তেমনই সরবরাহ করা হয়, স্থিতিশীলতা, কর্মক্ষমতা বা নির্ভরযোগ্যতার কোনও গ্যারান্টি ছাড়াই। - • আপনার নিজের ঝুঁকিতে ব্যবহার করুন: ক্র্যাশ, অপ্রত্যাশিত আচরণ, অথবা সিস্টেম সমস্যা দেখা দিতে পারে। - • কোনও ওয়ারেন্টি নেই: ডেভেলপাররা এর ব্যবহারের ফলে কোনও ডেটা ক্ষতি, সিস্টেমের ক্ষতি বা অন্যান্য পরিণতির জন্য দায়ী থাকবে না। - • শুধুমাত্র পরীক্ষার উদ্দেশ্যে: ঝুঁকিগুলি বোঝেন এবং সমস্যা সমাধানে স্বাচ্ছন্দ্য বোধ করেন এমন ব্যবহারকারীদের জন্য। - সোর্স কোড দেখুন %1$s - App Profile - ডিফল্ট - টেমপ্লেট - কাস্টম - প্রোফাইলের নাম - নেমস্পেস মাউন্ট করুন - উত্তরাধিকারসূত্রে প্রাপ্ত - গ্লোবাল - স্বতন্ত্র - গ্রুপ - সক্ষমতা - SELinux প্রসঙ্গ - Umount modules - Failed to update App Profile for %s - The current KernelSU Next version %1$d is too low for the manager to work properly. Please upgrade to version %2$d or higher! - Umount modules - 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. - Hide kprobes hook - This option disables the kprobes hook created by ksu and, instead, activates the embedded non-kprobes hook, implementing the same functionality that would be applied to a non-GKI kernel, which doesn\'t support kprobe. - Enabling this option will allow KernelSU Next to restore any modified files by the modules for this app. - Domain - Rules - Update - Update - Updated - Downloading module: %s - Start downloading: %s - New version %s is available, click to upgrade. - Launch - Close - Force stop - Restart - AMOLED mode - Enable a pure black theme useful for AMOLED screens to reduce eye strain and save battery. - Restart required - The app needs to restart for this change to take effect. - Failed to update SELinux rules for %s - Couldn\'t grant Superuser access to %s - Changelog - App Profile template - Manage local and online template of App Profile - Create template - Edit template - ID - Invalid template ID - Name - Description - Save - Delete - View template - Read only - Template ID already exists! - Import/Export - Import from clipboard - Export to clipboard - Cannot find local template to export! - Imported successfully - Sync online templates - Failed to save template - Clipboard is empty! - Fetch changelog failed: %s - Check for updates - Automatically check for updates when opening the app - Failed to grant root! - Action - WebUI - Open - 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) - 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). - Uninstall - Uninstall temporarily - Uninstall permanently - Restore stock image - Temporarily uninstall KernelSU Next, restore to original state after next reboot. - Uninstalling KernelSU Next (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 Next, please use \"Uninstall permanently\". - Flashing - Flash success - Flash failed - Selected LKM: %s - Save logs - Logs saved - Share logs - Disable su compatibility - Temporarily disable the ability of any app to gain root privileges via the ⁠su command (Existing root processes won\'t be affected). - Language - System default - Use legacy UI - Switch to the previous user interface style. - Enable banners - Show background banners for modules. - Use WebUI X - Use WebUI X instead of WebUI, which supports more APIs. - Inject Eruda into WebUI X - Inject a debug console into WebUI X to make debugging easier. Requires web debugging to be on. - Customization - Developer - SUCOMPAT DISABLED - Zygisk required - Zygisk injection - Superuser - Superusers - Module - Modules - Backup currently installed modules. - Sort (Enabled first) - Sort (Action first) - Sort (WebUI first) - diff --git a/manager/app/src/main/res/values-de-rDE/strings.xml b/manager/app/src/main/res/values-de-rDE/strings.xml deleted file mode 100644 index e1a29c77..00000000 --- a/manager/app/src/main/res/values-de-rDE/strings.xml +++ /dev/null @@ -1,234 +0,0 @@ - - - Haben Sie Schwierigkeiten? - Sind Sie auf einen Fehler gestoßen, oder wollen Sie eine Rückmeldung geben? - Melden Sie es so schnell wie möglich! - Meldung über GitHub - Kontakt über Telegram - https://github.com/KernelSU-Next/KernelSU-Next/issues - https://t.me/ksunext - Bestätigen - KernelSU Next - Hauptseite - Nicht installiert - Klicke zum Installieren - Der LKM-Modus ist jetzt veraltet! - Installieren Sie den GKI-Kernel oder integrieren Sie KernelSU Next in Ihrem Gerät. - Funktioniert - Version: %d - Aktualisierungen: %d - KernelSU Next v2 Signatur nicht gefunden im Kernel! [!KSU_NEXT || != Größe/Hash] - Bitten Sie Ihren Kernel-Entwickler KernelSU Next zu integrieren! - Kernel-Version - Einbindungs-Modus - Aktivieren - Deaktivieren - Aktiviert - Deaktiviert - Unterstützt - SuSFS: %s - SuSFS-Version - SuS SU - Android-Version - Manager-Version - Application Binary Interface (ABI) - SELinux-Status - Deaktiviert - Erzwingung - Freizügig - Unbekannt - Superuser - Modulaktivierung fehlgeschlagen: %s - Moduldeaktivierung fehlgeschlagen: %s - Kein Modul installiert - Modul - Die folgenden Module werden installiert: %1$s - Sortierung (A → Z) - Sortierung (Z → A) - Sortierung (niedrig → hoch) - Sortierung (hoch → niedrig) - Deinstallieren - Wiederherstellen - Installieren - Installieren - Neustart - deinstalliert - Einstellungen - Schneller Neustart - Neustart ins Recovery - Neustart in den Bootloader - Neustart in den Download-Modus - Neustart in den Notfall-Download-Modus (EDL) - Über - Möchten Sie das Modul %s wirklich deinstallieren? - %s deinstalliert - Deinstallation fehlgeschlagen: %s - Sind Sie sicher, dass Sie das Modul %s wiederherstellen möchten? - %s wiederhergestellt - Wiederherstellung fehlgeschlagen: %s - Version - Autor - Kennung - Schlüssel - Aktualisiere Json - Leer - Aktivieren Sie die Entwickleroptionen - Zeigen Sie die versteckten Einstellungen und Fehlersuche-Informationen, die nur für Entwickler relevant sind. - Module sind nicht verfügbar, da OverlayFS vom Kernel deaktiviert wurde! - Aktualisieren - System-Apps anzeigen - System-Apps ausblenden - Exportprotokolle - Abgesicherter Modus - Neu starten, um wirksam zu werden - Module sind aufgrund eines Konflikts mit Magisk nicht verfügbar! - Einhäng-Methode - Magic einhängen - OverlayFS - Nicht verfügbar - Verwenden Sie OverlayFS - Schalten Sie zwischen der Verwendung von OverlayFS und Magic Mount für das Einhängesystem von KernelSU Next um. - Neustart erforderlich - Die Änderungen werden nach dem Neustart des Systems wirksam. Möchten Sie jetzt neu starten? - Module wiederherstellen - Die Module aus der letzten Sicherung wiederherstellen. - Sicherung & Wiederherstellung - Module sichern - Zulassungsliste wiederherstellen - Die Zulassungsliste aus der letzten Sicherung wiederherstellen. - Zulassungsliste sichern - Sichern der momentan konfigurierten Zulassungsliste. - Warnung - Diese Funktion befindet sich noch im Beta-Status und in der Entwicklung. Bitte stellen Sie sicher, dass Sie Ihre Module sichern bevor Sie fortfahren. Verwenden Sie diese Funktion nur, wenn Sie die potenziellen Risiken verstehen. Vorsichtig vorgehen. - Fortfahren - Abbrechen - Später - Der LKM-Patch basiert auf geschlossenen Quellen-Komponenten. Möchten Sie fortfahren? - 🔥 Nächster Build - https://github.com/KernelSU-Next/KernelSU-Next - Nächster experimenteller Zweig. Probieren Sie es auf GitHub aus! - ⚠️ Warnung vor experimenteller Entwicklung! - 127.0.0.1 - KernelSU Next ist eine nicht-offizielle Version, die sich stets in aktiver experimenteller Entwicklung befindet. Sie wird im Ist-Zustand zur Verfügung gestellt, ohne Garantie auf Stabilität, Leistung oder Zuverlässigkeit. - • Verwenden Sie auf eigenes Risiko: Abstürze, unerwartes Verhalten oder Systemprobleme können auftreten. - • Keine Garantie: Die Entwickler sind nicht verantwortlich für Datenverluste, Systemschäden oder andere Folgen, die sich aus der Nutzung ergeben. - • Nur zu Testzwecken: für Benutzer, die sich der Risiken bewusst sind und mit der Behebung von Problemen vertraut sind. - Quellcode anzeigen unter %1$s - App-Profil - Standard - Vorlage - Benutzerdefiniert - Profilname - Namensraum einhängen - Geerbt - Global - Individuell - Gruppen - Fähigkeiten - SELinux Kontext - Module aushängen - Das App Profil für %s wurde nicht aktualisiert - Der aktuelle KernelSU Next Version %1$d ist zu niedrig, als dass der Manager ordnungsgemäß funktioniert. Bitte aktualisieren Sie auf Version %2$d oder höher! - Module aushängen - Der globale Standardwert für „Module aushängen“ im App-Profil. Wenn er aktiviert ist, werden alle Moduländerungen im System für Apps entfernt, für die kein Profil festgelegt ist. - Kprobes Hook ausblenden - Diese Option deaktiviert den von ksu erzeugten kprobes-Hook und aktiviert stattdessen den eingebetteten non-kprobes-Hook, der die gleiche Funktionalität implementiert, die auf einen Nicht-GKI-Kernel angewendet würde, der kprobe nicht unterstützt. - Wenn Sie diese Option aktivieren, kann KernelSU Next alle von den Modulen für diese Anwendung geänderten Dateien wiederherstellen. - Domäne - Regeln - Aktualisieren - Aktualisieren - Aktualisiert - Modul wird heruntergeladen: %s - Start des Herunterladens: %s - Neue Version %s ist verfügbar, klicken Sie zum Aktualisieren. - Start - Schließen - Stopp erzwingen - Neu starten - AMOLED-Modus - Aktivieren Sie ein rein schwarzes Thema, das für AMOLED-Bildschirme nützlich ist, um die Augen zu entlasten und den Akku zu schonen. - Neustart erforderlich - Die App muss neu gestartet werden, damit diese Änderung wirksam wird. - Aktualisierung der SELinux Regeln für %s fehlgeschlagen - Superuser-Zugriff auf %s konnte nicht gewährt werden - Änderungsprotokoll - App Profilvorlage - Verwalten Sie lokale und Online-Vorlagen von App-Profilen - Vorlage erstellen - Vorlage bearbeiten - Kennung - Ungültige Vorlagen-Kennung - Name - Beschreibung - Speichern - Löschen - Vorlage anzeigen - Nur lesen - Vorlagen -Kennung existiert bereits! - Importieren/Exportieren - Aus der Zwischenablage importieren - In die Zwischenablage exportieren - Lokale Vorlage zum Exportieren nicht gefunden! - Erfolgreich importiert - Online Vorlagen synchronisieren - Vorlage speichern fehlgeschlagenen - Zwischenablage ist leer! - Änderungsprotokoll abrufen fehlgeschlagen: %s - Nach Aktualisierungen suchen - Automatische Suche nach Updates beim Öffnen der App - Root-Zugriff konnte nicht gewährt werden! - Aktion - WebUI - Öffnen - WebView-Debugging einschalten - Kann zum Debuggen von WebUI verwendet werden. Bitte nur bei Bedarf aktivieren. - Direkte Installation (empfohlen) - Datei wählen - Auf inaktive Partition installieren (nach OTA) - Ihr Gerät wird **gezwungen**, nach einem Neustart in die aktuell inaktive Partition zu booten!\nVerwenden Sie diese Option nur, nachdem OTA abgeschlossen ist.\nFortfahren? - Weiter - %1$s Partitionsabbild wird empfohlen - Wählen Sie KMI - Sparse-Image minimieren - Ändern Sie 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, und verwenden Sie es daher nur, wenn es notwendig ist (z. B. zur Sicherung). - Deinstallieren - Vorübergehend deinstallieren - Dauerhaft deinstallieren - Werksabbild wiederherstellen - Vorübergehende Deinstallation von KernelSU Next, Wiederherstellung des ursprünglichen Zustands nach dem nächsten Neustart. - KernelSU Next (Root und alle Module) vollständig und dauerhaft deinstallieren. - Stellen Sie das Werksabbild wieder her (wenn ein Backup vorhanden ist), das normalerweise vor OTA verwendet wird; Wenn Sie KernelSU Next deinstallieren müssen, verwenden Sie bitte „Dauerhaft deinstallieren“. - Schreiben - Schreiben erfolgreich - Schreiben fehlgeschlagen - Ausgewählte LKM: %s - Protokolle speichern - Protokolle gespeichert - Protokolle teilen - su-Kompatibilität deaktivieren - Deaktivieren Sie vorübergehend die Fähigkeit jeder Anwendung, über den Befehl su Root-Rechte zu erlangen (bestehende Root-Prozesse sind davon nicht betroffen). - Sprache - Systemvorgabe - Alte Benutzeroberfläche verwenden - Wechseln Sie zum vorherigen Benutzeroberflächenstil. - Aktivieren von Bannern - Hintergrundbanner für Module anzeigen. - Verwende WebUI X - Verwenden Sie WebUI X anstelle von WebUI, das mehr APIs unterstützt. - Eruda in WebUI X einbinden - Einfügen einer Debug-Konsole in WebUI X, um das Debugging zu erleichtern. Erfordert, dass Web-Debugging eingeschaltet ist. - Personalisierung - Entwickler - SUCOMPAT DEAKTIVIERT - Zygisk erforderlich - Zygisk-Einbindung - Superuser - Superusers - Modul - Module - Sicherung der aktuell installierten Module. - Sortierung (aktivierte zuerst) - Sortierung (Aktion zuerst) - Sortierung (WebUI zuerst) - diff --git a/manager/app/src/main/res/values-es-rEM/strings.xml b/manager/app/src/main/res/values-es-rEM/strings.xml deleted file mode 100644 index baac3e0a..00000000 --- a/manager/app/src/main/res/values-es-rEM/strings.xml +++ /dev/null @@ -1,234 +0,0 @@ - - - ¿Tienes problemas? - ¿Encontraste un error o tienes comentarios? - ¡Repórtalo lo mas pronto posible! - Reportar en GitHub - Contactar por Telegram - https://github.com/KernelSU-Next/KernelSU-Next/issues - https://t.me/ksunext - Confirmar - KernelSU Next - Inicio - No instalado - Haz clic para instalar - ¡El modo LKM está obsoleto! - Instale un kernel GKI o integre KernelSU Next a su dispositivo. - Funcionando - Versión: %d - Actualizaciones: %d - ¡No se encontró la firma de KernelSU Next v2 en el kernel! [ !KSU_NEXT || != size/hash ] - ¡Pídele a tu desarrollador de kernel que integre KernelSU Next! - Versión del kernel - Método de intercepción - Activar - Desactivar - Activado - Desactivado - Compatible - SuSFS: %s - Versión de SuSFS - SuS SU - Versión de Android - Versión del gestor - ABI - Estado de SELinux - Deshabilitado - Estricto - Permisivo - Desconocido - Superusuario - No se pudo activar el módulo: %s - No se pudo desactivar el módulo: %s - Ningún módulo instalado - Módulo - Los siguientes módulos serán instalados: %1$s - Ordenar (A → Z) - Ordenar (Z → A) - Ordenar (Menor → Mayor) - Ordenar (Mayor → Menor) - Desinstalar - Restaurar - Instalar - Instalar - Reiniciar - Desinstalado - Ajustes - Reinicio rápido - Reiniciar en modo Recovery - Reiniciar en modo Bootloader - Reiniciar en modo Download - Reiniciar en Modo EDL - Acerca de - ¿Estás seguro que quieres desinstalar el módulo %s? - %s desinstalado - No se pudo desinstalar: %s - ¿Estás seguro que quieres restaurar el módulo %s? - %s restaurado - No se pudo restaurar: %s - Versión - Autor - Identificador - Revisión - UpdateJson - Vacío - Activar opciones de desarrollador - Muestra ajustes ocultos e información de depuración relevantes solo para desarrolladores. - ¡Los módulos no están disponibles ya que OverlayFS está deshabilitado por el kernel! - Actualizar - Mostrar aplicaciones del sistema - Ocultar aplicaciones del sistema - Exportar logs - Modo seguro - Reiniciar para aplicar cambios - ¡Los módulos no están disponibles debido a un conflicto con Magisk! - Mecanismo de montaje - Magic Mount - OverlayFS - No disponible - Usar OverlayFS - Alternar entre usar OverlayFS o Magic Mount para el mecanismo de montaje de KernelSU Next. - Reinicio requerido - Los cambios tendrán efecto después de reiniciar el sistema. ¿Deseas reiniciar ahora? - Restaurar módulo - Restaurar módulos desde una copia de seguridad reciente. - Copia de seguridad y Restauración - Respaldar módulo - Restaurar lista blanca - Restaurar la lista blanca desde una copia de seguridad reciente. - Respaldar lista blanca - Respaldar la lista blanca actualmente configurada. - Advertencia - Esta función aún está en fase beta y en desarrollo. Asegúrate de hacer una copia de seguridad de tus módulos antes de continuar. Usa esta función solo si entiendes los riesgos potenciales. Procede con precaución. - Continuar - Cancelar - Más adelante - El parche LKM depende de componentes de código cerrado. ¿Quieres continuar? - 🔥 Compilación Next - https://github.com/KernelSU-Next/KernelSU-Next - Rama Next experimental. ¡Échale un vistazo en GitHub! - ⚠️ ¡Advertencia de desarrollo experimental! - 127.0.0.1 - KernelSU Next es una versión no oficial que siempre está en desarrollo experimental activo. Se proporciona \"tal cual\", sin garantías de estabilidad, rendimiento o fiabilidad. - • Úsalo bajo tu propio riesgo: pueden ocurrir fallos, comportamientos inesperados o problemas en el sistema. - • Sin garantía: los desarrolladores no se hacen responsables de ninguna pérdida de datos, daño al sistema u otras consecuencias resultantes de su uso. - • Solo para fines de prueba: destinado a usuarios que entienden los riesgos y se sienten cómodos solucionando problemas. - Ver el código fuente en %1$s - Perfil de Aplicación - Predeterminado - Plantilla - Personalizado - Nombre de perfil - Espacio de nombres de montaje - Heredado - Global - Individual - Grupos - Privilegios - Contexto de SELinux - Desmontar módulos - Error al actualizar el perfil de aplicación para %s - La versión actual de KernelSU Next %1$d es demasiado antigua para que el gestor funcione correctamente. ¡Actualiza a la versión %2$d o superior! - Desmontar módulos - El valor global predeterminado para \"Desmontar módulos\" en el Perfil de App. Si se activa, eliminará todas las modificaciones de módulos en el sistema para las aplicaciones que no tengan un perfil establecido. - Ocultar la intercepción de kprobes - Esta opción desactiva la intercepción de kprobes creada por ksu y, en su lugar, activa la intercepción integrada que no se basa en kprobes, implementando la misma funcionalidad que se aplicaría a un kernel no GKI, que no soporta kprobes. - Activar esta opción permitirá a KernelSU Next restaurar cualquier archivo modificado por los módulos para esta apliacion. - Dominio - Reglas - Actualizar - Actualización - Actualizado - Descargando módulo: %s - Iniciando descarga: %s - Nueva versión %s disponible, toca para actualizar. - Iniciar - Cerrar - Forzar detención - Reiniciar - Modo AMOLED - Activa un aspecto negro puro, útil para pantallas AMOLED, reducir la fatiga visual y ahorrar batería. - Reinicio necesario - La aplicación necesita reiniciarse para que este cambio tenga efecto. - Error al actualizar las reglas de SELinux para %s - No se pudo conceder acceso de Superusuario a %s - Registro de cambios - Plantilla del perfil de aplicación - Gestiona plantillas locales y en línea del Perfil de la Aplicación - Crear plantilla - Editar plantilla - ID - ID de plantilla inválido - Nombre - Descripción - Guardar - Borrar - Ver plantilla - Sólo lectura - ¡El ID de plantilla ya existe! - Importar/Exportar - Importar desde portapapeles - Exportar al portapapeles - ¡No se encontraron plantillas locales para exportar! - Importado con éxito - Sincronizar plantillas en línea - Error al guardar plantilla - ¡El portapapeles está vacío! - Error al obtener registro de cambios: %s - Verificar si hay actualizaciones - Buscar actualizaciones automáticamente al abrir la aplicación - ¡Error al conceder root! - Acción - WebUI - Abrir - Activar depuración de WebView - Puede usarse para depurar WebUI. Habilitar solo cuando sea necesario. - Instalación directa (Recomendado) - Seleccionar un archivo - Instalar al ranura inactiva (Después de OTA) - ¡Tu dispositivo será **FORZADO** a iniciar en la ranura inactiva actual después de reiniciar!\nUsa esta opción solo después de haber realizado una OTA.\n¿Continuar? - Siguiente - Se recomienda la imagen de partición %1$s - Seleccionar KMI - Minimizar imagen sparse - Redimensiona la imagen sparse donde se encuentra el módulo a su tamaño real. Ten en cuenta que esto puede hacer que el módulo funcione de forma anómala, así que por favor, úsalo solo cuando sea necesario (como para una copia de seguridad). - Desinstalar - Desinstalar temporalmente - Desinstalar permanentemente - Restaurar imagen stock - Desinstalar temporalmente KernelSU Next, restaurar al estado original después del próximo reinicio. - Desinstalar KernelSU Next (Root y todos los módulos) completa y permanentemente. - Restaura la imagen stock (si existe una copia de seguridad), usado normalmente antes de una OTA; si necesitas desinstalar KernelSU Next, usa \"Desinstalación permanente\". - Flasheando - Flasheado con éxito - Error en el flasheo - Seleccionado LKM: %s - Guardar logs - Logs guardados - Enviar logs - Deshabilitar compatibilidad su - Desactiva temporalmente la capacidad de cualquier app para obtener privilegios root mediante el comando ⁠su (los procesos root existentes no se verán afectados). - Idioma - Predeterminado del sistema - Usar interfaz antigua - Cambia al estilo de interfaz de usuario anterior. - Activar banners - Mostrar banners de fondo para los módulos. - Usar WebUI X - Usar WebUI X en lugar de WebUI, que soporta más APIs. - Inyectar Eruda en WebUI X - Inyecta una consola de depuración en WebUI X para facilitar la depuración. Requiere que la depuración web esté activada. - Personalización - Desarrollador - SUCOMPAT DESHABILITADO - Se requiere Zygisk - Inyección Zygisk - Superusuario - Superusuarios - Módulo - Módulos - Respaldar los módulos actualmente instalados. - Sort (Enabled first) - Sort (Action first) - Sort (WebUI first) - diff --git a/manager/app/src/main/res/values-fa-rIR/strings.xml b/manager/app/src/main/res/values-fa-rIR/strings.xml deleted file mode 100644 index df25e921..00000000 --- a/manager/app/src/main/res/values-fa-rIR/strings.xml +++ /dev/null @@ -1,235 +0,0 @@ - - - مشکلی دارید؟ - با یک اشکال مواجه شدید یا بازخوردی دارید؟ - هر چه سریعتر گزارش دهید! - گزارش در گیت‌هاب - تماس از طریق تلگرام - https://github.com/KernelSU-Next/KernelSU-Next/issues - https://t.me/ksunext - تأیید - KernelSU Next - خانه - نصب نشده - برای نصب کلیک کنید - حالت LKM اکنون منسوخ شده است! - کرنل GKI نصب کنید یا KernelSU را به صورت دستی بسازید. - درحال کارکردن - نسخه: %d - بروزرسانی ها: %d - امضای KernelSU Next v2 در کرنل یافت نشد! -[KSU_NEXT || != size/hash!] - از توسعه‌دهنده‌ی کرنل خود بخواهید که KernelSU Next را برای دستگاهتون بسازد! - نسخه کرنل - حالت هوک - فعال کردن - غیرفعال کردن - فعال شده - غیرفعال شده - پشتیبانی شده - SuSFS: %s - نسخه SuSFS - SuS SU - نسخه اندروید - نسخه مدیریت - رابطه باینری - وضعیت SELinux - غیرفعال شده - درحال اجرا - مجاز - ناشناخته - کاربر روت - ماژول فعال نشد: %s - ماژول غیرفعال نشد: %s - ماژولی نصب نشده - ماژول - ماژول‌های زیر نصب خواهند شد: %1$s - مرتب‌سازی (الف → ی) - مرتب‌سازی (ی → الف) - مرتب‌سازی (کم → زیاد) - مرتب‌سازی (زیاد → کم) - حذف نصب - بازیابی - نصب - نصب - راه‌اندازی مجدد - حذف نصب شد - تنظیمات - راه‌اندازی مجدد نرم - راه‌اندازی مجدد به ریکاوری - راه‌اندازی مجدد به بوتلودر - راه‌اندازی مجدد به دانلود - راه‌اندازی مجدد به EDL - درباره - آیا مطمئن هستید که می‌خواهید ماژول را حذف نصب کنید %s؟ - %s حذف نصب شد - حذف نصب انجام نشد: %s - آیا مطمئن هستید که می‌خواهید ماژول را بازیابی کنید %s؟ - %s بازیابی شد - بازیابی نشد: %s - نسخه - نویسنده - آیدی - کد - لینک بروزرسانی - خالی - فعال کردن گزینه توسعه دهندگان - نمایش تنظیمات پنهان و اطلاعات اشکال‌زدایی که فقط برای توسعه‌دهندگان مرتبط است. - ماژول‌ها در دسترس نیستند زیرا OverlayFS توسط کرنل غیرفعال شده است! - ریفرش - نمایش برنامه‌های سیستمی - مخفی کردن برنامه‌های سیستمی - خروجی گرفتن از لاگ‌ها - حالت امن - برای اعمال شدن، راه‌اندازی مجدد کنید - ماژول‌ها به دلیل تداخل با Magisk در دسترس نیستند! - سیستم نصب - Magic Mount - OverlayFS - غیرقابل دسترس - از OverlayFS استفاده کنید - برای سیستم مانت KernelSU Next، بین استفاده از OverlayFS به جای Magic Mount یکی را انتخاب کنید. - راه‌اندازی مجدد مورد نیاز است - تغییرات پس از راه‌اندازی مجدد سیستم اعمال می‌شوند. آیا می‌خواهید اکنون سیستم را مجدداً راه‌اندازی کنید؟ - بازیابی ماژول - ماژول‌ها را از نسخه پشتیبان اخیر بازیابی کنید. - پشتیبان گیری & بازیابی - پشتیبان گیری ماژول - بازیابی لیست مجاز - لیست مجاز را از نسخه پشتیبان اخیر بازیابی کنید. - پشتیبان گیری لیست مجاز - از لیست مجوزهای پیکربندی‌شده‌ی فعلی پشتیبان تهیه کنید. - هشدار - این ویژگی هنوز در مرحله بتا و در حال توسعه است. لطفاً قبل از ادامه، از ماژول‌های خود نسخه پشتیبان تهیه کنید. فقط در صورتی از این ویژگی استفاده کنید که خطرات احتمالی را درک می‌کنید. با احتیاط عمل کنید. - ادامه - لغو - بعداً - پچ LKM به اجزای متن‌باز وابسته است. آیا می‌خواهید ادامه دهید؟ - 🔥 نسخه بعدی - https://github.com/KernelSU-Next/KernelSU-Next - شاخه آزمایشی بعدی. آن را در گیت‌هاب بررسی کنید! - ⚠️ هشدار توسعه آزمایشی! - 127.0.0.1 - KernelSU Next یک نسخه غیر رسمی است که همیشه در حال توسعه آزمایشی فعال است. این نسخه به همین شکل ارائه می‌شود و هیچ تضمینی برای پایداری، عملکرد یا قابلیت اطمینان آن وجود ندارد. - • با مسئولیت خودتان استفاده کنید: ممکن است خرابی، رفتار غیرمنتظره یا مشکلات سیستم رخ دهد. - • بدون ضمانت: توسعه‌دهندگان هیچ مسئولیتی در قبال از دست رفتن داده‌ها، آسیب سیستم یا سایر عواقب ناشی از استفاده از آن ندارند. - • فقط برای اهداف آزمایشی: برای کاربرانی در نظر گرفته شده است که خطرات را درک می‌کنند و در عیب‌یابی مشکلات راحت هستند. - نمایش کد منبع در %1$s - پروفایل برنامه - پیش‌فرض - الگو - سفارشی - نام پروفایل - فضای نام mount - به ارث رسیده - جهانی - فردی - گروه‌ها - قابلیت‌ها - زمینه SELinux - ماژول‌های نصب‌شده - به‌روزرسانی نمایه برنامه برای %s ناموفق بود - نسخه فعلی KernelSU Next %1$d برای عملکرد صحیح مدیریت، بسیار ضعیف است. لطفاً به نسخه %2$d یا بالاتر ارتقا دهید! - ماژول‌های نصب‌شده - مقدار پیش‌فرض جهانی برای \"ماژول‌های نصب‌شده\" در پروفایل برنامه. در صورت فعال بودن، تمام تغییرات ماژول در سیستم را برای برنامه‌هایی که مجموعه پروفایل ندارند، حذف می‌کند. - هوک kprobes را مخفی کنید - این گزینه هوک kprobes ایجاد شده توسط ksu را غیرفعال می‌کند و در عوض، هوک non-kprobes تعبیه شده را فعال می‌کند و همان عملکردی را که برای یک کرنل غیر GKI اعمال می‌شود، پیاده‌سازی می‌کند، کرنلی که از kprobe پشتیبانی نمی‌کند. - فعال کردن این گزینه به KernelSU Next اجازه می‌دهد تا هرگونه فایل تغییر یافته توسط ماژول‌های این برنامه را بازیابی کند. - دامنه - قوانین - بروزرسانی - بروزرسانی - بروزرسانی شده - درحال دانلود ماژول: %s - شروع دانلود: %s - نسخه جدید %s در دسترس است، برای ارتقا کلیک کنید. - اجرا - بستن - توقف اجباری - راه‌اندازی مجدد - حالت آمولد - یک تم مشکی خالص که برای صفحات آمولد مفید است را فعال کنید تا خستگی چشم کاهش یابد و باتری ذخیره شود. - راه‌اندازی مجدد مورد نیاز است - برای اعمال این تغییر، برنامه باید مجدداً راه‌اندازی شود. - به‌روزرسانی قوانین SELinux برای %s ناموفق بود - نتوانستم به کاربر روت دسترسی به %s را بدم - تغییرات - الگوی پروفایل برنامه - مدیریت قالب محلی و آنلاین پروفایل برنامه - ایجاد الگو - ویرایش الگو - آیدی - آیدی الگو نامعتبر است - نام - توضیحات - ذخیره - حذف - مشاهده الگو - فقط خواندنی - آیدی الگو از قبل وجود دارد! - ورودی/خروجی - وارد کردن از کلیپ بورد - خروجی گرفتن به کلیپ بورد - الگوی محلی برای خروجی گرفتن پیدا نشد! - با موفقیت وارد شد - همگام‌سازی قالب‌های آنلاین - ذخیره الگو انجام نشد - کلیپ بورد خالی است! - دریافت گزارش تغییرات ناموفق بود: %s - بررسی برای بروزرسانی - بررسی خودکار به‌روزرسانی‌ها هنگام باز کردن برنامه - دادن دسترسی روت ناموفق بود! - اکشن - WebUI - باز - اشکال‌زدایی WebView را فعال کنید - می‌تواند برای اشکال‌زدایی رابط کاربری WebUI استفاده شود. لطفاً فقط در صورت نیاز فعال کنید. - نصب مستقیم (پیشنهادی) - انتخاب فایل - نصب روی اسلات غیرفعال (بعد از OTA) - دستگاه شما پس از راه‌اندازی مجدد **مجبور** خواهد بود که در اسلات غیرفعال فعلی بوت شود!\nفقط پس از انجام OTA از این گزینه استفاده کنید.\nادامه می‌دهید؟ - بعدی - تصویر پارتیشن %1$s توصیه می‌شود - انتخاب KMI - تصویر پراکنده را به حداقل برسانید - تصویر پراکنده‌ای که ماژول در آن قرار دارد را به اندازه واقعی آن تغییر اندازه دهید. توجه داشته باشید که این کار ممکن است باعث شود ماژول به طور غیرعادی کار کند، بنابراین لطفاً فقط در صورت لزوم (مانند پشتیبان‌گیری) از آن استفاده کنید. - حذف نصب - حذف نصب موقت - حذف نصب دائم - بازنشانی image استوک - KernelSU را موقتاً حذف نصب کنید، سپس پس از راه‌اندازی مجدد بعدی، سیستم را به حالت اولیه بازگردانید. - حذف کامل و دائمی KernelSU Next (روت و تمام ماژول‌ها). - بازیابی فایل ایمیج کارخانه (در صورت وجود نسخه پشتیبان) که معمولاً قبل از OTA استفاده می‌شود؛ اگر نیاز به حذف نصب KernelSU دارید، لطفاً از گزینه «حذف دائمی» استفاده کنید. - درحال فلش کردن - فلش با موفقیت انجام شد - فلش نشد - LKM انتخاب شده: %s - ذخیره لاگ‌ها - لاگ‌ها ذخیره شدند - اشتراک گذاری لاگ‌ها - غیرفعال کردن سازگاری با su - به طور موقت توانایی هر برنامه‌ای را برای به دست آوردن امتیازات روت از طریق دستور ⁠SU غیرفعال کنید (فرآیندهای روت موجود تحت تأثیر قرار نمی‌گیرند) - زبان - پیش فرض سیستم - استفاده از Legacy UI - به سبک رابط کاربری قبلی تغییر دهید. - فعال کردن بنرها - نمایش بنرهای پس‌زمینه برای ماژول‌ها. - استفاده از WebUI X - به جای WebUI از WebUI X استفاده کنید که از APIهای بیشتری پشتیبانی می‌کند. - تزریق Eruda به WebUI X - یک کنسول اشکال‌زدایی به WebUI X تزریق کنید تا اشکال‌زدایی آسان‌تر شود. لازم است اشکال‌زدایی وب فعال باشد. - سفارشی‌سازی - توسعه دهنده - SUCOMPAT غیرفعال شده - نیازمند Zygisk - تزریق Zygisk - کاربر روت - کاربر های روت - ماژول - ماژول ها - از ماژول‌های نصب‌شده‌ی فعلی پشتیبان تهیه کنید. - Sort (Enabled first) - Sort (Action first) - Sort (WebUI first) - diff --git a/manager/app/src/main/res/values-fr-rFR/strings.xml b/manager/app/src/main/res/values-fr-rFR/strings.xml deleted file mode 100644 index 4dc8be46..00000000 --- a/manager/app/src/main/res/values-fr-rFR/strings.xml +++ /dev/null @@ -1,236 +0,0 @@ - - - Un problème ? - Vous avez rencontré un bug ou avez un commentaire ? - Signalez-le dès que possible ! - Reportez sur GitHub - Contact par Telegram - https://github.com/KernelSU-Next/KernelSU-Next/issues - https://t.me/ksunext - Confirmer - KernelSU Next - Accueil - Pas installé - Cliquez pour installer - Le mode LKM est désormais obsolète ! - Installez un noyau GKI ou intégrez KernelSU Next a votre appareil. - En fonctionnement - Version : %d - Mises à jour : %d - La signature suivante de la v2 KernelSU Next est introuvable dans le noyau! [ !KSU_NEXT || != size/hash ] - Demandez à votre développeur de noyau d\'intégrer KernelSU Next ! - Version du noyau - Méthode d\'interception - Activer - Désactiver - Activé - Désactivé - Supporté - SuSFS: %s - Version de SuSFS - SuS SU - Version d\'Android - Version du gestionnaire - IBA - Statut de SELinux - Désactivé - Strict - Permissif - Inconnu - Super utilisateur - Échec de l\'activation du module : %s - Échec de la désactivation du module : %s - Aucun module installé - Module - Les modules suivants seront installés : %1$s - Trier (A → Z) - Trier (Z → A) - Trier (Léger → Lourd) - Trier (Lourd → Léger) - Désinstaller - Restaurer - Installer - Installer - Redémarrer - Désinstallé - Paramètres - Redémarrage rapide - Redémarrer en mode Recovery - Redémarrage dans le bootloader - Redémarrer pour télécharger - Redémarrer en mode EDL - À propos - Êtes-vous sûr de vouloir désinstaller le module %s \ ? - %s désinstallé - Échec de la désinstallation : %s - Voulez-vous vraiment restaurer le module %s? - %s restauré - Échec de la restauration : %s - Version - Auteur - ID - Code - Mise à jour Json - Vide - Activer les options de développeur - Afficher les paramètres cachés et les informations de débogage uniquement pour les développeurs. - Les modules sont indisponibles car OverlayFS est désactivé par le noyau ! - Actualiser - Afficher les applications système - Masquer les applis système - Exporter les logs - Mode sans échec - Redémarrer pour prendre effet - Les modules sont indisponibles en raison d\'un conflit avec Magisk ! - Monter le système - Magic Mount - OverlayFS - Indisponible - Utiliser OverlayFS - Basculer entre l\'utilisation de OverlayFS sur Magic Mount pour le système de montage de KernelSU Next. - Redémarrage nécessaire - Les changements prendront effet après le redémarrage du système. Voulez-vous redémarrer maintenant ? - Rétablir le module - Restaurer les modules de la sauvegarde récente. - Sauvegarder & Restaurer - Modules de sauvegarde - Restaurer la liste d\'autorisations - Restaurer la liste d\'autorisations à partir de la sauvegarde récente. - Sauvegarde de la liste d\'autorisations - Sauvegarde de la liste d\'autorisations configurée. - Attention - Cette fonctionnalité est encore en version bêta et en cours de développement. Veillez à sauvegarder vos modules avant de procéder. N\'utilisez cette fonctionnalité que si vous comprenez les risques potentiels. Procédez avec prudence. - Procéder - Annuler - Plus tard - Le correctif LKM repose sur des composants en source fermée. Voulez-vous continuer ? - 🔥 Prochaine version - https://github.com/KernelSU-Next/KernelSU-Next - Prochaine branche expérimentale. Consultez-la sur GitHub ! - ⚠️ Avertissement concernant le développement expérimental ! - 127.0.0.1 - KernelSU Next est une version non officielle qui fait toujours l\'objet d\'un développement expérimental actif. Elle est fournie telle quelle, sans garantie de stabilité, de performance ou de fiabilité. - • L\'utilisation est à vos risques et périls : des pannes, des comportements inattendus ou des problèmes de système peuvent survenir. - • Aucune garantie : les développeurs ne sont pas responsables des pertes de données, des dommages du système ou d\'autres conséquences résultant de son utilisation. - • À des fins de test uniquement : destiné aux utilisateurs qui comprennent les risques et sont à l\'aise avec le dépannage des problèmes. - Afficher le code source sur %1$s - App Profile - Défaut - Modèle - Personnaliser - Nom du profil - Espace de noms du montage - Hériter - Global - Individuel - Groupes - Fonctionnalités - Contexte SELinux - Démonter les modules - Échec de la modification du profil d\'application de %s - 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 ! - Démonter les modules - La valeur par défaut globale pour « Démonter les modules » dans le profil de l\'application. Si activé, cela supprimera toutes les modifications de modules sur le système pour les applications qui n\'ont pas de profil défini. - Cacher l\'interception kprobes - Cette option désactive l\'interception kprobes créé par ksu et, à la place, active l\'interception non-kpropes intégré, implémentant les mêmes fonctionnalités qui seraient appliquées à un noyau non GKI, qui ne supporte pas kprobe. - Activer cette option permettra à KernelSU Next de restaurer tous les fichiers modifiés par les modules pour cette application. - Domaine - Règles - Mettre à jour - Mettre à jour - Mise à jour - Téléchargement du module : %s - Démarrage du téléchargement de %s - La nouvelle version %s est disponible, appuyez ici pour mettre à jour. - Lancer - Fermer - Forcer l\'arrêt - Redémarrer - Mode AMOLED - Activez un thème noir pur et utile pour les écrans AMOLED pour réduire la fatigue oculaire et économiser la batterie. - Redémarrage nécessaire - L\'application doit redémarrer pour que ce changement prenne effet. - Échec de la mise à jour des règles SELinux pour %s - Impossible d\'autoriser l\'accès Super-utilisateur à %s - Journal de modifications - Modèles de profils d\'application - Gérer le modèle local et en ligne du profil de l\'application - Créer un modèle - Modifier le modèle - ID - ID du modèle invalide - Nom - Description - Sauvegarder - Supprimer - Voir le modèle - En lecture seule - L\'ID du modèle existe déjà ! - Importer/Exporter - Importer depuis le presse-papiers - Exporter dans le presse-papiers - Impossible de trouver un modèle local à exporter ! - Importé avec succès - Synchroniser les modèles en ligne - Échec de la sauvegarde du modèle - Le presse-papiers est vide ! - La récupération du journal des modifications a échoué : %s - Vérifier les mises à jour - Vérifier automatiquement les mises à jour à l\'ouverture de l\'application - Échec de l\'octroi des privilèges root ! - Action - WebUI - Ouvrir - Activer le débogage WebView - Peut être utilisé pour déboguer WebUI, n\'activez cette option que si nécessaire. - Installation directe (Recommandé) - Sélectionner un fichier - Installer dans un slot inactif (After OTA) - Votre appareil sera **FORCÉ** à démarrer sur l\'emplacement inactif actuel après un redémarrage ! -\nN\'utilisez cette option qu\'une fois la mise à jour OTA terminée. -\nContinuer ? - Suivant - L\'image de la partition %1$s est recommandée - Sélectionnez un KMI - Minimiser la taille de l\'image sparse - Redimensionne l\'image sparse 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 d\'origine - Désinstallez temporairement KernelSU Next, restaurez l\'état d\'origine après le prochain redémarrage. - Désinstallation complète et permanente de KernelSU Next (Root et de tous les modules). - Restaurer l\'image d\'usine d\'origine (si une sauvegarde existe), généralement utilisée avant OTA; si vous avez besoin de désinstaller KernelSU Next, veuillez utiliser \"Désinstaller définitivement\". - Flash en cours - Flash réussi - Échec du flash - LKM sélectionné : %s - Enregistrer les journaux (logs) - Journaux (logs) enregistrés - Partager les journaux (logs) - Désactiver la compatibilité su - Désactivez temporairement la capacité de n\'importe quelle application à obtenir les privilèges root via la commande su (Les processus root existants ne seront pas affectés). - Langue - Valeurs par défaut du système - Utiliser l\'ancienne interface utilisateur - Basculez vers le style de l\'interface utilisateur précédent. - Activer les bannières - Afficher les bannières d\'arrière-plan pour les modules. - Utiliser WebUI X - Utiliser WebUI X au lieu de WebUI, qui prend en charge d\'avantage d\'API. - Injecter Eruda dans WebUI X - Injecter une console de débogage dans WebUI X pour déboguer plus facilement. Le débogage web doit être activé. - Customisation - Développeur - SUCOMPAT DÉACTIVÉ - Zygisk requis - Injection de Zygisk - Super-utilisateur - Super-utilisateurs - Module - Modules - Sauvegarder les modules actuellement installés. - Trier (Activé en premier) - Trier (Action en premier) - Trier (WebUI en premier) - diff --git a/manager/app/src/main/res/values-hi-rIN/strings.xml b/manager/app/src/main/res/values-hi-rIN/strings.xml deleted file mode 100644 index f686c6ec..00000000 --- a/manager/app/src/main/res/values-hi-rIN/strings.xml +++ /dev/null @@ -1,236 +0,0 @@ - - - समस्या आ रही है? - क्या आपको कोई बग मिला या आपके पास कोई प्रतिक्रिया है? - जितनी जल्दी हो सके इसकी रिपोर्ट करें! - गिटहब पर रिपोर्ट करे - टेलीग्राम के माध्यम से संपर्क करें - https://github.com/KernelSU-Next/KernelSU-Next/issues - https://t.me/ksunext - पुष्टि करें - KernelSU Next - होम - इन्स्टॉल नहीं है - इंस्टॉल करने के लिए क्लिक करें - LKM मोड अब अप्रचलित हो गया है! - GKI कर्नल अधिष्ठापित करें या KernelSU Next को अपने डिवाइस पर इन्स्टॉल करें। - काम कर रहा है - वर्जन: %d - अपडेट्स: %d - KernelSU Next v2 सिग्नेचर कर्नेल में नहीं मिला! -[ !KSU_NEXT || != size/hash ] - अपने कर्नल डेवलपर से KernelSU नेक्स्ट को एकीकृत करने के लिए कहें! - कर्नल वर्जन - हुक मोड - सक्षम करें - अक्षम करें - सक्षम है - निष्क्रिय है - समर्थित - SuSFS: %s - SuSFS वर्जन - SuS SU - एंड्रॉयड वर्जन - मैनेजर वर्जन - - SELinux स्थिति - निष्क्रिय है - एनफोर्सिंग - परमिसीव - अज्ञात - सुपरयूजर - %s: मॉड्यूल चालू करने में विफल | - %s: मॉड्यूल बंद करने में विफल | - कोई मॉड्यूल इंस्टाल नहीं हुआ | - मॉड्यूल - निम्नलिखित मॉड्यूल इंस्टाल किए जाएंगे: %1$s - क्रमबद्ध करें (A → Z) - क्रमबद्ध करें (Z → A) - क्रमबद्ध करें (न्यूनतम → उच्चतम) - क्रमबद्ध करें (उच्चतम → न्यूनतम) - अनइंस्टॉल करें - पहले जैसा करें - इंस्टॉल करें - इंस्टॉल करें - रिबूट - अनइंस्टॉल किया गया - सेटिंग्स - सॉफ्ट रिबूट - रिकवरी में रिबूट करें - बूटलोडर में रिबूट करें - डाउनलोड में रिबूट करें - EDL मोड में रिबूट करें - इसके बारे में - क्या आप सच में %s मॉड्यूल को अनइंस्टॉल करना चाहते हैं? - %s अनइंस्टॉल सफल हुआ | - %s अनइंस्टल करने में असफल | - क्या आप सच में %s मॉड्यूल को पहले जैसा करना चाहते हैं? - %s मॉड्यूल को पहले जैसा किया गया | - पुनर्स्थापित करने में विफल: %s - वर्जन - रचयिता - आइडी - कोड - UpdateJson - खाली - डेवलपर विकल्प सक्षम करें - Show hidden settings and debug info relevant only for developers. - Modules are unavailable as OverlayFS is disabled by the kernel! - रीफ्रेश करें - सिस्टम ऐप्स दिखाएँ। - सिस्टम ऐप्स छिपाएं - लॉग एक्सपोर्ट करें - सुरक्षित मोड - प्रभाव में होने के लिए रीबूट करें - मॉड्यूल बंद कर दिए गए हैं क्योंकि यह मैजिसक के साथ टकरा रहे है! - माउंट सिस्टम - Magic Mount - OverlayFS - अनुपलब्ध - OverlayFS का उपयोग करें - Toggle between using OverlayFS over Magic Mount for KernelSU Next\'s mount system. - रिबूट करना जरूरी है! - सिस्टम रीस्टार्ट के बाद यह परिवर्तन प्रभावी होगा। क्या आप अभी रीबूट करना चाहते हैं? - Restore module - Restore modules from recent backup. - बैकअप & रिस्टोर - Backup module - Restore allowlist - Restore allowlist from recent backup. - Backup allowlist - Backup currently configured allowlist. - चेतावनी - This feature is still in beta and under development. Please ensure you backup your modules before proceeding. Only use this feature if you understand the potential risks. Proceed with caution. - आगे बढ़ें - रद्द करें - बाद में - The LKM patch relies on closed source components. Do you want to continue? - 🔥 Next build - https://github.com/KernelSU-Next/KernelSU-Next - Next experimental branch. Check it out on GitHub! - ⚠️ प्रायोगिक विकास की चेतावनी! - 127.0.0.1 - KernelSU Next एक गैर-आधिकारिक संस्करण है जो हमेशा सक्रिय प्रयोगात्मक विकास के अंतर्गत रहता है। इसे वैसे ही उपलब्ध कराया जाता है, जिसमें स्थिरता, प्रदर्शन या विश्वसनीयता की कोई गारंटी नहीं होती। - • अपने जोखिम पर उपयोग करें: क्रैश, अप्रत्याशित व्यवहार या सिस्टम समस्याएं हो सकती हैं। - • कोई वारंटी नहीं: डेवलपर्स इसके उपयोग से होने वाली किसी भी डेटा हानि, सिस्टम क्षति या अन्य परिणामों के लिए जिम्मेदार नहीं हैं। - • केवल परीक्षण के उद्देश्य से: उन उपयोगकर्ताओं के लिए जो जोखिमों को समझते हैं और समस्याओं का निवारण करने में सहज हैं। - %1$s पर स्रोत कोड देखें - ऐप प्रोफ़ाइल - डिफ़ॉल्ट - टेम्पलेट - कस्टम - प्रोफ़ाइल नाम - Namespace माउंट करें - इनहेरिट किया गया - ग्लोबल - विशिष्ट - ग्रुप्स - क्षमताएं - SELinux कॉन्टेक्स्ट - मॉड्यूल्स यूमाउंट करें - %s के लिए ऐप प्रोफ़ाइल अपडेट करने में विफल - मैनेजर के ठीक से काम करने के लिए वर्तमान KernelSU Next वर्जन %1$d बहुत कम है। कृपया वर्जन %2$d या उच्चतर में अपग्रेड करें! - मॉड्यूल्स यूमाउंट करें - ऐप प्रोफाइल में \"अनमाउंट मॉड्यूल\" के लिए ग्लोबल डिफ़ॉल्ट वैल्यू। यदि चालू किया गया है, तो यह एप्लीकेशंस के लिऐ सिस्टम के सभी मॉड्यूल मोडिफिकेशन को हटा देगा जिनकी प्रोफ़ाइल सेट नहीं है। - Kprobes हुक को छिपाएं - - यह विकल्प KSU द्वारा निर्मित kprobes हुक को निष्क्रिय कर देता है, तथा इसके स्थान पर एम्बेडेड non-kprobes हुक को सक्रिय कर देता है, तथा उसी कार्यक्षमता को क्रियान्वित करता है जो non-GKI कर्नल पर लागू होती है, जो kprobe का समर्थन नहीं करता है। - इस विकल्प को चालू करने से KernelSU Next इस एप्लिकेशन के लिए मॉड्यूल द्वारा किसी भी मोडिफाइड फ़ाइल को रिस्टोर कर सकता है। - डोमेन - नियम - अपडेट - अपडेट - अपडेट किया गया - %s मॉड्यूल डाउनलोड हो रहा है - %s की डाउनलोडिंग स्टार्ट करें - नया वर्जन: %s उपलब्ध है,अपग्रेड के लिए क्लिक करें| - लॉन्च - बंद - ज़बरदस्ती रोकें - रिस्टार्ट करें - AMOLED मोड - आंखों पर पड़ने वाले तनाव को कम करने और बैटरी बचाने के लिए AMOLED स्क्रीन के लिए उपयोगी काले रंग की थीम को सक्षम करें। - पुनः आरंभ की आवश्यकता है - इस बदलाव के लिए ऐप को दोबारा शुरू करना होगा। - %s के लिए SELinux नियमों को अपटेड करने में विफल - %s को सुपरयूज़र एक्सेस नहीं दिया जा सका - चेंजलॉग - ऐप प्रोफ़ाइल नमूना - ऐप प्रोफ़ाइल के स्थानीय और ऑनलाइन नमूनों का प्रबंधन करें - नया टेम्प्लेट बनाएं - टेम्पलेट एडिट करें - आइडी - अमान्य नमूना ID - नाम - विवरण - सेव - डिलीट - नमूना देखें - केवल पढ़ने के लिए - नमूना ID पहले से मौजूद है! - आयात/निर्यात - क्लिपबोर्ड से इम्पोर्ट करें - क्लिपबोर्ड को निर्यात करें - एक्सपोर्ट करने के लिए कोई स्थानीय नमूना नहीं मिला! - सफलतापूर्वक आयात किया गया - ऑनलाइन नमूने सिंक करें - नमूना सहेजने में विफल रहा - क्लिपबोर्ड खाली है! - Fetch changelog failed: %s - अपडेट के लिए चेक करें - ऐप खोलने पर अपने आप अपडेट जांचें - रूट एक्सेस देने में विफल! - एक्शन - WebUI - खोलें - - Can be used to debug WebUI. Please enable only when needed. - सीधा इंस्टॉल करें (सिफारिश की जाती है) - फाइल चुनें - निष्क्रिय स्लॉट में इंस्टॉल करें (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? - अगला - %1$s partition image की सिफारिश की जाती है - 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). - अनइंस्टॉल करें - कुछ समय के लिए हटाएं - हमेशा के लिए हटाएं - Restore stock image - KernelSU Next को अभी के लिए हटाया जाएगा, अगली बार डिवाइस चालू होने पर यह वापस आ जाएगा। - KernelSU Next को रूट और सभी मॉड्यूल के साथ पूरी तरह से हमेशा के लिए हटाया जा रहा है। - Restore the stock factory image (If a backup exists), usually used before OTA; if you need to uninstall KernelSU Next, please use \"Uninstall permanently\". - फ़्लैश हो रहा है - फ्लैश सफल रहा - फ्लैश असफल रहा - चुना गया LKM: %s - लॉग सहेजें - Logs saved - लॉग शेयर करें - SU compatibility अस्थायी रूप से बंद करें - Temporarily disable the ability of any app to gain root privileges via the ⁠su command (Existing root processes won\'t be affected). - भाषा - सिस्टम डिफ़ॉल्ट - Use legacy UI - Switch to the previous user interface style. - Enable banners - Show background banners for modules. - WebUI X का उपयोग करें - WebUI के बजाय WebUI X का उपयोग करें, जो अधिक API का समर्थन करता है। - Eruda को WebUI X में इंजेक्ट करें - डीबगिंग को आसान बनाने के लिए WebUI X में डीबग कंसोल इंजेक्ट करें। वेब डीबगिंग चालू होना आवश्यक है। - कस्टमाइजेशन - डेवलपर - SUCOMPAT DISABLED - Zygisk आवश्यक है! - Zygisk इंजेक्शन - सुपरयूजर - सुपरयूजरस - मॉड्यूल - मॉड्यूलस - Backup currently installed modules. - Sort (Enabled first) - Sort (Action first) - Sort (WebUI first) - diff --git a/manager/app/src/main/res/values-hu-rHU/strings.xml b/manager/app/src/main/res/values-hu-rHU/strings.xml deleted file mode 100644 index 88a9bb4d..00000000 --- a/manager/app/src/main/res/values-hu-rHU/strings.xml +++ /dev/null @@ -1,234 +0,0 @@ - - - Having trouble? - Encountered a bug or have feedback? - Report it as soon as possible! - Report on GitHub - Contact via Telegram - https://github.com/KernelSU-Next/KernelSU-Next/issues - https://t.me/ksunext - Confirm - KernelSU Next - Home - Not installed - Click to install - LKM mode is now deprecated! - Install GKI kernel or integrate KernelSU Next to your device. - Working - Version: %d - Updates: %d - KernelSU Next v2 signature not found in kernel! [ !KSU_NEXT || != size/hash ] - Ask your kernel developer to integrate KernelSU Next! - Kernel version - Hook mode - Enable - Disable - Enabled - Disabled - Supported - SuSFS: %s - SuSFS version - SuS SU - Android version - Manager version - ABI - SELinux status - Disabled - Enforcing - Permissive - Unknown - Superuser - Failed to enable module: %s - Failed to disable module: %s - No module installed - Module - The following modules will be installed: %1$s - Sort (A → Z) - Sort (Z → A) - Sort (Low → High) - Sort (High → Low) - Uninstall - Restore - Install - Install - Reboot - Uninstalled - Settings - Soft reboot - Reboot to Recovery - Reboot to Bootloader - Reboot to Download - Reboot to EDL - About - Are you sure you want to uninstall module %s? - %s uninstalled - Failed to uninstall: %s - Are you sure you want to restore module %s? - %s restored - Failed to restore: %s - Version - Author - ID - Code - UpdateJson - Empty - Enable developer options - Show hidden settings and debug info relevant only for developers. - Modules are unavailable as OverlayFS is disabled by the kernel! - Refresh - Show system apps - Hide system apps - Export logs - Safe mode - Reboot to take effect - Modules are unavailable due to a conflict with Magisk! - Mount system - Magic Mount - OverlayFS - Unavailable - Use OverlayFS - Toggle between using OverlayFS over Magic Mount for KernelSU Next\'s mount system. - Reboot required - The changes will take effect after the system restart. Do you want to reboot now? - Restore module - Restore modules from recent backup. - Backup & Restore - Backup module - Restore allowlist - Restore allowlist from recent backup. - Backup allowlist - Backup currently configured allowlist. - Warning - This feature is still in beta and under development. Please ensure you backup your modules before proceeding. Only use this feature if you understand the potential risks. Proceed with caution. - Proceed - Cancel - Later - The LKM patch relies on closed source components. Do you want to continue? - 🔥 Next build - https://github.com/KernelSU-Next/KernelSU-Next - Next experimental branch. Check it out on GitHub! - ⚠️ Experimental development warning! - 127.0.0.1 - KernelSU Next is a non-official version that is always under active experimental development. It\'s provided as-is, with no guarantees of stability, performance, or reliability. - • Use at your own risk: crashes, unexpected behavior, or system issues may occur. - • No warranty: the developers aren\'t responsible for any data loss, system damage, or other consequences resulting from its use. - • For testing purposes only: intended for users who understand the risks and are comfortable troubleshooting issues. - View source code at %1$s - App Profile - Default - Template - Custom - Profile name - Mount namespace - Inherited - Global - Individual - Groups - Capabilities - SELinux context - Umount modules - Failed to update App Profile for %s - The current KernelSU Next version %1$d is too low for the manager to work properly. Please upgrade to version %2$d or higher! - Umount modules - 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. - Hide kprobes hook - This option disables the kprobes hook created by ksu and, instead, activates the embedded non-kprobes hook, implementing the same functionality that would be applied to a non-GKI kernel, which doesn\'t support kprobe. - Enabling this option will allow KernelSU Next to restore any modified files by the modules for this app. - Domain - Rules - Update - Update - Updated - Downloading module: %s - Start downloading: %s - New version %s is available, click to upgrade. - Launch - Close - Force stop - Restart - AMOLED mode - Enable a pure black theme useful for AMOLED screens to reduce eye strain and save battery. - Restart required - The app needs to restart for this change to take effect. - Failed to update SELinux rules for %s - Couldn\'t grant Superuser access to %s - Changelog - App Profile template - Manage local and online template of App Profile - Create template - Edit template - ID - Invalid template ID - Name - Description - Save - Delete - View template - Read only - Template ID already exists! - Import/Export - Import from clipboard - Export to clipboard - Cannot find local template to export! - Imported successfully - Sync online templates - Failed to save template - Clipboard is empty! - Fetch changelog failed: %s - Check for updates - Automatically check for updates when opening the app - Failed to grant root! - Action - WebUI - Open - 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) - 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). - Uninstall - Uninstall temporarily - Uninstall permanently - Restore stock image - Temporarily uninstall KernelSU Next, restore to original state after next reboot. - Uninstalling KernelSU Next (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 Next, please use \"Uninstall permanently\". - Flashing - Flash success - Flash failed - Selected LKM: %s - Save logs - Logs saved - Share logs - Disable su compatibility - Temporarily disable the ability of any app to gain root privileges via the ⁠su command (Existing root processes won\'t be affected). - Language - System default - Use legacy UI - Switch to the previous user interface style. - Enable banners - Show background banners for modules. - Use WebUI X - Use WebUI X instead of WebUI, which supports more APIs. - Inject Eruda into WebUI X - Inject a debug console into WebUI X to make debugging easier. Requires web debugging to be on. - Customization - Developer - SUCOMPAT DISABLED - Zygisk required - Zygisk injection - Superuser - Superusers - Module - Modules - Backup currently installed modules. - Sort (Enabled first) - Sort (Action first) - Sort (WebUI first) - diff --git a/manager/app/src/main/res/values-in-rID/strings.xml b/manager/app/src/main/res/values-in-rID/strings.xml deleted file mode 100644 index bcfbd212..00000000 --- a/manager/app/src/main/res/values-in-rID/strings.xml +++ /dev/null @@ -1,235 +0,0 @@ - - - Ada kendala? - Menemukan bug atau memiliki umpan balik? - Laporkan sesegera mungkin! - Laporkan di GitHub - Hubungi melalui Telegram - https://github.com/KernelSU-Next/KernelSU-Next/issues - https://t.me/ksunext - Konfirmasi - KernelSU Next - Beranda - Tidak terpasang - Klik untuk memasang - Mode LKM sekarang sudah tidak digunakan lagi! - Pasang kernel GKI atau integrasikan KernelSU Next ke perangkat anda. - Berfungsi - Versi: %d - Pembaruan: %d - Tandatangan KernelSU Next v2 tidak ditemukan pada kernel! [ !KSU_NEXT || != size/hash ] - Minta pengembang kernel Anda untuk mengintegrasikan KernelSU Next! - Versi kernel - Mode hook - Aktifkan - Nonaktifkan - Diaktifkan - Nonaktif - Didukung - SuSFS: %s - Versi SuSFS - SuS SU - Versi android - Versi manajer - ABI - Status SELinu - Nonaktif - Enforcing - Permissive - Tidak diketahui - Superuser - Gagal mengaktifkan modul: %s - Gagal menonaktifkan modul: %s - Tidak ada modul yang terpasang - Modul - Modul berikut akan dipasang: %1$s - Urutkan (A → Z) - Urutkan (Z → A) - Urutkan (Kecil → Besar) - Urutkan (Besar → Kecil) - Hapus - Pulihkan - Pasang - Pasang - Mulai ulang - Dihapus - Pengaturan - Mulai ulang halus - Mulai ulang ke Recovery - Mulai ulang ke Bootloader - Mulai ulang ke Download - Mulai ulang ke EDL - Tentang - Anda yakin akan menghapus modul %s? - %s berhasil dihapus - Gagal menghapus: %s - Anda yakin ingin memulihkan modul %s? - %s dipulihkan - Gagal memulihkan: %s - Versi - Pengembang - ID - Kode - UpdateJson - Kosong - Aktifkan opsi pengembang - Menampilkan pengaturan tersembunyi dan info debug yang hanya relevan bagi pengembang. - Modul tidak tersedia karena OverlayFS dinonaktifkan oleh kernel! - Muat ulang - Tampilkan aplikasi sistem - Sembunyikan aplikasi sistem - Ekspor log - Mode aman - Mulai ulang untuk menerapkan perubahan - Modul tidak tersedia karena konflik dengan Magisk! - Sistem mount - Magic Mount - OverlayFS - Tidak tersedia - Gunakan OverlayFS - Pilih antara OverlayFS atau Magic Mount untuk sistem mount KernelSU Next. - Mulai ulang diperlukan - Perubahan akan berlaku setelah sistem dinyalakan ulang. Apakah Anda ingin menyalakan ulang sekarang? - Pulihkan modul - Pulihkan modul dari cadangan terkini. - Pencadangan & Pemulihan - Cadangkan modul - Pulihkan daftar izin - Pulihkan daftar izin dari cadangan terkini. - Cadangkan daftar izin - Cadangkan daftar izin yang dikonfigurasi saat ini. - Peringatan - Fitur ini masih dalam tahap beta dan pengembangan. Pastikan anda mencadangkan modul sebelum melanjutkan. Gunakan fitur ini hanya jika anda memahami potensi risikonya. Lanjutkan dengan hati-hati. - Lanjutkan - Batalkan - Nanti - Patch LKM bergantung pada komponen dengan sumber tertutup. Apakah Anda ingin melanjutkan? - 🔥 Build Next - https://github.com/KernelSU-Next/KernelSU-Next - Next cabang eksperimental. Lihat di GitHub! - ⚠️ Peringatan pengembangan eksperimental! - 127.0.0.1 - KernelSU Next adalah versi non-resmi yang selalu dalam tahap pengembangan eksperimental aktif. Versi ini disediakan apa adanya, tanpa jaminan stabilitas, kinerja, atau keandalan. - • Gunakan dengan risiko anda sendiri: kerusakan, perilaku aneh, atau kesalahan sistem dapat terjadi. - • Tidak bergaransi: pengembang tidak bertanggung jawab atas segala kehilangan data, kerusakan sistem, atau lainnya dalam penggunaan. - • Hanya untuk pengujian: ditujukan bagi pengguna yang memahami risiko dan siap untuk memecahkan masalah. - Lihat sumber kode di %1$s - Profil Aplikasi - Bawaan - Templat - Kustom - Nama profil - Mount namespace - Diwariskan - Global - Individual - Grup - Kemampuan - Konteks SELinux - Umount modul - Gagal memperbarui Profil Aplikasi untuk %s - Versi KernelSU Next saat ini %1$d sangat rendah untuk manager agar bekerja dengan baik. Mohon perbarui ke versi %2$d atau lebih tinggi! - Umount modul - Menggunakan \"Umount modul\" secara universal pada Profil Aplikasi. Jika diaktifkan, ini akan menghapus semua modifikasi modul pada sistem untuk aplikasi yang tidak memiliki set profil. - Sembunyikan hook kprobes - Opsi ini akan menonaktifkan hook kprobe yang digunakan oleh KSU, dan sebagai gantinya akan mengaktifkan hook non-kprobe yang tertanam, yang menerapkan fungsionalitas yang sama, ditujukan untuk kernel non-GKI yang tidak mendukung kprobe. - Mengaktifkan opsi ini akan memungkinkan KernelSU Next untuk memulihkan berkas yang termodifikasi oleh modul untuk aplikasi ini. - Domain - Peraturan - Perbarui - Pembaruan - Diperbarui - Mengunduh modul: %s - Mulai mengunduh: %s - Versi baru %s tersedia, klik untuk memperbaharui. - Luncurkan - Tutup - Paksa berhenti - Mulai ulang - Mode AMOLED - Aktifkan tema hitam pekat yang berguna pada layar AMOLED untuk mengurangi ketegangan mata dan menghemat baterai. - Mulai ulang diperlukan - Aplikasi perlu dimulai ulang agar perubahan ini diterapkan. - Gagal memperbarui aturan SELinux untuk %s - Tidak dapat memberikan akses Superuser untuk %s - Catatan Perubahan - Templat Profil Aplikasi - Kelola templat Profil Aplikasi lokal dan daring - Buat templat - Edit templat - ID - ID templat tidak valid - Nama - Deskripsi - Simpan - Hapus - Lihat templat - Hanya baca - ID templat sudah ada! - Impor/Ekspor - Impor dari papan klip - Ekspor ke papan klip - Tidak ditemukan templat lokal untuk diekspor! - Berhasil diimpor - Sinkronkan templat daring - Gagal menyimpan templat - Papan klip kosong! - Gagal mengambil catatan perubahan: %s - Periksa pembaruan - Periksa pembaruan secara otomatis saat membuka aplikasi - Gagal memberikan akses root! - Aksi - WebUI - Buka - Aktifkan debugging WebView - Dapat digunakan untuk men-debug WebUI. Harap aktifkan hanya bila diperlukan. - Pasang langsung (direkomendasikan) - Pilih berkas - Instal ke slot nonaktif (setelah OTA) - Perangkatmu akan **DIPAKSA** untuk boot ke slot tidak aktif saat ini setelah reboot!\nGunakan hanya setelah proses OTA selesai. -\nLanjutkan? - Selanjutnya - %1$s partisi image direkomendasikan - Pilih KMI - Minimalkan sparse image - Ubah ukuran sparse image tempat modul berada ke ukuran sebenarnya. Harap diperhatikan bahwa hal ini dapat menyebabkan modul bekerja secara tidak normal, harap gunakan hanya bila diperlukan (misalnya untuk pencadangan). - Hapus - Hapus sementara - Hapus permanen - Pulihkan image bawaan - Hapus sementara KernelSU Next, mengembalikan ke kondisi asal setelah reboot berikutnya. - Hapus permanen KernelSU Next (root dan modul). - Mengembalikan image bawaan ROM (jika cadangan tersedia), umumnya dilakukan sebelum OTA; jika ingin menghapus KernelSU Next, gunakan fungsi \"Hapus permanen\". - Pasang - Pemasangan Berhasil - Pemasangan gagal - LKM dipilih: %s - Simpan log - Log disimpan - Bagikan log - Nonaktifkan kompatibilitas su - Nonaktifkan sementara kemampuan aplikasi apa pun untuk mendapatkan hak akses root melalui perintah ⁠su (Proses root yang ada tidak akan terpengaruh). - Bahasa - Sistem bawaan - Gunakan tampilan lama - Beralih ke gaya antarmuka yang sebelumnya. - Aktifkan banner - Tampilkan banner latar belakang untuk modul. - Gunakan WebUI X - Gunakan WebUI X dibandingkan menggunakan WebUI, yang mendukung lebih banyak API. - Integrasikan Eruda ke WebUI X - Mengintegrasikan konsol debug ke WebUI X untuk mempermudah proses debug. Web debugging harus aktif. - Kustomisasi - Pengembang - SUCOMPAT DINONAKTIFKAN - Zygisk diperlukan - Injeksi Zygisk - Superuser - Superuser - Modul - Modul - Cadangkan modul yang terinstal saat ini. - Urutkan (Diaktifkan lebih dulu) - Urutkan (Aksi lebih dulu) - Urutkan (WebUI lebih dulu) - diff --git a/manager/app/src/main/res/values-it-rIT/strings.xml b/manager/app/src/main/res/values-it-rIT/strings.xml deleted file mode 100644 index 45fc7474..00000000 --- a/manager/app/src/main/res/values-it-rIT/strings.xml +++ /dev/null @@ -1,234 +0,0 @@ - - - Stai riscontrando problemi? - Hai riscontrato un bug o vuoi fornirci feedback? - Segnalacelo il prima possibile! - Segnala su GitHub - Contatto tramite Telegram - https://github.com/KernelSU-Next/KernelSU-Next/issues - https://t.me/ksunext - Conferma - KernelSU Next - Casa - Non installato - Clicca per installare - La modalità LKM è ora obsoleta! - Installa il kernel GKI o integra KernelSU Next sul tuo dispositivo. - In esecuzione - Versione: %d - Aggiornamenti: %d - KernelSU Next firma v2 non trovata nel kernel! [ !KSU_NEXT || != size/hash ] - Chiedi allo sviluppatore del kernel d\'integrare KernelSU Next! - Versione kernel - Modalità Hook - Abilita - Disabilita - Abilitato - Disabilitato - Supportato - SuSFS: %s - Versione SuSFS - SuS SU - Versione Android - Versione Manager - ABI - Stato SELinux - Disabilitato - Enforcing - Permissive - Sconosciuto - Superutente - Impossibile abilitare il modulo: %s - Impossibile disabilitare il modulo: %s - Nessun modulo installato - Modulo - Verranno installati i seguenti moduli: %1$s - Ordina (A → Z) - Ordina (Z → A) - Ordina (Basso→ Alto) - Ordina (Alto → Basso) - Disinstalla - Ripristina - Installa - Installa - Riavvia - Disinstallato - Impostazioni - Riavvio veloce - Riavvia in Recovery - Riavvia in Bootloader - Riavvia in Download - Riavvia in EDL - Info - Sei sicuro di voler disinstallare il modulo %s? - %s disinstallato - Impossibile disinstallare: %s - Sei sicuro di voler ripristinare il modulo %s? - %s ripristinato - Ripristino fallito: %s - Versione - Autore - ID - Codice - UpdateJson - Vuoto - Attiva le opzioni sviluppatore - Mostra impostazioni nascoste e informazioni di debug rilevanti solo per gli sviluppatori. - I moduli non sono disponibili perché OverlayFS è disattivato nel kernel! - Ricarica - Mostra app di sistema - Nascondi app di sistema - Esporta log - Modalità provvisoria - Riavvia per applicare le modifiche - I moduli non sono disponibili a causa di un conflitto con Magisk! - Sistema di montaggio - Magic Mount - OverlayFS - Non disponibile - Usa OverlayFS - Alterna l\'utilizzo di OverlayFS su Magic Mount per il sistema di montaggio di KernelSU Next. - Riavvio richiesto - Le modifiche verranno applicate dopo il riavvio del sistema. Vuoi riavviare ora? - Ripristina moduli - Ripristina i moduli da un backup. - Backup & Ripristina - Backup moduli - Ripristina la lista consentita - Ripristina la lista consentita da un backup. - Backup lista consentita - Esegue il backup della lista consentita attualmente configurata. - Avviso - Questa funzione è ancora in beta e in fase di sviluppo. Assicurati di eseguire il backup dei moduli prima di procedere. Usa questa funzione solo se capisci i potenziali rischi. Procedere con cautela. - Procedi - Annulla - Più tardi - La patch LKM si basa su codici vuoi continuare? - 🔥 Prossima build - https://github.com/KernelSU-Next/KernelSU-Next - Prossimo branch sperimentale. Provalo su GitHub! - ⚠️ Avviso di sviluppo sperimentale! - 127.0.0.1 - KernelSU Next è una versione non ufficiale che è sempre sotto sviluppo sperimentale. È proposto allo stato attuale, con nessuna garanzia di stabilità, prestazioni, o affidabilità. - • Utilizzalo al tuo rischio: potrebbero verificarsi arresti anomali, comportamenti imprevisti o problemi al sistema. - • Nessuna garanzia: gli sviluppatori non sono responsabili per eventuali perdite di dati, danni al sistema o altre conseguenze derivanti dall\'utilizzo. - • Solo a scopo di test: destinato a utenti che comprendono i rischi e sono in grado di risolvere eventuali problemi. - Mostra il codice sorgente a %1$s - Profilo App - Default - Modello - Personalizzato - Nome profilo - Monta namespace - Ereditato - Globale - Individuale - Gruppi - Capacità - Contesto SELinux - Smonta moduli - Aggiornamento App Profile fallito per %s - La versione attualmente installata di KernelSU Next %1$d è troppo vecchia ed il manager non può funzionare correttamente. Si prega di aggiornare alla versione %2$d o successiva! - Smonta moduli - Il valore predefinito globale per \"Smonta moduli\" nel Profilo App. Se abilitato, rimuoverà tutte le modifiche ai moduli nel sistema per le app che non hanno un profilo impostato. - Nascondi kprobes hook - Questa opzione disabilita l\'hook kprobes creato da ksu e, al suo posto, attiva l\'hook non-kprobes incorporato, implementando la stessa funzionalità che verrebbe applicata a un kernel non-GKI, che non supporta kprobe. - Abilitando questa opzione, KernelSU Next potrà ripristinare tutti i file modificati dai moduli per questa app. - Dominio - Regole - Aggiorna - Aggiornamento - Aggiornato - Download modulo: %s - Inizio download: %s - Nuova versione %s disponibile, tocca per aggiornare. - Avvia - Chiudi - Arresto forzato - Riavvia - Modalità AMOLED - Abilita un tema nero puro, utile per gli schermi AMOLED, per ridurre l\'affaticamento degli occhi e risparmiare batteria. - Riavvio richiesto - Affinché la modifica abbia effetto, è necessario riavviare l\'app. - Impossibile aggiornare le regole SELinux per %s - Impossibile concedere i permessi root a %s - Changelog - Modelli App Profile - Gestisci i modelli locali e online di App Profile - Crea modello - Modifica modello - ID - ID modello non valido - Nome - Descrizione - Salva - Elimina - Visualizza modello - Sola lettura - ID modello già esistente! - Importa/Esporta - Importa dagli appunti - Esporta negli appunti - Impossibile trovare un modello locale da esportare! - Importato con successo - Sincronizza modelli online - Impossibile salvare il modello - Gli appunti sono vuoti! - Recupero del changelog non riuscito: %s - Controlla aggiornamenti - Cerca automaticamente gli aggiornamenti all\'apertura dell\'applicazione. - Impossibile concedere i permessi root! - Azione - WebUI - Apri - Attiva il debugging WebView - Può essere utlizzato per il debug di WebUI. Attiva quest\'opzione solo quando è necessario. - Installazione diretta (consigliato) - Seleziona un file - Installa nello slot inattivo (dopo un aggiornamento OTA) - Dopo il riavvio, il dispositivo sarà FORZATO ad avviarsi nello slot attualmente inattivo!\nUtilizza questa opzione solo dopo aver completato l\'aggiornamento OTA.\nVuoi continuare? - - Immagine partizione %1$s consigliata - Seleziona KMI - Minimizza l\'immagine sparsa - Ridimensiona l\'immagine sparsa in cui si trova il modulo alla sua dimensione effettiva. Nota che ciò potrebbe causare un funzionamento anomalo del modulo, quindi utilizzalo solo se necessario (ad esempio per eseguire un backup). - Disinstalla - Disinstalla temporaneamente - Disinstalla permanentemente - Ripristina l\'immagine originale - Disinstalla temporaneamente KernelSU Next e recupera il suo stato originale dopo il prossimo riavvio. - Disinstalla KernelSU Next (i permessi di root e tutti i moduli) completamente e permanentemente. - Ripristina l\'immagine del sistema originale (se un backup esiste), che viene solitamente utilizzata prima di un aggiornamento OTA; se vuoi disinstallare KernelSU Next, utlizza \"Disinstalla permanentemente\". - Installazione in corso - Installazione completata - Installazione fallita - LKM selezionato: %s - Salva i log - Log salvati - Condividi i log - Disattiva la compatibilità su - Disattiva temporaneamente la capacità di ogni applicazione di ottenere i permessi di root tramite il comando su (le sessioni di root esistenti non subiranno modifiche). - Lingua - Default di sistema - Usa lo stile d\'interfaccia precedente - Passa allo stile d\'interfaccia utente precedente. - Attiva i banner - Mostra uno sfondo dietro ai moduli. - Utilizza WebUI X - Utilizza WebUI X al posto di WebUI, che supporta più API. - Inietta Eruda in WebUI X - Inietta una console di debug in WebUI X per facilitare il debugging. Richiede il web debugging per funzionare. - Personalizzazione - Sviluppatore - SUCOMPAT DISABILITATO - Zygisk richiesto - Iniezione Zygisk - Superutente - Superutenti - Modulo - Moduli - Esegue il backup dei moduli attualmente esistenti. - Ordina (prima Abilitato) - Ordina (prima Azione) - Ordina (prima WebUI) - diff --git a/manager/app/src/main/res/values-ja-rJP/strings.xml b/manager/app/src/main/res/values-ja-rJP/strings.xml deleted file mode 100644 index e0c0696c..00000000 --- a/manager/app/src/main/res/values-ja-rJP/strings.xml +++ /dev/null @@ -1,234 +0,0 @@ - - - 問題が発生しましたか? - バグに遭遇しましたか?改善案を思い付きましたか? - なるべく早くご報告をお願いします! - GitHub で報告 - Telegram で問い合わせる - https://github.com/KernelSU-Next/KernelSU-Next/issues - https://t.me/ksunext - 確認 - KernelSU Next - ホーム - 未インストール - タップでインストール - LKMモードは現在非推奨です! - GKI カーネルをインストールするか、お使いのデバイスに KernelSU Next を統合してください。 - 動作中 - バージョン: %d - アップデート: %d - カーネル内に KernelSU Next v2 の署名がありません! [ !KSU_NEXT || != size/hash ] - カーネルの開発者に KernelSU Next を統合するように依頼してください! - カーネルバージョン - フックモード - 有効化 - 無効化 - 有効 - 無効 - 対応済み - SuSFS: %s - SuSFS バージョン - SuS SU - Android バージョン - マネージャーバージョン - ABI - SELinux の状態 - 無効 - Enforcing - Permissive - 不明 - スーパーユーザー - モジュールの有効化に失敗: %s - モジュールの無効化に失敗: %s - モジュールがインストールされていません - モジュール - %1$s がインストールされます - 並び替え (A→Z) - 並び替え (Z→A) - 並べ替え (サイズ小→大) - 並べ替え (サイズ大→小) - アンインストール - 復元 - インストール - インストール - 再起動 - アンインストール済み - 設定 - ソフト再起動 - Recovery へ再起動 - Bootloader へ再起動 - Download へ再起動 - EDL へ再起動 - このプロジェクトについて - モジュール %s をアンインストールしてもよろしいですか? - %s をアンインストールしました - %s のアンインストールに失敗しました - モジュール %s を復元してもよろしいですか? - %s を復元しました - %s の復元に失敗しました - バージョン - 作者 - ID - コード - UpdateJson - - 開発者向けオプションを有効にする - 開発者向けの非表示オプションとデバッグ情報を表示します。 - OverlayFS はカーネルによって無効化されているため、モジュールは使用できません! - 更新 - システムアプリを表示 - システムアプリを非表示 - ログをエクスポート - セーフモード - 再起動後に適用されます - Magisk と競合しているため、モジュールは利用できません! - マウントシステム - Magic Mount - OverlayFS - 利用不可 - OverlayFS を使用 - KernelSU Next のマウントシステムで、Magic Mount の代わりに OverlayFS の使用に切り替えます。 - 再起動が必要です - 変更はシステムの再起動後に有効化されます。今すぐに再起動しますか? - モジュールを復元 - 最近のバックアップからモジュールを復元します。 - バックアップ&復元 - モジュールをバックアップ - 許可リストを復元 - 最近のバックアップから許可リストを復元します。 - 許可リストをバックアップ - バックアップは現在許可リストを設定しています。 - 警告 - この機能はまだベータ版であり、開発中です。続行する前に必ずモジュールをバックアップしてください。潜在的なリスクを理解している場合でのみ、この機能を使用してください。注意して続行してください。 - 続行 - キャンセル - 後で - LKM パッチはクローズドソースのコンポーネントに依存しています。続行しますか? - 🔥 Next build - https://github.com/KernelSU-Next/KernelSU-Next - 次の試験的なブランチです。GitHub で確認してください! - ⚠️ 開発版の警告! - 127.0.0.1 - KernelSU Next は非公式なバージョンであり、常に積極的に試験的な開発が行われています。現状有姿で提供されるため、安定性やパフォーマンス、信頼性は保証されません。 - • 自己責任でご利用ください。クラッシュや予期しない動作、システムの問題が発生する可能性があります。 - • 保証はありません。開発者は使用によって生じるデータの損失やシステムの破損、その他の問題についての責任を一切負いません。 - • テスト目的のみ。リスクを理解し、問題のトラブルシューティングに慣れているユーザーを対象としています。 - %1$s でソースコードを見る - アプリプロファイル - デフォルト - テンプレート - カスタム - プロファイル名 - マウント名前空間 - 継承する - グローバル - 個別 - グループ - 機能 - SELinux コンテキスト - モジュールをアンマウント - %s のアプリプロファイルの更新に失敗しました - 現在のバージョン %1$d の KernelSU Next は、マネージャーの正常動作の保証ができません。バージョン %2$d またはそれ以降に更新してください! - モジュールをアンマウント - アプリプロファイルの「モジュールをアンマウント」のデフォルト値を設定します。有効にすると、プロファイルが設定されていないアプリでの、全てのモジュールのシステムに対する変更が削除されます。 - kprobes フックを隠す - このオプションは ksu によって作成された kprobes フックを無効にし、埋め込まれた kprobes フックを代わりに有効にします。 kprobe をサポートしていない非 GKI カーネルに適用されるものと同じ機能を実装します。 - このオプションを有効にすると、KernelSU Next はこのアプリのモジュールによって変更されたファイルを復元することができます。 - ドメイン - ルール - 更新 - 更新 - 更新されました - モジュールをダウンロード中: %s - ダウンロードを開始: %s - 新しいバージョン %s が利用可能です。タップで更新します。 - 起動 - 閉じる - 強制停止 - 再起動 - AMOLED モード - AMOLED スクリーンに効果的である真っ黒なテーマを有効にすると、目の負担の軽減とバッテリーの節約に繋がります。 - 再起動が必要です - この変更を反映させるにはアプリを再起動する必要があります。 - SELinux ルールの更新に失敗しました: %s - %s へのスーパーユーザーアクセスを許可できませんでした - 更新履歴 - アプリプロファイルのテンプレート - アプリプロファイルのローカルまたはオンラインテンプレートを管理します - テンプレートを作成 - テンプレートを編集 - ID - 無効なテンプレート ID - 名前 - 説明 - 保存 - 削除 - テンプレートの表示 - 読み取り専用 - そのテンプレート ID は既に存在します! - インポート/エクスポート - クリップボードからインポート - クリップボードにエクスポート - エクスポートするローカル テンプレートが見つかりません! - インポートに成功しました - オンラインテンプレートを同期 - テンプレートの保存に失敗しました - クリップボードは空です! - 更新履歴の取得に失敗: %s - アップデートの確認 - アプリの起動時に更新を自動で確認します - Root 権限の付与に失敗しました! - 実行 - WebUI - 開く - WebView デバッグを有効化する - WebUI のデバッグが行えます。必要な時のみ有効化してください。 - 直接インストール (推奨) - ファイルを選択 - 非アクティブなスロットにインストール (OTA 後) - 再起動後、デバイスは**強制的に**、現在非アクティブなスロットから起動します。\nこのオプションは、OTA が完了した後にのみ使用してください。\n続行しますか? - 次へ - %1$s パーティション イメージが推奨されます - KMI を選択 - スパースイメージを最小化 - モジュールが配置されているスパースイメージのサイズを実際のサイズに変更します。モジュールが正常に動作しなくなる可能性がありますので、必要な場合 (バックアップなど) にのみご使用ください。 - アンインストール - 一時的にアンインストール - 完全にアンインストール - ストックイメージを復元 - KernelSU Next を一時的にアンインストールして、次回の再起動後に元の状態に戻します。 - KernelSU Next (root およびすべてのモジュール) を完全で恒久的にアンインストールします。 - OTA の前に使用されるストックファクトリーイメージ (バックアップが存在する場合) を復元します。KernelSU Next をアンインストールする必要がある場合は「完全アンインストール」を使用してください。 - フラッシュ - フラッシュに成功 - フラッシュに失敗 - 選択した LKM: %s - ログを保存 - ログを保存しました - ログを共有 - su 互換性を無効にする - su コマンドを通じてアプリがrootに昇格する機能を一時的に無効化します (既存のrootプロセスに影響はありません)。 - 言語 - システムのデフォルト - レガシー UI を使用 - 以前のユーザーインターフェイスのスタイルに切り替えます。 - バナーを有効化 - モジュールの背景バナーを表示します。 - WebUI X を使用 - WebUI の代わりにより多くの API をサポートする WebUI X を使用します。 - WebUI に Eruda をインジェクトする - デバッグを容易にするために WebUI X にデバッグコンソールを挿入します。Web デバッグが ON になっている必要があります。 - カスタマイズ - 開発者 - SUCOMPAT 無効 - Zygisk 必須 - Zygisk インジェクション - スーパーユーザー - スーパーユーザー - モジュール - モジュール - 現在インストールされているモジュールをバックアップします。 - 並べ替え (有効を優先) - 並べ替え (実行可能を優先) - 並べ替え (WebUIを優先) - diff --git a/manager/app/src/main/res/values-ko-rKR/strings.xml b/manager/app/src/main/res/values-ko-rKR/strings.xml deleted file mode 100644 index fadc9d28..00000000 --- a/manager/app/src/main/res/values-ko-rKR/strings.xml +++ /dev/null @@ -1,234 +0,0 @@ - - - 문제가 있나요? - 버그를 발견하셨거나 의견이 있으신가요? - 바로 알려주세요! - Github에 신고 - Telegram으로 문의 - https://github.com/KernelSU-Next/KernelSU-Next/issues - https://t.me/ksunext - 확인 - KernelSU Next - - 설치되지 않음 - 눌러서 설치하기 - 이제 LKM 모드는 지원되지 않습니다! - GKI Kernel 혹은 KernelSU Next를 설치하세요. - 처리 중 - Version: %d - Updates: %d - Kernel에 KernelSU Next v2 signature가 없습니다! !KSU_NEXT || != size/hash ] - Kernel 개발자에게 KernelSU Next를 지원하도록 요청하세요! - Kernel version - Hook mode - 활성화 - 비활성화 - 활성화됨 - 비활성화됨 - 지원됨 - SuSFS: %s - SuSFS version - SuS SU - Android version - Manager version - ABI - SELinux 상태 - 비활성화됨 - Enforcing - Permissive - 알 수 없음 - Superuser - 모듈 활성화 실패: %s - 모듈 비활성화 실패: %s - 설치된 모듈 없음 - 모듈 - 다음과 같은 모듈이 설치됩니다: %1$s - 정렬 (A → Z) - 정렬 (Z → A) - 오름차순 정렬 - 내림차순 정렬 - 삭제 - 복구 - 설치 - 설치 - 재시작 - 삭제됨 - 설정 - 빠른 재시작 - 복구 모드로 다시 시작 - 부트로더로 다시 시작 - 다운로드 모드로 다시 시작 - EDL 모드로 다시 시작 - 정보 - %s 모듈을 삭제할까요? - %s 모듈 삭제됨 - 모듈 삭제 실패: %s - %s 모듈을 복구할까요? - %s 복구됨 - 복구 실패: %s - Version - 만든 사람 - ID - Code - UpdateJson - Empty - 개발자 옵션 활성화 - 숨겨진 설정과 개발자용 디버그 정보를 표시 - Kernel에 의해 OverlayFS가 비활성화되어 모듈을 사용할 수 없습니다! - 새로고침 - 시스템 앱 표시 - 시스템 앱 숨기기 - 로그 내보내기 - 안전 모드 - 다시 시작하여 변경 사항 적용 - Magisk와의 충돌로 인해 모듈을 사용할 수 없습니다! - Mount system - Magic Mount - OverlayFS - 사용할 수 없음 - OverlayFS 사용 - KernelSU Next 마운트 시스템에 Magic Mount대신 OverlayFS 사용 - 재시작 필요 - 변경사항은 시스템 재시작 이후 적용됩니다. 지금 재시작 할까요? - 모듈 복구 - 최근 백업에서 모듈 백업 - 백업 & 복원 - 모듈 백업 - Allowlist 복구 - 최근 백업에서 Allowlist 복구 - Allowlist 백업 - 현재 구성된 Allowlist 백업 - 경고 - 이 기능은 아직 베타 단계이며 개발 중입니다. 진행하기 전에 반드시 모듈을 백업해 주세요. 잠재적인 위험을 충분히 이해한 경우에만 이 기능을 사용하시기 바랍니다. 사용 시 주의해 주세요. - 계속 - 취소 - 다음에 - LKM 패치는 비공개 소스 구성 요소에 의존합니다. 계속 진행할까요? - 🔥 Next build - https://github.com/KernelSU-Next/KernelSU-Next - 다음 실험적인 Branch. Github에서 확인해보세요! - ⚠️ Experimental development 주의! - 127.0.0.1 - KernelSU Next는 공식 버전이 아니며, 항상 활발하고 실험적인 개발을 지향합니다. 안정성, 성능, 신뢰성 보장을 제공하지않습니다. - • 얘기치 못한 문제나 크래쉬, 시스템 문제가 일어날 수 있습니다. 사용자의 책임하에 사용하세요. - • No warranty: 개발자는 데이터, 시스템을 포함한 어떠한 문제도 책임지지 않습니다. - • 테스트용: 문제해결에 익숙하고 잠재적 위험을 이해하는 사용자를 위한 기능입니다. - Source code 보기 %1$s - 앱 프로필 - 기본값 - 템플릿 - 사용자정의 - 프로필 이름 - Mount namespace - Inherited - 전역 - 개별 - 그룹 - 권한 - SELinux context - 모듈 언마운트 - %s에 대한 앱 프로필 업데이트 실패 - KernelSU Next 버전 %1$d이 너무 낮아서 Manager가 작동하지 않습니다. %2$d 이상의 버전을 사용하세요! - 모듈 언마운트 - 앱 프로필 메뉴의 \"모듈 언마운트\" 설정에 대한 전역 기본값을 설정합니다. 활성화 시, 개별 프로필이 설정되지 않은 앱은 모든 모듈의 시스템 수정 사항을 제거합니다. - Kprobes hook 숨김 - 이 옵션을 선택하면 ksu에서 생성한 kprobes hook가 비활성화되고, 대신 내장된 non-kprobes hook가 활성화됩니다. 이로써 kprobe를 지원하지 않는 non-GKI 커널에 적용되는 것과 동일한 기능이 구현됩니다. - 이 옵션이 활성화되면, KernelSU는 이 앱에 대한 모듈의 모든 수정사항을 복구합니다. - Domain - Rules - 업데이트 - 업데이트 - 업데이트됨 - %s 모듈 다운로드 중 - %s 다운로드 시작 - 새 버전: %s 사용 가능, 여기를 눌러서 받기 - 실행 - 닫기 - 강제 종료 - 재시작 - AMOLED 모드 - Pure Black 테마로 AMOLED 화면에서 눈의 피로와 배터리 소모를 줄이세요 - 재시작 필요 - 변경 사항을 적용하려면 앱을 재시작해야 합니다. - %s에 대한 SELinux rule 업데이트 실패 - %s에 Superuser를 획득 할 수 없습니다. - 변경 내역 - 앱 프로필 템플릿 - 앱 프로필의 로컬 및 온라인 템플릿 관리 - 템플릿 만들기 - 템플릿 수정 - ID - 올바르지 않은 템플릿 ID - 이름 - 설명 - 저장 - 삭제 - 템플릿 보기 - 읽기 전용 - 템플릿 ID가 이미 존재합니다! - 가져오기/내보내기 - 클립보드에서 불러오기 - 클립보드로 내보내기 - 내보낼 로컬 템플릿이 없습니다! - 가져오기 성공 - 온라인 템플릿 동기화 - 템플릿 저장 실패 - 클립보드가 비어 있습니다. - 업데이트 내역 가져오기 실패: %s - 업데이트 확인하기 - 앱 실행시 자동으로 업데이트 확인 - 루트 권한 부여 실패! - Action - WebUI - 열기 - WebView 디버깅 활성화 - WebUI 디버깅에 사용 가능, 필요할 때만 활성화해주세요. - 직접 설치 (권장) - 파일 선택 - 비활성 슬롯에 설치 (OTA 이후) - 재부팅 후 기기는 **강제로** 비활성 슬롯으로 부팅합니다!\nOTA를 진행한 후에만 이 옵션을 사용하세요.\n진행할까요? - 다음 - %1$s 파티션 이미지 권장됨 - KMI 선택 - Sparse 이미지 최소화 - 모듈이 위치한 Sparse 이미지를 실제 크기로 조정합니다. 이 작업으로 인해 모듈이 비정상적으로 동작할 수 있으니, 백업 등 꼭 필요한 경우에만 사용해 주세요. - 삭제 - 임시 삭제 - 영구 삭제 - 순정 이미지 복구 - 임시로 KernelSU를 삭제하고, 다음 재부팅에 원래대로 복구합니다. - 완전히, 그리고 영구히 KernelSU (루트 및 모든 모듈)를 삭제합니다. - 순정 이미지 복구 (백업이 존재한다면), 보통 OTA 전에 사용합니다; KernelSU를 삭제해야 한다면, \"영구적 삭제\"를 사용해 주세요. - 플래시 중 - 플래시 성공 - 플래시 실패 - Selected LKM: %s - 로그 저장 - 로그 저장됨 - 로그 공유 - Disable su compatibility - su 명령어를 통해 앱이 루트권한을 얻는것을 일시 중지합니다. (기존 루트 프로세스는 영향받지 않습니다) - 언어 - 시스템 기본값 - 이전 UI 사용하기 - 이전 UI 스타일로 전환하기 - 배너 활성화 - 모듈 백그라운드 배너 보이기 - WebUI X 사용 - WebUI보다 더많은 API를 지원하는 WebUI X를 사용 - WebUI X에 Eruda 적용 - WebUI X에 디버그 콘솔을 삽입하여 디버깅을 쉽게 할 수 있습니다. 이 기능을 사용하려면 웹 디버깅이 켜져 있어야 합니다 - 커스터마이징 - 개발자 - SUCOMPAT 비활성화됨 - Zygisk가 필요합니다 - Zygisk injection - Superuser - Superusers - 모듈 - 모듈 - 현재 설치된 모듈을 백업 - 정렬 (활성화됨 우선) - 정렬 (동작 우선) - 정렬 (WebUI 우선) - diff --git a/manager/app/src/main/res/values-night/themes.xml b/manager/app/src/main/res/values-night/themes.xml deleted file mode 100644 index d76ba8e6..00000000 --- a/manager/app/src/main/res/values-night/themes.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - \ No newline at end of file diff --git a/manager/app/src/main/res/xml/backup_rules.xml b/manager/app/src/main/res/xml/backup_rules.xml deleted file mode 100644 index fa0f996d..00000000 --- a/manager/app/src/main/res/xml/backup_rules.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - \ No newline at end of file diff --git a/manager/app/src/main/res/xml/data_extraction_rules.xml b/manager/app/src/main/res/xml/data_extraction_rules.xml deleted file mode 100644 index 9ee9997b..00000000 --- a/manager/app/src/main/res/xml/data_extraction_rules.xml +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/manager/app/src/main/res/xml/filepaths.xml b/manager/app/src/main/res/xml/filepaths.xml deleted file mode 100644 index f8a9a5c5..00000000 --- a/manager/app/src/main/res/xml/filepaths.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/manager/app/src/main/res/xml/network_security_config.xml b/manager/app/src/main/res/xml/network_security_config.xml deleted file mode 100644 index 6dd26ccf..00000000 --- a/manager/app/src/main/res/xml/network_security_config.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - 127.0.0.1 - 0.0.0.0 - ::1 - - diff --git a/manager/build.gradle.kts b/manager/build.gradle.kts deleted file mode 100644 index 1d3285b0..00000000 --- a/manager/build.gradle.kts +++ /dev/null @@ -1,96 +0,0 @@ -import com.android.build.api.dsl.ApplicationDefaultConfig -import com.android.build.api.dsl.CommonExtension -import com.android.build.gradle.api.AndroidBasePlugin -import java.io.ByteArrayOutputStream - -plugins { - alias(libs.plugins.agp.app) apply false - alias(libs.plugins.agp.lib) apply false - alias(libs.plugins.kotlin) apply false - alias(libs.plugins.compose.compiler) apply false - alias(libs.plugins.lsplugin.cmaker) -} - -cmaker { - default { - arguments.addAll( - arrayOf( - "-DANDROID_STL=none", - ) - ) - abiFilters("arm64-v8a", "armeabi-v7a", "x86_64") - } - buildTypes { - if (it.name == "release") { - arguments += "-DDEBUG_SYMBOLS_PATH=${layout.buildDirectory.asFile.get().absolutePath}/symbols" - } - } -} - -val androidMinSdkVersion = 26 -val androidTargetSdkVersion = 36 -val androidCompileSdkVersion = 36 -val androidCompileNdkVersion = "28.1.13356709" -val androidSourceCompatibility = JavaVersion.VERSION_21 -val androidTargetCompatibility = JavaVersion.VERSION_21 -val managerVersionCode by extra(getVersionCode()) -val managerVersionName by extra(getVersionName()) - -fun getGitCommitCount(): Int { - val out = ByteArrayOutputStream() - exec { - commandLine("git", "rev-list", "--count", "HEAD") - standardOutput = out - } - return out.toString().trim().toInt() -} - -fun getGitDescribe(): String { - val out = ByteArrayOutputStream() - exec { - commandLine("git", "describe", "--tags", "--always") - standardOutput = out - } - return out.toString().trim() -} - -fun getVersionCode(): Int { - val commitCount = getGitCommitCount() - val major = 1 - return major * 10000 + commitCount + 200 -} - -fun getVersionName(): String { - return getGitDescribe() -} - -subprojects { - plugins.withType(AndroidBasePlugin::class.java) { - extensions.configure(CommonExtension::class.java) { - compileSdk = androidCompileSdkVersion - ndkVersion = androidCompileNdkVersion - - defaultConfig { - minSdk = androidMinSdkVersion - if (this is ApplicationDefaultConfig) { - targetSdk = androidTargetSdkVersion - versionCode = managerVersionCode - versionName = managerVersionName - } - ndk { - abiFilters += listOf("arm64-v8a", "armeabi-v7a", "x86_64") - } - } - - lint { - abortOnError = true - checkReleaseBuilds = false - } - - compileOptions { - sourceCompatibility = androidSourceCompatibility - targetCompatibility = androidTargetCompatibility - } - } - } -} \ No newline at end of file diff --git a/manager/gradle.properties b/manager/gradle.properties deleted file mode 100644 index 387da396..00000000 --- a/manager/gradle.properties +++ /dev/null @@ -1,3 +0,0 @@ -android.experimental.enableNewResourceShrinker.preciseShrinking=true -android.enableAppCompileTimeRClass=true -android.useAndroidX=true diff --git a/manager/gradle/libs.versions.toml b/manager/gradle/libs.versions.toml deleted file mode 100644 index 83500867..00000000 --- a/manager/gradle/libs.versions.toml +++ /dev/null @@ -1,77 +0,0 @@ -[versions] -agp = "8.10.1" -kotlin = "2.1.21" -ksp = "2.1.21-2.0.2" -compose-bom = "2025.06.00" -lifecycle = "2.9.1" -navigation = "2.9.0" -activity-compose = "1.10.1" -kotlinx-coroutines = "1.10.2" -coil-compose = "2.7.0" -compose-destination = "2.2.0" -sheets-compose-dialogs = "1.3.0" -markdown = "4.6.2" -webkit = "1.14.0" -appiconloader-coil = "1.5.0" -parcelablelist = "2.0.1" -libsu = "6.0.0" -apksign = "1.4" -cmaker = "1.2" -mmrl = "v33890" - -[plugins] -agp-app = { id = "com.android.application", version.ref = "agp" } -agp-lib = { id = "com.android.library", version.ref = "agp" } - -kotlin = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" } -compose-compiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" } - -ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" } - -lsplugin-apksign = { id = "org.lsposed.lsplugin.apksign", version.ref = "apksign" } -lsplugin-cmaker = { id = "org.lsposed.lsplugin.cmaker", version.ref = "cmaker" } - -[libraries] -androidx-activity-compose = { group = "androidx.activity", name = "activity-compose", version.ref = "activity-compose" } - -androidx-navigation-compose = { group = "androidx.navigation", name = "navigation-compose", version.ref = "navigation" } - -androidx-compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "compose-bom" } -androidx-compose-material-icons-extended = { group = "androidx.compose.material", name = "material-icons-extended" } -androidx-compose-material = { group = "androidx.compose.material", name = "material" } -androidx-compose-material3 = { group = "androidx.compose.material3", name = "material3" } -androidx-compose-ui = { group = "androidx.compose.ui", name = "ui" } -androidx-compose-ui-test-manifest = { group = "androidx.compose.ui", name = "ui-test-manifest" } -androidx-compose-ui-tooling = { group = "androidx.compose.ui", name = "ui-tooling" } -androidx-compose-ui-tooling-preview = { group = "androidx.compose.ui", name = "ui-tooling-preview" } - -androidx-lifecycle-runtime-ktx = { group = "androidx.lifecycle", name = "lifecycle-runtime-ktx", version.ref = "lifecycle" } -androidx-lifecycle-runtime-compose = { group = "androidx.lifecycle", name = "lifecycle-runtime-compose", version.ref = "lifecycle" } -androidx-lifecycle-viewmodel-compose = { group = "androidx.lifecycle", name = "lifecycle-viewmodel-compose", version.ref = "lifecycle" } - -androidx-webkit = { module = "androidx.webkit:webkit", version.ref = "webkit" } - -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" } - -dev-rikka-rikkax-parcelablelist = { module = "dev.rikka.rikkax.parcelablelist:parcelablelist", version.ref = "parcelablelist" } - -io-coil-kt-coil-compose = { group = "io.coil-kt", name = "coil-compose", version.ref = "coil-compose" } - -kotlinx-coroutines-core = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "kotlinx-coroutines" } - -me-zhanghai-android-appiconloader-coil = { group = "me.zhanghai.android.appiconloader", name = "appiconloader-coil", version.ref = "appiconloader-coil" } - -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" } - -lsposed-cxx = { module = "org.lsposed.libcxx:libcxx", version = "28.1.13356709" } - -mmrl-ui = { group = "com.github.MMRLApp.MMRL", name = "ui", version.ref = "mmrl" } \ No newline at end of file diff --git a/manager/gradle/wrapper/gradle-wrapper.jar b/manager/gradle/wrapper/gradle-wrapper.jar deleted file mode 100644 index 1b33c55b..00000000 Binary files a/manager/gradle/wrapper/gradle-wrapper.jar and /dev/null differ diff --git a/manager/gradle/wrapper/gradle-wrapper.properties b/manager/gradle/wrapper/gradle-wrapper.properties deleted file mode 100644 index e0246c08..00000000 --- a/manager/gradle/wrapper/gradle-wrapper.properties +++ /dev/null @@ -1,7 +0,0 @@ -distributionBase=GRADLE_USER_HOME -distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.2-bin.zip -networkTimeout=10000 -validateDistributionUrl=true -zipStoreBase=GRADLE_USER_HOME -zipStorePath=wrapper/dists \ No newline at end of file diff --git a/manager/gradlew b/manager/gradlew deleted file mode 100755 index 23d15a93..00000000 --- a/manager/gradlew +++ /dev/null @@ -1,251 +0,0 @@ -#!/bin/sh - -# -# Copyright © 2015-2021 the original authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -# SPDX-License-Identifier: Apache-2.0 -# - -############################################################################## -# -# Gradle start up script for POSIX generated by Gradle. -# -# Important for running: -# -# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is -# noncompliant, but you have some other compliant shell such as ksh or -# bash, then to run this script, type that shell name before the whole -# command line, like: -# -# ksh Gradle -# -# Busybox and similar reduced shells will NOT work, because this script -# requires all of these POSIX shell features: -# * functions; -# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», -# «${var#prefix}», «${var%suffix}», and «$( cmd )»; -# * compound commands having a testable exit status, especially «case»; -# * various built-in commands including «command», «set», and «ulimit». -# -# Important for patching: -# -# (2) This script targets any POSIX shell, so it avoids extensions provided -# by Bash, Ksh, etc; in particular arrays are avoided. -# -# The "traditional" practice of packing multiple parameters into a -# space-separated string is a well documented source of bugs and security -# problems, so this is (mostly) avoided, by progressively accumulating -# options in "$@", and eventually passing that to Java. -# -# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, -# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; -# see the in-line comments for details. -# -# There are tweaks for specific operating systems such as AIX, CygWin, -# Darwin, MinGW, and NonStop. -# -# (3) This script is generated from the Groovy template -# 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/. -# -############################################################################## - -# Attempt to set APP_HOME - -# Resolve links: $0 may be a link -app_path=$0 - -# Need this for daisy-chained symlinks. -while - APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path - [ -h "$app_path" ] -do - ls=$( ls -ld "$app_path" ) - link=${ls#*' -> '} - case $link in #( - /*) app_path=$link ;; #( - *) app_path=$APP_HOME$link ;; - esac -done - -# This is normally unused -# 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 -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit - -# Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD=maximum - -warn () { - echo "$*" -} >&2 - -die () { - echo - echo "$*" - echo - exit 1 -} >&2 - -# OS specific support (must be 'true' or 'false'). -cygwin=false -msys=false -darwin=false -nonstop=false -case "$( uname )" in #( - CYGWIN* ) cygwin=true ;; #( - Darwin* ) darwin=true ;; #( - MSYS* | MINGW* ) msys=true ;; #( - NONSTOP* ) nonstop=true ;; -esac - -CLASSPATH="\\\"\\\"" - - -# Determine the Java command to use to start the JVM. -if [ -n "$JAVA_HOME" ] ; then - if [ -x "$JAVA_HOME/jre/sh/java" ] ; then - # IBM's JDK on AIX uses strange locations for the executables - JAVACMD=$JAVA_HOME/jre/sh/java - else - JAVACMD=$JAVA_HOME/bin/java - fi - if [ ! -x "$JAVACMD" ] ; then - die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." - fi -else - JAVACMD=java - if ! command -v java >/dev/null 2>&1 - then - die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." - fi -fi - -# Increase the maximum file descriptors if we can. -if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then - case $MAX_FD in #( - max*) - # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. - # shellcheck disable=SC2039,SC3045 - MAX_FD=$( ulimit -H -n ) || - warn "Could not query maximum file descriptor limit" - esac - case $MAX_FD in #( - '' | soft) :;; #( - *) - # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. - # shellcheck disable=SC2039,SC3045 - ulimit -n "$MAX_FD" || - warn "Could not set maximum file descriptor limit to $MAX_FD" - esac -fi - -# Collect all arguments for the java command, stacking in reverse order: -# * args from the command line -# * the main class name -# * -classpath -# * -D...appname settings -# * --module-path (only if needed) -# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. - -# For Cygwin or MSYS, switch paths to Windows format before running java -if "$cygwin" || "$msys" ; then - APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) - CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) - - JAVACMD=$( cygpath --unix "$JAVACMD" ) - - # Now convert the arguments - kludge to limit ourselves to /bin/sh - for arg do - if - case $arg in #( - -*) false ;; # don't mess with options #( - /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath - [ -e "$t" ] ;; #( - *) false ;; - esac - then - arg=$( cygpath --path --ignore --mixed "$arg" ) - fi - # Roll the args list around exactly as many times as the number of - # args, so each arg winds up back in the position where it started, but - # possibly modified. - # - # NB: a `for` loop captures its iteration list before it begins, so - # changing the positional parameters here affects neither the number of - # iterations, nor the values presented in `arg`. - shift # remove old arg - set -- "$@" "$arg" # push replacement arg - done -fi - - -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' - -# Collect all arguments for the java command: -# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, -# and any embedded shellness will be escaped. -# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be -# treated as '${Hostname}' itself on the command line. - -set -- \ - "-Dorg.gradle.appname=$APP_BASE_NAME" \ - -classpath "$CLASSPATH" \ - -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ - "$@" - -# Stop when "xargs" is not available. -if ! command -v xargs >/dev/null 2>&1 -then - die "xargs is not available" -fi - -# Use "xargs" to parse quoted args. -# -# With -n1 it outputs one arg per line, with the quotes and backslashes removed. -# -# In Bash we could simply go: -# -# readarray ARGS < <( xargs -n1 <<<"$var" ) && -# set -- "${ARGS[@]}" "$@" -# -# but POSIX shell has neither arrays nor command substitution, so instead we -# post-process each arg (as a line of input to sed) to backslash-escape any -# character that might be a shell metacharacter, then use eval to reverse -# that process (while maintaining the separation between arguments), and wrap -# the whole thing up as a single "set" statement. -# -# This will of course break if any of these variables contains a newline or -# an unmatched quote. -# - -eval "set -- $( - printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | - xargs -n1 | - sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | - tr '\n' ' ' - )" '"$@"' - -exec "$JAVACMD" "$@" diff --git a/manager/gradlew.bat b/manager/gradlew.bat deleted file mode 100644 index db3a6ac2..00000000 --- a/manager/gradlew.bat +++ /dev/null @@ -1,94 +0,0 @@ -@rem -@rem Copyright 2015 the original author or authors. -@rem -@rem Licensed under the Apache License, Version 2.0 (the "License"); -@rem you may not use this file except in compliance with the License. -@rem You may obtain a copy of the License at -@rem -@rem https://www.apache.org/licenses/LICENSE-2.0 -@rem -@rem Unless required by applicable law or agreed to in writing, software -@rem distributed under the License is distributed on an "AS IS" BASIS, -@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -@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 ########################################################################## -@rem -@rem Gradle startup script for Windows -@rem -@rem ########################################################################## - -@rem Set local scope for the variables with windows NT shell -if "%OS%"=="Windows_NT" setlocal - -set DIRNAME=%~dp0 -if "%DIRNAME%"=="" set DIRNAME=. -@rem This is normally unused -set APP_BASE_NAME=%~n0 -set APP_HOME=%DIRNAME% - -@rem Resolve any "." and ".." in APP_HOME to make it shorter. -for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi - -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" - -@rem Find java.exe -if defined JAVA_HOME goto findJavaFromJavaHome - -set JAVA_EXE=java.exe -%JAVA_EXE% -version >NUL 2>&1 -if %ERRORLEVEL% equ 0 goto execute - -echo. 1>&2 -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 -echo. 1>&2 -echo Please set the JAVA_HOME variable in your environment to match the 1>&2 -echo location of your Java installation. 1>&2 - -goto fail - -:findJavaFromJavaHome -set JAVA_HOME=%JAVA_HOME:"=% -set JAVA_EXE=%JAVA_HOME%/bin/java.exe - -if exist "%JAVA_EXE%" goto execute - -echo. 1>&2 -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 -echo. 1>&2 -echo Please set the JAVA_HOME variable in your environment to match the 1>&2 -echo location of your Java installation. 1>&2 - -goto fail - -:execute -@rem Setup the command line - -set CLASSPATH= - - -@rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* - -:end -@rem End local scope for the variables with windows NT shell -if %ERRORLEVEL% equ 0 goto mainEnd - -:fail -rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of -rem the _cmd.exe /c_ return code! -set EXIT_CODE=%ERRORLEVEL% -if %EXIT_CODE% equ 0 set EXIT_CODE=1 -if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% -exit /b %EXIT_CODE% - -:mainEnd -if "%OS%"=="Windows_NT" endlocal - -:omega diff --git a/manager/settings.gradle.kts b/manager/settings.gradle.kts deleted file mode 100644 index e8f86c85..00000000 --- a/manager/settings.gradle.kts +++ /dev/null @@ -1,21 +0,0 @@ -@file:Suppress("UnstableApiUsage") - -enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS") - -pluginManagement { - repositories { - google() - mavenCentral() - } -} - -dependencyResolutionManagement { - repositories { - google() - mavenCentral() - maven("https://jitpack.io") - } -} - -rootProject.name = "KernelSU-Next" -include(":app") diff --git a/manager/sign.example.properties b/manager/sign.example.properties deleted file mode 100644 index bc70a60c..00000000 --- a/manager/sign.example.properties +++ /dev/null @@ -1,4 +0,0 @@ -KEYSTORE_FILE= -KEYSTORE_PASSWORD= -KEY_ALIAS= -KEY_PASSWORD= diff --git a/manager/spoof b/manager/spoof deleted file mode 100755 index e3364471..00000000 --- a/manager/spoof +++ /dev/null @@ -1,34 +0,0 @@ -#!/bin/bash - -# Generate 3 random lowercase words (6 letters each) -word1=$(tr -dc 'a-z' $word1" -echo "rifsxd -> $word2" -echo "ksunext -> $word3" -echo "" - -# Export variables for use in find -exec -export word1 word2 word3 - -# Rename directories -find . -depth -type d -name 'com' -execdir mv {} "$word1" \; -find . -depth -type d -name 'rifsxd' -execdir mv {} "$word2" \; -find . -depth -type d -name 'ksunext' -execdir mv {} "$word3" \; - -# Replace inside files -find . -type f -exec sed -i \ - -e "s/com\.rifsxd\.ksunext/$word1.$word2.$word3/g" \ - -e "s/com\/rifsxd\/ksunext/$word1\/$word2\/$word3/g" \ - -e "s/com_rifsxd_ksunext/${word1}_${word2}_${word3}/g" {} + - -# Append '-spoofed' to managerVersionName in build.gradle.kts -if [ -f "./build.gradle.kts" ]; then - sed -i 's/\(val managerVersionName by extra(getVersionName())\)/\1\nval managerVersionNameSpoofed = "${managerVersionName}-spoofed"/' ./build.gradle.kts - sed -i 's/\(versionName = managerVersionName\)/versionName = managerVersionNameSpoofed/' ./build.gradle.kts -fi - -echo "Done." diff --git a/scripts/abi_gki_all.py b/scripts/abi_gki_all.py deleted file mode 100644 index d433634a..00000000 --- a/scripts/abi_gki_all.py +++ /dev/null @@ -1,10 +0,0 @@ -from xml.dom.minidom import parse -import xml.dom.minidom -import sys - - -DOMTree = xml.dom.minidom.parse(sys.argv[1]) -symbols = DOMTree.getElementsByTagName("elf-symbol") -print("[abi_symbol_list]") -for symbol in symbols: - print(" " + symbol.getAttribute("name")) diff --git a/scripts/add_device_handler.py b/scripts/add_device_handler.py deleted file mode 100644 index f18ad3a2..00000000 --- a/scripts/add_device_handler.py +++ /dev/null @@ -1,50 +0,0 @@ -import json -import sys -import os - - -def main(): - assert len(sys.argv) == 2 - file_name = sys.argv[1] - github = "https://github.com/" - issue_content = os.environ["ISSUE_CONTENT"] - lines = issue_content.split("\n\n") - assert len(lines) == 6 - url = lines[1] - print(url) - device = lines[3] - print(device) - code_of_conduct = lines[5] - print(code_of_conduct) - assert code_of_conduct.find("[X]") > 0 - tmp = url.removesuffix("/").replace(github, "").split("/") - print(tmp) - assert len(tmp) == 2 - maintainer = tmp[0] - print(maintainer) - maintainer_link = "%s%s" % (github, maintainer) - print(maintainer_link) - kernel_name = tmp[1] - print(kernel_name) - kernel_link = "%s%s/%s" % (github, maintainer, kernel_name) - print(kernel_link) - with open(file_name, "r") as f: - data = json.loads(f.read()) - data.append( - { - "maintainer": maintainer, - "maintainer_link": maintainer_link, - "kernel_name": kernel_name, - "kernel_link": kernel_link, - "devices": device, - } - ) - os.remove(file_name) - with open(file_name, "w") as f: - f.write(json.dumps(data, indent=4)) - os.system("echo success=true >> $GITHUB_OUTPUT") - os.system("echo device=%s >> $GITHUB_OUTPUT" % device) - - -if __name__ == "__main__": - main() \ No newline at end of file diff --git a/scripts/allowlist.bt b/scripts/allowlist.bt deleted file mode 100644 index d95bb5ac..00000000 --- a/scripts/allowlist.bt +++ /dev/null @@ -1,89 +0,0 @@ -// Define constants as per the provided structure. -#define KSU_MAX_PACKAGE_NAME 256 -#define KSU_MAX_GROUPS 32 -#define KSU_SELINUX_DOMAIN 64 - -// Define the root_profile structure with padding for 64-bit alignment. -struct root_profile { - uint32 uid; - uint32 gid; - - uint32 groups_count; - uint32 groups[KSU_MAX_GROUPS]; - char padding1[4]; // Padding for 64-bit alignment. - - struct { - uint64 effective; - uint64 permitted; - uint64 inheritable; - } capabilities; - - char selinux_domain[KSU_SELINUX_DOMAIN]; - - uint32 namespaces; - char padding2[4]; // Padding for 64-bit alignment. -}; - -// Define the non_root_profile structure with padding for 64-bit alignment. -struct non_root_profile { - byte umount_modules; - char padding[7]; // Padding to make the total size a multiple of 8. -}; - -// Define the rp_config structure with padding for 64-bit alignment. -struct rp_config_t { - byte use_default; - - char template_name[KSU_MAX_PACKAGE_NAME]; - char padding[7]; // Padding to make the total size a multiple of 8. - - struct root_profile profile; -}; - -// Define the nrp_config structure with padding for 64-bit alignment. -struct nrp_config_t { - byte use_default; - char padding1[7]; // Padding to make the total size a multiple of 8. - - struct non_root_profile profile; - char padding2[488]; // Padding to align the union -}; - -// Define the main app_profile structure -typedef struct { - uint32 version; - char key[KSU_MAX_PACKAGE_NAME]; - int32 current_uid; - int64 allow_su; - - // Based on allow_su, decide which profile to use - if (allow_su != 0) { - rp_config_t rp_config; - } else { - nrp_config_t nrp_config; - } - -} app_profile; - -// Define the file header with magic number and version -typedef struct { - uint32 magic; - uint32 version; -} file_header; - -// Main entry for parsing the file -file_header header; - -if (header.magic != 0x7f4b5355) { - Printf("Invalid file magic number.\n"); - return; -} - -FSeek(8); // Skip the header - - -// Continually read app_profile instances until end of file -while (!FEof()) { - app_profile profile; -} - diff --git a/scripts/bin2c.py b/scripts/bin2c.py deleted file mode 100644 index 58513132..00000000 --- a/scripts/bin2c.py +++ /dev/null @@ -1,51 +0,0 @@ -#!/usr/bin/python3 - -import argparse -import os -import re - -line_size = 80 - - -def bin2c(filename, varname='data'): - if not os.path.isfile(filename): - print('File "%s" is not found!' % filename) - return '' - if not re.match('[a-zA-Z_][a-zA-Z0-9_]*', varname): - print('Invalid variable name "%s"' % varname) - return - with open(filename, 'rb') as in_file: - data = in_file.read() - # limit the line length - byte_len = 6 # '0x00, ' - out = 'unsigned int %s_size = %d;\n' \ - 'const char %s[%d] = {\n' % (varname, len(data), varname, len(data)) - line = '' - for byte in data: - line += '0x%02x, ' % byte - if len(line) + 4 + byte_len >= line_size: - out += ' ' * 4 + line + '\n' - line = '' - # add the last line - if len(line) + 4 + byte_len < line_size: - out += ' ' * 4 + line + '\n' - # strip the last comma - out = out.rstrip(', \n') + '\n' - out += '};' - return out - - -def main(): - """ Main func """ - parser = argparse.ArgumentParser() - parser.add_argument( - 'filename', help='filename to convert to C array') - parser.add_argument( - 'varname', nargs='?', help='variable name', default='data') - args = parser.parse_args() - # print out the data - print(bin2c(args.filename, args.varname)) - - -if __name__ == '__main__': - main() diff --git a/scripts/ksunextbot.py b/scripts/ksunextbot.py deleted file mode 100644 index 7b7245e7..00000000 --- a/scripts/ksunextbot.py +++ /dev/null @@ -1,106 +0,0 @@ -import asyncio -import os -import sys -from telethon import TelegramClient -from telethon.tl.functions.help import GetConfigRequest - -# Environment Variables -API_ID = os.environ.get("API_ID") -API_HASH = os.environ.get("API_HASH") -BOT_TOKEN = os.environ.get("BOT_TOKEN") -CHAT_ID = os.environ.get("CHAT_ID") -MESSAGE_THREAD_ID = os.environ.get("MESSAGE_THREAD_ID") -COMMIT_URL = os.environ.get("COMMIT_URL") -COMMIT_MESSAGE = os.environ.get("COMMIT_MESSAGE") -RUN_URL = os.environ.get("RUN_URL") -TITLE = os.environ.get("TITLE") -VERSION = os.environ.get("VERSION") -MSG_TEMPLATE = """ -**{title}** -#ci_{version} -``` -{commit_message} -``` -[Commit]({commit_url}) -[Workflow run]({run_url}) -""".strip() - - -def get_caption(): - msg = MSG_TEMPLATE.format( - title=TITLE, - version=VERSION, - commit_message=COMMIT_MESSAGE, - commit_url=COMMIT_URL, - run_url=RUN_URL, - ) - if len(msg) > 1024: - return COMMIT_URL - return msg - - -def check_environ(): - global CHAT_ID, MESSAGE_THREAD_ID - if BOT_TOKEN is None: - print("[-] Invalid BOT_TOKEN") - exit(1) - if CHAT_ID is None: - print("[-] Invalid CHAT_ID") - exit(1) - else: - CHAT_ID = int(CHAT_ID) - if COMMIT_URL is None: - print("[-] Invalid COMMIT_URL") - exit(1) - if COMMIT_MESSAGE is None: - print("[-] Invalid COMMIT_MESSAGE") - exit(1) - if RUN_URL is None: - print("[-] Invalid RUN_URL") - exit(1) - if TITLE is None: - print("[-] Invalid TITLE") - exit(1) - if VERSION is None: - print("[-] Invalid VERSION") - exit(1) - if MESSAGE_THREAD_ID is None: - print("[-] Invaild MESSAGE_THREAD_ID") - exit(1) - else: - MESSAGE_THREAD_ID = int(MESSAGE_THREAD_ID) - - -async def main(): - print("[+] Uploading to telegram") - check_environ() - files = sys.argv[1:] - print("[+] Files:", files) - if len(files) <= 0: - print("[-] No files to upload") - exit(1) - print("[+] Logging in Telegram with bot") - script_dir = os.path.dirname(os.path.abspath(sys.argv[0])) - session_dir = os.path.join(script_dir, "ksunextbot") - async with await TelegramClient(session=session_dir, api_id=API_ID, api_hash=API_HASH).start(bot_token=BOT_TOKEN) as bot: - caption = [""] * len(files) - caption[-1] = get_caption() - print("[+] Caption: ") - print("---") - print(caption) - print("---") - print("[+] Sending") - await bot.send_file( - entity=CHAT_ID, - file=files, - caption=caption, - reply_to=MESSAGE_THREAD_ID, - parse_mode="markdown" - ) - print("[+] Done!") - -if __name__ == "__main__": - try: - asyncio.run(main()) - except Exception as e: - print(f"[-] An error occurred: {e}") diff --git a/userspace/ksud_magic/.gitignore b/userspace/ksud_magic/.gitignore deleted file mode 100644 index 3c71873d..00000000 --- a/userspace/ksud_magic/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -/target -.cargo/ \ No newline at end of file diff --git a/userspace/ksud_magic/Cargo.lock b/userspace/ksud_magic/Cargo.lock deleted file mode 100644 index c0539e64..00000000 --- a/userspace/ksud_magic/Cargo.lock +++ /dev/null @@ -1,1675 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 4 - -[[package]] -name = "addr2line" -version = "0.24.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" -dependencies = [ - "gimli", -] - -[[package]] -name = "adler2" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" - -[[package]] -name = "adler32" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234" - -[[package]] -name = "ahash" -version = "0.8.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" -dependencies = [ - "cfg-if", - "once_cell", - "version_check", - "zerocopy", -] - -[[package]] -name = "allocator-api2" -version = "0.2.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" - -[[package]] -name = "android-properties" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc7eb209b1518d6bb87b283c20095f5228ecda460da70b44f0802523dea6da04" - -[[package]] -name = "android-tzdata" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" - -[[package]] -name = "android_log-sys" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84521a3cf562bc62942e294181d9eef17eb38ceb8c68677bc49f144e4c3d4f8d" - -[[package]] -name = "android_logger" -version = "0.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6f39be698127218cca460cb624878c9aa4e2b47dba3b277963d2bf00bad263b" -dependencies = [ - "android_log-sys", - "env_filter", - "log", -] - -[[package]] -name = "android_system_properties" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" -dependencies = [ - "libc", -] - -[[package]] -name = "anstream" -version = "0.6.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "301af1932e46185686725e0fad2f8f2aa7da69dd70bf6ecc44d6b703844a3933" -dependencies = [ - "anstyle", - "anstyle-parse", - "anstyle-query", - "anstyle-wincon", - "colorchoice", - "is_terminal_polyfill", - "utf8parse", -] - -[[package]] -name = "anstyle" -version = "1.0.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "862ed96ca487e809f1c8e5a8447f6ee2cf102f846893800b20cebdf541fc6bbd" - -[[package]] -name = "anstyle-parse" -version = "0.2.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2" -dependencies = [ - "utf8parse", -] - -[[package]] -name = "anstyle-query" -version = "1.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c8bdeb6047d8983be085bab0ba1472e6dc604e7041dbf6fcd5e71523014fae9" -dependencies = [ - "windows-sys 0.59.0", -] - -[[package]] -name = "anstyle-wincon" -version = "3.0.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "403f75924867bb1033c59fbf0797484329750cfbe3c4325cd33127941fabc882" -dependencies = [ - "anstyle", - "once_cell_polyfill", - "windows-sys 0.59.0", -] - -[[package]] -name = "anyhow" -version = "1.0.98" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487" - -[[package]] -name = "arbitrary" -version = "1.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dde20b3d026af13f561bdd0f15edf01fc734f0dafcedbaf42bba506a9517f223" -dependencies = [ - "derive_arbitrary", -] - -[[package]] -name = "async-trait" -version = "0.1.88" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e539d3fca749fcee5236ab05e93a52867dd549cc157c8cb7f99595f3cedffdb5" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "autocfg" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" - -[[package]] -name = "backtrace" -version = "0.3.75" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6806a6321ec58106fea15becdad98371e28d92ccbc7c8f1b3b6dd724fe8f1002" -dependencies = [ - "addr2line", - "cfg-if", - "libc", - "miniz_oxide", - "object", - "rustc-demangle", - "windows-targets", -] - -[[package]] -name = "bitflags" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - -[[package]] -name = "bitflags" -version = "2.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" - -[[package]] -name = "block-buffer" -version = "0.10.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" -dependencies = [ - "generic-array", -] - -[[package]] -name = "bumpalo" -version = "3.18.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "793db76d6187cd04dff33004d8e6c9cc4e05cd330500379d2394209271b4aeee" - -[[package]] -name = "byteorder" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" - -[[package]] -name = "bytes" -version = "1.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" - -[[package]] -name = "cc" -version = "1.2.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d487aa071b5f64da6f19a3e848e3578944b726ee5a4854b82172f02aa876bfdc" -dependencies = [ - "shlex", -] - -[[package]] -name = "cfg-if" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268" - -[[package]] -name = "chrono" -version = "0.4.41" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c469d952047f47f91b68d1cba3f10d63c11d73e4636f24f08daf0278abf01c4d" -dependencies = [ - "android-tzdata", - "iana-time-zone", - "js-sys", - "num-traits", - "wasm-bindgen", - "windows-link", -] - -[[package]] -name = "clap" -version = "4.5.40" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40b6887a1d8685cebccf115538db5c0efe625ccac9696ad45c409d96566e910f" -dependencies = [ - "clap_builder", - "clap_derive", -] - -[[package]] -name = "clap_builder" -version = "4.5.40" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0c66c08ce9f0c698cbce5c0279d0bb6ac936d8674174fe48f736533b964f59e" -dependencies = [ - "anstream", - "anstyle", - "clap_lex", - "strsim", -] - -[[package]] -name = "clap_derive" -version = "4.5.40" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2c7947ae4cc3d851207c1adb5b5e260ff0cca11446b1d6d1423788e442257ce" -dependencies = [ - "heck", - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "clap_lex" -version = "0.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b94f61472cee1439c0b966b47e3aca9ae07e45d070759512cd390ea2bebc6675" - -[[package]] -name = "colorchoice" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" - -[[package]] -name = "const_format" -version = "0.2.34" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "126f97965c8ad46d6d9163268ff28432e8f6a1196a55578867832e3049df63dd" -dependencies = [ - "const_format_proc_macros", -] - -[[package]] -name = "const_format_proc_macros" -version = "0.2.34" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d57c2eccfb16dbac1f4e61e206105db5820c9d26c3c472bc17c774259ef7744" -dependencies = [ - "proc-macro2", - "quote", - "unicode-xid", -] - -[[package]] -name = "core-foundation-sys" -version = "0.8.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" - -[[package]] -name = "core2" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b49ba7ef1ad6107f8824dbe97de947cbaac53c44e7f9756a1fba0d37c1eec505" -dependencies = [ - "memchr", -] - -[[package]] -name = "cpufeatures" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" -dependencies = [ - "libc", -] - -[[package]] -name = "crc" -version = "3.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9710d3b3739c2e349eb44fe848ad0b7c8cb1e42bd87ee49371df2f7acaf3e675" -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" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "crossbeam" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1137cd7e7fc0fb5d3c5a8678be38ec56e819125d8d7907411fe24ccb943faca8" -dependencies = [ - "crossbeam-channel", - "crossbeam-deque", - "crossbeam-epoch", - "crossbeam-queue", - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-channel" -version = "0.5.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82b8f8f868b36967f9606790d1903570de9ceaf870a7bf9fbbd3016d636a2cb2" -dependencies = [ - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-deque" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" -dependencies = [ - "crossbeam-epoch", - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-epoch" -version = "0.9.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" -dependencies = [ - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-queue" -version = "0.3.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f58bbc28f91df819d0aa2a2c00cd19754769c2fad90579b3592b1c9ba7a3115" -dependencies = [ - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-utils" -version = "0.8.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" - -[[package]] -name = "crypto-common" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" -dependencies = [ - "generic-array", - "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.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da692b8d1080ea3045efaab14434d40468c3d8657e42abddfffca87b428f4c1b" - -[[package]] -name = "deranged" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c9e6a11ca8224451684bc0d7d5a7adbf8f2fd6887261a1cfc3c0432f9d4068e" -dependencies = [ - "powerfmt", -] - -[[package]] -name = "derive-new" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2cdc8d50f426189eef89dac62fabfa0abb27d5cc008f25bf4156a0203325becc" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "derive_arbitrary" -version = "1.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30542c1ad912e0e3d22a1935c290e12e8a29d704a420177a31faad4a601a0800" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "digest" -version = "0.10.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" -dependencies = [ - "block-buffer", - "crypto-common", -] - -[[package]] -name = "either" -version = "1.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" - -[[package]] -name = "encoding_rs" -version = "0.8.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "env_filter" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "186e05a59d4c50738528153b83b0b0194d3a29507dfec16eccd4b342903397d0" -dependencies = [ - "log", -] - -[[package]] -name = "env_home" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7f84e12ccf0a7ddc17a6c41c93326024c42920d7ee630d04950e6926645c0fe" - -[[package]] -name = "env_logger" -version = "0.11.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13c863f0904021b108aa8b2f55046443e6b1ebde8fd4a15c399893aae4fa069f" -dependencies = [ - "env_filter", - "log", -] - -[[package]] -name = "equivalent" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" - -[[package]] -name = "errno" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1" -dependencies = [ - "errno-dragonfly", - "libc", - "winapi", -] - -[[package]] -name = "errno" -version = "0.3.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cea14ef9355e3beab063703aa9dab15afd25f0667c341310c1e5274bb1d0da18" -dependencies = [ - "libc", - "windows-sys 0.59.0", -] - -[[package]] -name = "errno-dragonfly" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" -dependencies = [ - "cc", - "libc", -] - -[[package]] -name = "extattr" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b59f8a77817ff1b795adafc535941bdf664184f5f95e0b6d1d77dd6d12815dc" -dependencies = [ - "bitflags 1.3.2", - "errno 0.2.8", - "libc", -] - -[[package]] -name = "fastrand" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" - -[[package]] -name = "flate2" -version = "1.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a3d7db9596fecd151c5f638c0ee5d5bd487b6e0ea232e5dc96d5250f6f94b1d" -dependencies = [ - "crc32fast", - "libz-rs-sys", - "miniz_oxide", -] - -[[package]] -name = "generic-array" -version = "0.14.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" -dependencies = [ - "typenum", - "version_check", -] - -[[package]] -name = "getopts" -version = "0.2.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cba6ae63eb948698e300f645f87c70f76630d505f23b8907cf1e193ee85048c1" -dependencies = [ - "unicode-width", -] - -[[package]] -name = "getrandom" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" -dependencies = [ - "cfg-if", - "libc", - "r-efi", - "wasi", -] - -[[package]] -name = "gimli" -version = "0.31.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -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.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5971ac85611da7067dbfcabef3c70ebb5606018acd9e2a3903a0da507521e0d5" - -[[package]] -name = "heck" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" - -[[package]] -name = "hex" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" - -[[package]] -name = "humansize" -version = "2.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6cb51c9a029ddc91b07a787f1d86b53ccfa49b0e86688c946ebe8d3555685dd7" -dependencies = [ - "libm", -] - -[[package]] -name = "iana-time-zone" -version = "0.1.63" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0c919e5debc312ad217002b8048a17b7d83f80703865bbfcfebb0458b0b27d8" -dependencies = [ - "android_system_properties", - "core-foundation-sys", - "iana-time-zone-haiku", - "js-sys", - "log", - "wasm-bindgen", - "windows-core", -] - -[[package]] -name = "iana-time-zone-haiku" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" -dependencies = [ - "cc", -] - -[[package]] -name = "include-flate" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df49c16750695486c1f34de05da5b7438096156466e7f76c38fcdf285cf0113e" -dependencies = [ - "include-flate-codegen", - "lazy_static", - "libflate", -] - -[[package]] -name = "include-flate-codegen" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c5b246c6261be723b85c61ecf87804e8ea4a35cb68be0ff282ed84b95ffe7d7" -dependencies = [ - "libflate", - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "indexmap" -version = "2.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e" -dependencies = [ - "equivalent", - "hashbrown 0.15.4", -] - -[[package]] -name = "is_executable" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4a1b5bad6f9072935961dfbf1cced2f3d129963d091b6f69f007fe04e758ae2" -dependencies = [ - "winapi", -] - -[[package]] -name = "is_terminal_polyfill" -version = "1.70.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" - -[[package]] -name = "itoa" -version = "1.0.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" - -[[package]] -name = "java-properties" -version = "2.0.0" -source = "git+https://github.com/KernelSU-Next/java-properties.git?branch=master#42a4aa941b70ded2dd3be9e9f892471023e70229" -dependencies = [ - "encoding_rs", - "lazy_static", - "regex-lite", -] - -[[package]] -name = "js-sys" -version = "0.3.77" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" -dependencies = [ - "once_cell", - "wasm-bindgen", -] - -[[package]] -name = "jwalk" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2735847566356cd2179a2a38264839308f7079fa96e6bd5a42d740460e003c56" -dependencies = [ - "crossbeam", - "rayon", -] - -[[package]] -name = "ksud" -version = "0.1.0" -dependencies = [ - "android-properties", - "android_logger", - "anyhow", - "chrono", - "clap", - "const_format", - "derive-new", - "encoding_rs", - "env_logger", - "extattr", - "getopts", - "humansize", - "is_executable", - "java-properties", - "jwalk", - "libc", - "log", - "nom", - "procfs", - "regex-lite", - "rust-embed", - "rustix 0.38.34", - "serde_json", - "sha1", - "sha256", - "tempfile", - "which", - "zip", - "zip-extensions", -] - -[[package]] -name = "lazy_static" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" - -[[package]] -name = "libc" -version = "0.2.173" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8cfeafaffdbc32176b64fb251369d52ea9f0a8fbc6f8759edffef7b525d64bb" - -[[package]] -name = "libflate" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45d9dfdc14ea4ef0900c1cddbc8dcd553fbaacd8a4a282cf4018ae9dd04fb21e" -dependencies = [ - "adler32", - "core2", - "crc32fast", - "dary_heap", - "libflate_lz77", -] - -[[package]] -name = "libflate_lz77" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6e0d73b369f386f1c44abd9c570d5318f55ccde816ff4b562fa452e5182863d" -dependencies = [ - "core2", - "hashbrown 0.14.5", - "rle-decode-fast", -] - -[[package]] -name = "libm" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de" - -[[package]] -name = "libz-rs-sys" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "172a788537a2221661b480fee8dc5f96c580eb34fa88764d3205dc356c7e4221" -dependencies = [ - "zlib-rs", -] - -[[package]] -name = "linux-raw-sys" -version = "0.4.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" - -[[package]] -name = "linux-raw-sys" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12" - -[[package]] -name = "log" -version = "0.4.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" - -[[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 = "lzma-sys" -version = "0.1.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fda04ab3764e6cde78b9974eec4f779acaba7c4e84b36eca3cf77c581b85d27" -dependencies = [ - "cc", - "libc", - "pkg-config", -] - -[[package]] -name = "memchr" -version = "2.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" - -[[package]] -name = "miniz_oxide" -version = "0.8.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" -dependencies = [ - "adler2", -] - -[[package]] -name = "nom" -version = "8.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df9761775871bdef83bee530e60050f7e54b1105350d6884eb0fb4f46c2f9405" -dependencies = [ - "memchr", -] - -[[package]] -name = "num-conv" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" - -[[package]] -name = "num-traits" -version = "0.2.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" -dependencies = [ - "autocfg", -] - -[[package]] -name = "object" -version = "0.36.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" -dependencies = [ - "memchr", -] - -[[package]] -name = "once_cell" -version = "1.21.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" - -[[package]] -name = "once_cell_polyfill" -version = "1.70.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4895175b425cb1f87721b59f0f286c2092bd4af812243672510e1ac53e2e0ad" - -[[package]] -name = "pin-project-lite" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" - -[[package]] -name = "pkg-config" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" - -[[package]] -name = "powerfmt" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" - -[[package]] -name = "proc-macro2" -version = "1.0.95" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "procfs" -version = "0.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc5b72d8145275d844d4b5f6d4e1eef00c8cd889edb6035c21675d1bb1f45c9f" -dependencies = [ - "bitflags 2.9.1", - "chrono", - "flate2", - "hex", - "procfs-core", - "rustix 0.38.44", -] - -[[package]] -name = "procfs-core" -version = "0.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "239df02d8349b06fc07398a3a1697b06418223b1c7725085e801e7c0fc6a12ec" -dependencies = [ - "bitflags 2.9.1", - "chrono", - "hex", -] - -[[package]] -name = "quote" -version = "1.0.40" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "r-efi" -version = "5.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5" - -[[package]] -name = "rayon" -version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" -dependencies = [ - "either", - "rayon-core", -] - -[[package]] -name = "rayon-core" -version = "1.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" -dependencies = [ - "crossbeam-deque", - "crossbeam-utils", -] - -[[package]] -name = "regex-lite" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53a49587ad06b26609c52e423de037e7f57f20d53535d66e08c695f347df952a" - -[[package]] -name = "rle-decode-fast" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3582f63211428f83597b51b2ddb88e2a91a9d52d12831f9d08f5e624e8977422" - -[[package]] -name = "rust-embed" -version = "8.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "025908b8682a26ba8d12f6f2d66b987584a4a87bc024abc5bbc12553a8cd178a" -dependencies = [ - "include-flate", - "rust-embed-impl", - "rust-embed-utils", - "walkdir", -] - -[[package]] -name = "rust-embed-impl" -version = "8.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6065f1a4392b71819ec1ea1df1120673418bf386f50de1d6f54204d836d4349c" -dependencies = [ - "proc-macro2", - "quote", - "rust-embed-utils", - "syn", - "walkdir", -] - -[[package]] -name = "rust-embed-utils" -version = "8.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6cc0c81648b20b70c491ff8cce00c1c3b223bb8ed2b5d41f0e54c6c4c0a3594" -dependencies = [ - "sha2", - "walkdir", -] - -[[package]] -name = "rustc-demangle" -version = "0.1.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "989e6739f80c4ad5b13e0fd7fe89531180375b18520cc8c82080e4dc4035b84f" - -[[package]] -name = "rustix" -version = "0.38.34" -source = "git+https://github.com/KernelSU-Next/rustix.git?branch=main#4a53fbc7cb7a07cabe87125cc21dbc27db316259" -dependencies = [ - "bitflags 2.9.1", - "errno 0.3.12", - "itoa", - "libc", - "linux-raw-sys 0.4.15", - "once_cell", - "windows-sys 0.52.0", -] - -[[package]] -name = "rustix" -version = "0.38.44" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" -dependencies = [ - "bitflags 2.9.1", - "errno 0.3.12", - "libc", - "linux-raw-sys 0.4.15", - "windows-sys 0.59.0", -] - -[[package]] -name = "rustix" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c71e83d6afe7ff64890ec6b71d6a69bb8a610ab78ce364b3352876bb4c801266" -dependencies = [ - "bitflags 2.9.1", - "errno 0.3.12", - "libc", - "linux-raw-sys 0.9.4", - "windows-sys 0.59.0", -] - -[[package]] -name = "rustversion" -version = "1.0.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a0d197bd2c9dc6e53b84da9556a69ba4cdfab8619eb41a8bd1cc2027a0f6b1d" - -[[package]] -name = "ryu" -version = "1.0.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" - -[[package]] -name = "same-file" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" -dependencies = [ - "winapi-util", -] - -[[package]] -name = "serde" -version = "1.0.219" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" -dependencies = [ - "serde_derive", -] - -[[package]] -name = "serde_derive" -version = "1.0.219" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "serde_json" -version = "1.0.140" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" -dependencies = [ - "itoa", - "memchr", - "ryu", - "serde", -] - -[[package]] -name = "sha1" -version = "0.10.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" -dependencies = [ - "cfg-if", - "cpufeatures", - "digest", -] - -[[package]] -name = "sha2" -version = "0.10.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" -dependencies = [ - "cfg-if", - "cpufeatures", - "digest", -] - -[[package]] -name = "sha256" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f880fc8562bdeb709793f00eb42a2ad0e672c4f883bbe59122b926eca935c8f6" -dependencies = [ - "async-trait", - "bytes", - "hex", - "sha2", - "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" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" - -[[package]] -name = "strsim" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" - -[[package]] -name = "syn" -version = "2.0.103" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4307e30089d6fd6aff212f2da3a1f9e32f3223b1f010fb09b7c95f90f3ca1e8" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "tempfile" -version = "3.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8a64e3985349f2441a1a9ef0b853f869006c3855f2cda6862a94d26ebb9d6a1" -dependencies = [ - "fastrand", - "getrandom", - "once_cell", - "rustix 1.0.7", - "windows-sys 0.59.0", -] - -[[package]] -name = "time" -version = "0.3.41" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a7619e19bc266e0f9c5e6686659d394bc57973859340060a69221e57dbc0c40" -dependencies = [ - "deranged", - "num-conv", - "powerfmt", - "serde", - "time-core", -] - -[[package]] -name = "time-core" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9e9a38711f559d9e3ce1cdb06dd7c5b8ea546bc90052da6d06bb76da74bb07c" - -[[package]] -name = "tokio" -version = "1.45.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75ef51a33ef1da925cea3e4eb122833cb377c61439ca401b770f54902b806779" -dependencies = [ - "backtrace", - "bytes", - "pin-project-lite", -] - -[[package]] -name = "typenum" -version = "1.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" - -[[package]] -name = "unicode-ident" -version = "1.0.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" - -[[package]] -name = "unicode-width" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a1a07cc7db3810833284e8d372ccdc6da29741639ecc70c9ec107df0fa6154c" - -[[package]] -name = "unicode-xid" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" - -[[package]] -name = "utf8parse" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" - -[[package]] -name = "version_check" -version = "0.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" - -[[package]] -name = "walkdir" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" -dependencies = [ - "same-file", - "winapi-util", -] - -[[package]] -name = "wasi" -version = "0.14.2+wasi-0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3" -dependencies = [ - "wit-bindgen-rt", -] - -[[package]] -name = "wasm-bindgen" -version = "0.2.100" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" -dependencies = [ - "cfg-if", - "once_cell", - "rustversion", - "wasm-bindgen-macro", -] - -[[package]] -name = "wasm-bindgen-backend" -version = "0.2.100" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" -dependencies = [ - "bumpalo", - "log", - "proc-macro2", - "quote", - "syn", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-macro" -version = "0.2.100" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" -dependencies = [ - "quote", - "wasm-bindgen-macro-support", -] - -[[package]] -name = "wasm-bindgen-macro-support" -version = "0.2.100" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "wasm-bindgen-backend", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-shared" -version = "0.2.100" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "which" -version = "8.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3fabb953106c3c8eea8306e4393700d7657561cb43122571b172bbfb7c7ba1d" -dependencies = [ - "env_home", - "rustix 1.0.7", - "winsafe", -] - -[[package]] -name = "winapi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" -dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", -] - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" - -[[package]] -name = "winapi-util" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" -dependencies = [ - "windows-sys 0.59.0", -] - -[[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" - -[[package]] -name = "windows-core" -version = "0.61.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3" -dependencies = [ - "windows-implement", - "windows-interface", - "windows-link", - "windows-result", - "windows-strings", -] - -[[package]] -name = "windows-implement" -version = "0.60.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "windows-interface" -version = "0.59.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "windows-link" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" - -[[package]] -name = "windows-result" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" -dependencies = [ - "windows-link", -] - -[[package]] -name = "windows-strings" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57" -dependencies = [ - "windows-link", -] - -[[package]] -name = "windows-sys" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" -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.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" -dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_gnullvm", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", -] - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" - -[[package]] -name = "windows_i686_gnu" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" - -[[package]] -name = "windows_i686_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" - -[[package]] -name = "windows_i686_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" - -[[package]] -name = "winsafe" -version = "0.0.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d135d17ab770252ad95e9a872d365cf3090e3be864a34ab46f48555993efc904" - -[[package]] -name = "wit-bindgen-rt" -version = "0.39.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" -dependencies = [ - "bitflags 2.9.1", -] - -[[package]] -name = "xz2" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "388c44dc09d76f1536602ead6d325eb532f5c122f17782bd57fb47baeeb767e2" -dependencies = [ - "lzma-sys", -] - -[[package]] -name = "zerocopy" -version = "0.8.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1702d9583232ddb9174e01bb7c15a2ab8fb1bc6f227aa1233858c351a3ba0cb" -dependencies = [ - "zerocopy-derive", -] - -[[package]] -name = "zerocopy-derive" -version = "0.8.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28a6e20d751156648aa063f3800b706ee209a32c0b4d9f24be3d980b01be55ef" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "zip" -version = "3.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12598812502ed0105f607f941c386f43d441e00148fce9dec3ca5ffb0bde9308" -dependencies = [ - "arbitrary", - "crc32fast", - "deflate64", - "flate2", - "indexmap", - "lzma-rs", - "memchr", - "time", - "xz2", - "zopfli", -] - -[[package]] -name = "zip-extensions" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f105becb0a5da773e655775dd05fee454ca1475bcc980ec9d940a02f42cee40" -dependencies = [ - "zip", -] - -[[package]] -name = "zlib-rs" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "626bd9fa9734751fc50d6060752170984d7053f5a39061f524cda68023d4db8a" - -[[package]] -name = "zopfli" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edfc5ee405f504cd4984ecc6f14d02d55cfda60fa4b689434ef4102aae150cd7" -dependencies = [ - "bumpalo", - "crc32fast", - "log", - "simd-adler32", -] diff --git a/userspace/ksud_magic/Cargo.toml b/userspace/ksud_magic/Cargo.toml deleted file mode 100644 index e7a17982..00000000 --- a/userspace/ksud_magic/Cargo.toml +++ /dev/null @@ -1,64 +0,0 @@ -[package] -name = "ksud" -version = "0.1.0" -edition = "2024" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -anyhow = "1" -clap = { version = "4", features = ["derive"] } -const_format = "0.2" -zip = { version = "3", features = [ - - "deflate", - "deflate64", - "time", - "lzma", - "xz", -], default-features = false } -zip-extensions = { version = "0.8", features = [ - "deflate", - "lzma", - "xz", -], default-features = false } -java-properties = { git = "https://github.com/KernelSU-Next/java-properties.git", branch = "master", default-features = false } -log = "0.4" -env_logger = { version = "0.11", default-features = false } -serde_json = "1" -encoding_rs = "0.8" -humansize = "2" -libc = "0.2" -extattr = "1" -jwalk = "0.8" -is_executable = "1" -nom = "8" -derive-new = "0.7" -rust-embed = { version = "8", features = [ - "debug-embed", - "compression", # must clean build after updating binaries -] } -which = "8" -getopts = "0.2" -sha256 = "1" -sha1 = "0.10" -tempfile = "3" -chrono = "0.4" -regex-lite = "0.1" - -[target.'cfg(any(target_os = "android", target_os = "linux"))'.dependencies] -rustix = { git = "https://github.com/KernelSU-Next/rustix.git", branch = "main", features = [ - "all-apis", -] } -# 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.17" - -[target.'cfg(target_os = "android")'.dependencies] -android_logger = { version = "0.15", default-features = false } - -[profile.release] -strip = true -opt-level = "z" -lto = true -codegen-units = 1 \ No newline at end of file diff --git a/userspace/ksud_magic/bin/.gitignore b/userspace/ksud_magic/bin/.gitignore deleted file mode 100644 index c8b95e0e..00000000 --- a/userspace/ksud_magic/bin/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -**/*.ko -susfsd \ No newline at end of file diff --git a/userspace/ksud_magic/bin/aarch64/bootctl b/userspace/ksud_magic/bin/aarch64/bootctl deleted file mode 100644 index cf5c6136..00000000 Binary files a/userspace/ksud_magic/bin/aarch64/bootctl and /dev/null differ diff --git a/userspace/ksud_magic/bin/aarch64/busybox b/userspace/ksud_magic/bin/aarch64/busybox deleted file mode 100755 index 2fd8bcfa..00000000 Binary files a/userspace/ksud_magic/bin/aarch64/busybox and /dev/null differ diff --git a/userspace/ksud_magic/bin/aarch64/ksuinit b/userspace/ksud_magic/bin/aarch64/ksuinit deleted file mode 100644 index 8d3ba057..00000000 Binary files a/userspace/ksud_magic/bin/aarch64/ksuinit and /dev/null differ diff --git a/userspace/ksud_magic/bin/aarch64/resetprop b/userspace/ksud_magic/bin/aarch64/resetprop deleted file mode 100644 index 305032c9..00000000 Binary files a/userspace/ksud_magic/bin/aarch64/resetprop and /dev/null differ diff --git a/userspace/ksud_magic/bin/arm/busybox b/userspace/ksud_magic/bin/arm/busybox deleted file mode 100755 index aded8cfa..00000000 Binary files a/userspace/ksud_magic/bin/arm/busybox and /dev/null differ diff --git a/userspace/ksud_magic/bin/arm/resetprop b/userspace/ksud_magic/bin/arm/resetprop deleted file mode 100644 index 2e7e1575..00000000 Binary files a/userspace/ksud_magic/bin/arm/resetprop and /dev/null differ diff --git a/userspace/ksud_magic/bin/x86_64/busybox b/userspace/ksud_magic/bin/x86_64/busybox deleted file mode 100644 index 3603e741..00000000 Binary files a/userspace/ksud_magic/bin/x86_64/busybox and /dev/null differ diff --git a/userspace/ksud_magic/bin/x86_64/resetprop b/userspace/ksud_magic/bin/x86_64/resetprop deleted file mode 100644 index 80030612..00000000 Binary files a/userspace/ksud_magic/bin/x86_64/resetprop and /dev/null differ diff --git a/userspace/ksud_magic/build.rs b/userspace/ksud_magic/build.rs deleted file mode 100644 index 90a08777..00000000 --- a/userspace/ksud_magic/build.rs +++ /dev/null @@ -1,52 +0,0 @@ -use std::env; -use std::fs::File; -use std::io::Write; -use std::path::Path; -use std::process::Command; - -fn get_git_version() -> Result<(u32, String), std::io::Error> { - let output = Command::new("git") - .args(["rev-list", "--count", "HEAD"]) - .output()?; - - let output = output.stdout; - let version_code = String::from_utf8(output).expect("Failed to read git count stdout"); - let version_code: u32 = version_code - .trim() - .parse() - .map_err(|_| std::io::Error::other("Failed to parse git count"))?; - let version_code = 10000 + 200 + version_code; // For historical reasons - - let version_name = String::from_utf8( - Command::new("git") - .args(["describe", "--tags", "--always"]) - .output()? - .stdout, - ) - .map_err(|_| std::io::Error::other("Failed to read git describe stdout"))?; - - let version_name = version_name.trim_start_matches('v').to_string(); - Ok((version_code, version_name)) -} - -fn main() { - let (code, name) = match get_git_version() { - Ok((code, name)) => (code, name), - Err(_) => { - // show warning if git is not installed - println!("cargo:warning=Failed to get git version, using 0.0.0"); - (0, "0.0.0".to_string()) - } - }; - let out_dir = env::var("OUT_DIR").expect("Failed to get $OUT_DIR"); - let out_dir = Path::new(&out_dir); - File::create(Path::new(out_dir).join("VERSION_CODE")) - .expect("Failed to create VERSION_CODE") - .write_all(code.to_string().as_bytes()) - .expect("Failed to write VERSION_CODE"); - - File::create(Path::new(out_dir).join("VERSION_NAME")) - .expect("Failed to create VERSION_NAME") - .write_all(name.trim().as_bytes()) - .expect("Failed to write VERSION_NAME"); -} diff --git a/userspace/ksud_magic/src/apk_sign.rs b/userspace/ksud_magic/src/apk_sign.rs deleted file mode 100644 index 4b2c4a98..00000000 --- a/userspace/ksud_magic/src/apk_sign.rs +++ /dev/null @@ -1,115 +0,0 @@ -use anyhow::{Result, ensure}; -use std::io::{Read, Seek, SeekFrom}; - -pub fn get_apk_signature(apk: &str) -> Result<(u32, String)> { - let mut buffer = [0u8; 0x10]; - let mut size4 = [0u8; 4]; - let mut size8 = [0u8; 8]; - let mut size_of_block = [0u8; 8]; - - let mut f = std::fs::File::open(apk)?; - - let mut i = 0; - loop { - let mut n = [0u8; 2]; - f.seek(SeekFrom::End(-i - 2))?; - f.read_exact(&mut n)?; - - let n = u16::from_le_bytes(n); - if i64::from(n) == i { - f.seek(SeekFrom::Current(-22))?; - f.read_exact(&mut size4)?; - - if u32::from_le_bytes(size4) ^ 0xcafe_babe_u32 == 0xccfb_f1ee_u32 { - if i > 0 { - println!("warning: comment length is {i}"); - } - break; - } - } - - ensure!(n != 0xffff, "not a zip file"); - - i += 1; - } - - f.seek(SeekFrom::Current(12))?; - // offset - f.read_exact(&mut size4)?; - f.seek(SeekFrom::Start(u64::from(u32::from_le_bytes(size4)) - 0x18))?; - - f.read_exact(&mut size8)?; - f.read_exact(&mut buffer)?; - - ensure!(&buffer == b"APK Sig Block 42", "Can not found sig block"); - - let pos = u64::from(u32::from_le_bytes(size4)) - (u64::from_le_bytes(size8) + 0x8); - f.seek(SeekFrom::Start(pos))?; - f.read_exact(&mut size_of_block)?; - - ensure!(size_of_block == size8, "not a signed apk"); - - let mut v2_signing: Option<(u32, String)> = None; - let mut v3_signing_exist = false; - let mut v3_1_signing_exist = false; - - loop { - let mut id = [0u8; 4]; - let mut offset = 4u32; - - f.read_exact(&mut size8)?; // sequence length - if size8 == size_of_block { - break; - } - - f.read_exact(&mut id)?; // id - - let id = u32::from_le_bytes(id); - if id == 0x7109_871a_u32 { - v2_signing = Some(calc_cert_sha256(&mut f, &mut size4, &mut offset)?); - } else if id == 0xf053_68c0_u32 { - // v3 signature scheme - v3_signing_exist = true; - } else if id == 0x1b93_ad61_u32 { - // v3.1 signature scheme: credits to vvb2060 - v3_1_signing_exist = true; - } - - f.seek(SeekFrom::Current( - i64::from_le_bytes(size8) - i64::from(offset), - ))?; - } - - if v3_signing_exist || v3_1_signing_exist { - return Err(anyhow::anyhow!("Unexpected v3 signature found!",)); - } - - v2_signing.ok_or(anyhow::anyhow!("No signature found!")) -} - -fn calc_cert_sha256( - f: &mut std::fs::File, - size4: &mut [u8; 4], - offset: &mut u32, -) -> Result<(u32, String)> { - f.read_exact(size4)?; // signer-sequence length - f.read_exact(size4)?; // signer length - f.read_exact(size4)?; // signed data length - *offset += 0x4 * 3; - - f.read_exact(size4)?; // digests-sequence length - let pos = u32::from_le_bytes(*size4); // skip digests - f.seek(SeekFrom::Current(i64::from(pos)))?; - *offset += 0x4 + pos; - - f.read_exact(size4)?; // certificates length - f.read_exact(size4)?; // certificate length - *offset += 0x4 * 2; - - let cert_len = u32::from_le_bytes(*size4); - let mut cert: Vec = vec![0; cert_len as usize]; - f.read_exact(&mut cert)?; - *offset += cert_len; - - Ok((cert_len, sha256::digest(&cert))) -} diff --git a/userspace/ksud_magic/src/assets.rs b/userspace/ksud_magic/src/assets.rs deleted file mode 100644 index bf8d5069..00000000 --- a/userspace/ksud_magic/src/assets.rs +++ /dev/null @@ -1,58 +0,0 @@ -use anyhow::Result; -use const_format::concatcp; -use rust_embed::RustEmbed; -use std::path::Path; - -use crate::{defs::BINARY_DIR, utils}; - -pub const RESETPROP_PATH: &str = concatcp!(BINARY_DIR, "resetprop"); -pub const BUSYBOX_PATH: &str = concatcp!(BINARY_DIR, "busybox"); - -pub const BOOTCTL_PATH: &str = concatcp!(BINARY_DIR, "bootctl"); - -#[allow(dead_code)] -pub const SUSFSD_PATH: &str = concatcp!(BINARY_DIR, "susfsd"); - -#[cfg(all(target_arch = "aarch64", target_os = "android"))] -#[derive(RustEmbed)] -#[folder = "bin/aarch64"] -struct Asset; - -#[cfg(all(target_arch = "arm", target_os = "android"))] -#[derive(RustEmbed)] -#[folder = "bin/arm"] -struct Asset; - -#[cfg(all(target_arch = "x86_64", target_os = "android"))] -#[derive(RustEmbed)] -#[folder = "bin/x86_64"] -struct Asset; - -pub fn ensure_binaries(ignore_if_exist: bool) -> Result<()> { - for file in Asset::iter() { - if file == "ksuinit" || file.ends_with(".ko") { - // don't extract ksuinit and kernel modules - continue; - } - let asset = Asset::get(&file).ok_or(anyhow::anyhow!("asset not found: {}", file))?; - utils::ensure_binary(format!("{BINARY_DIR}{file}"), &asset.data, ignore_if_exist)? - } - Ok(()) -} - -pub fn copy_assets_to_file(name: &str, dst: impl AsRef) -> Result<()> { - let asset = Asset::get(name).ok_or(anyhow::anyhow!("asset not found: {}", name))?; - std::fs::write(dst, asset.data)?; - Ok(()) -} - -pub fn list_supported_kmi() -> Result> { - let mut list = Vec::new(); - for file in Asset::iter() { - // kmi_name = "xxx_kernelsu.ko" - if let Some(kmi) = file.strip_suffix("_kernelsu.ko") { - list.push(kmi.to_string()); - } - } - Ok(list) -} diff --git a/userspace/ksud_magic/src/boot_patch.rs b/userspace/ksud_magic/src/boot_patch.rs deleted file mode 100644 index ba835287..00000000 --- a/userspace/ksud_magic/src/boot_patch.rs +++ /dev/null @@ -1,887 +0,0 @@ -#[cfg(unix)] -use std::os::unix::fs::PermissionsExt; -use std::path::Path; -use std::path::PathBuf; -use std::process::Command; -use std::process::Stdio; - -use anyhow::Context; -use anyhow::Result; -use anyhow::anyhow; -use anyhow::bail; -use anyhow::ensure; -use regex_lite::Regex; -use which::which; - -use crate::defs; -use crate::defs::BACKUP_FILENAME; -use crate::defs::{KSU_BACKUP_DIR, KSU_BACKUP_FILE_PREFIX}; -use crate::{assets, utils}; - -#[cfg(target_os = "android")] -fn ensure_gki_kernel() -> Result<()> { - let version = get_kernel_version()?; - let is_gki = version.0 == 5 && version.1 >= 10 || version.2 > 5; - ensure!(is_gki, "only support GKI kernel"); - Ok(()) -} - -#[cfg(target_os = "android")] -pub fn get_kernel_version() -> Result<(i32, i32, i32)> { - let uname = rustix::system::uname(); - let version = uname.release().to_string_lossy(); - let re = Regex::new(r"(\d+)\.(\d+)\.(\d+)")?; - if let Some(captures) = re.captures(&version) { - let major = captures - .get(1) - .and_then(|m| m.as_str().parse::().ok()) - .ok_or_else(|| anyhow!("Major version parse error"))?; - let minor = captures - .get(2) - .and_then(|m| m.as_str().parse::().ok()) - .ok_or_else(|| anyhow!("Minor version parse error"))?; - let patch = captures - .get(3) - .and_then(|m| m.as_str().parse::().ok()) - .ok_or_else(|| anyhow!("Patch version parse error"))?; - Ok((major, minor, patch)) - } else { - Err(anyhow!("Invalid kernel version string")) - } -} - -#[cfg(target_os = "android")] -fn parse_kmi(version: &str) -> Result { - let re = Regex::new(r"(.* )?(\d+\.\d+)(\S+)?(android\d+)(.*)")?; - let cap = re - .captures(version) - .ok_or_else(|| anyhow::anyhow!("Failed to get KMI from boot/modules"))?; - let android_version = cap.get(4).map_or("", |m| m.as_str()); - let kernel_version = cap.get(2).map_or("", |m| m.as_str()); - Ok(format!("{android_version}-{kernel_version}")) -} - -#[cfg(target_os = "android")] -fn parse_kmi_from_uname() -> Result { - let uname = rustix::system::uname(); - let version = uname.release().to_string_lossy(); - parse_kmi(&version) -} - -#[cfg(target_os = "android")] -fn parse_kmi_from_modules() -> Result { - use std::io::BufRead; - // find a *.ko in /vendor/lib/modules - let modfile = std::fs::read_dir("/vendor/lib/modules")? - .filter_map(Result::ok) - .find(|entry| entry.path().extension().is_some_and(|ext| ext == "ko")) - .map(|entry| entry.path()) - .ok_or_else(|| anyhow!("No kernel module found"))?; - let output = Command::new("modinfo").arg(modfile).output()?; - for line in output.stdout.lines().map_while(Result::ok) { - if line.starts_with("vermagic") { - return parse_kmi(&line); - } - } - anyhow::bail!("Parse KMI from modules failed") -} - -#[cfg(target_os = "android")] -pub fn get_current_kmi() -> Result { - parse_kmi_from_uname().or_else(|_| parse_kmi_from_modules()) -} - -#[cfg(not(target_os = "android"))] -pub fn get_current_kmi() -> Result { - bail!("Unsupported platform") -} - -fn parse_kmi_from_kernel(kernel: &PathBuf, workdir: &Path) -> Result { - use std::fs::{File, copy}; - 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) - .stdout(Stdio::null()) - .stderr(Stdio::null()) - .arg("cpio") - .arg("ramdisk.cpio") - .arg(cmd) - .status()?; - - ensure!(status.success(), "magiskboot cpio {} failed", cmd); - Ok(()) -} - -fn do_vendor_init_boot_cpio_cmd(magiskboot: &Path, workdir: &Path, cmd: &str) -> Result<()> { - let vendor_init_boot_cpio = workdir.join("vendor_ramdisk").join("init_boot.cpio"); - let status = Command::new(magiskboot) - .current_dir(workdir) - .stdout(Stdio::null()) - .stderr(Stdio::null()) - .arg("cpio") - .arg(vendor_init_boot_cpio) - .arg(cmd) - .status()?; - - ensure!(status.success(), "magiskboot cpio {} failed", cmd); - Ok(()) -} - -fn do_vendor_ramdisk_cpio_cmd(magiskboot: &Path, workdir: &Path, cmd: &str) -> Result<()> { - let vendor_ramdisk_cpio = workdir.join("vendor_ramdisk").join("ramdisk.cpio"); - let status = Command::new(magiskboot) - .current_dir(workdir) - .stdout(Stdio::null()) - .stderr(Stdio::null()) - .arg("cpio") - .arg(vendor_ramdisk_cpio) - .arg(cmd) - .status()?; - - ensure!(status.success(), "magiskboot cpio {} failed", cmd); - Ok(()) -} - -fn is_magisk_patched(magiskboot: &Path, workdir: &Path) -> Result { - let status = Command::new(magiskboot) - .current_dir(workdir) - .stdout(Stdio::null()) - .stderr(Stdio::null()) - .args(["cpio", "ramdisk.cpio", "test"]) - .status()?; - - // 0: stock, 1: magisk - Ok(status.code() == Some(1)) -} - -fn is_magisk_patched_vendor_init_boot(magiskboot: &Path, workdir: &Path) -> Result { - let vendor_init_boot_cpio = workdir.join("vendor_ramdisk").join("init_boot.cpio"); - let status = Command::new(magiskboot) - .current_dir(workdir) - .stdout(Stdio::null()) - .stderr(Stdio::null()) - .args(["cpio", vendor_init_boot_cpio.to_str().unwrap(), "test"]) - .status()?; - - // 0: stock, 1: magisk - Ok(status.code() == Some(1)) -} - -fn is_magisk_patched_vendor_ramdisk(magiskboot: &Path, workdir: &Path) -> Result { - let vendor_ramdisk_cpio = workdir.join("vendor_ramdisk").join("ramdisk.cpio"); - let status = Command::new(magiskboot) - .current_dir(workdir) - .stdout(Stdio::null()) - .stderr(Stdio::null()) - .args(["cpio", vendor_ramdisk_cpio.to_str().unwrap(), "test"]) - .status()?; - - // 0: stock, 1: magisk - Ok(status.code() == Some(1)) -} - -fn is_kernelsu_patched(magiskboot: &Path, workdir: &Path) -> Result { - let status = Command::new(magiskboot) - .current_dir(workdir) - .stdout(Stdio::null()) - .stderr(Stdio::null()) - .args(["cpio", "ramdisk.cpio", "exists kernelsu.ko"]) - .status()?; - - Ok(status.success()) -} - -fn is_kernelsu_patched_vendor_init_boot(magiskboot: &Path, workdir: &Path) -> Result { - let vendor_ramdisk_cpio = workdir.join("vendor_ramdisk").join("init_boot.cpio"); - let status = Command::new(magiskboot) - .current_dir(workdir) - .stdout(Stdio::null()) - .stderr(Stdio::null()) - .args([ - "cpio", - vendor_ramdisk_cpio.to_str().unwrap(), - "exists kernelsu.ko", - ]) - .status()?; - - Ok(status.success()) -} - -fn is_kernelsu_patched_vendor_ramdisk(magiskboot: &Path, workdir: &Path) -> Result { - let vendor_ramdisk_cpio = workdir.join("vendor_ramdisk").join("ramdisk.cpio"); - let status = Command::new(magiskboot) - .current_dir(workdir) - .stdout(Stdio::null()) - .stderr(Stdio::null()) - .args([ - "cpio", - vendor_ramdisk_cpio.to_str().unwrap(), - "exists kernelsu.ko", - ]) - .status()?; - - Ok(status.success()) -} - -fn dd, Q: AsRef>(ifile: P, ofile: Q) -> Result<()> { - let status = Command::new("dd") - .stdout(Stdio::null()) - .stderr(Stdio::null()) - .arg(format!("if={}", ifile.as_ref().display())) - .arg(format!("of={}", ofile.as_ref().display())) - .status()?; - ensure!( - status.success(), - "dd if={:?} of={:?} failed", - ifile.as_ref(), - ofile.as_ref() - ); - Ok(()) -} - -pub fn restore( - image: Option, - magiskboot_path: Option, - flash: bool, -) -> Result<()> { - let tmpdir = tempfile::Builder::new() - .prefix("KernelSU Next") - .tempdir() - .context("create temp dir failed")?; - let workdir = tmpdir.path(); - let magiskboot = find_magiskboot(magiskboot_path, workdir)?; - - let kmi = get_current_kmi().unwrap_or_else(|_| String::from("")); - - let skip_init = kmi.starts_with("android12-"); - - let (bootimage, bootdevice) = find_boot_image(&image, skip_init, false, false, workdir)?; - - println!("- Unpacking boot image"); - let status = Command::new(&magiskboot) - .current_dir(workdir) - .stdout(Stdio::null()) - .stderr(Stdio::null()) - .arg("unpack") - .arg(bootimage.display().to_string()) - .status()?; - ensure!(status.success(), "magiskboot unpack failed"); - - let no_ramdisk = !workdir.join("ramdisk.cpio").exists(); - let no_vendor_init_boot = !workdir - .join("vendor_ramdisk") - .join("init_boot.cpio") - .exists(); - let no_vendor_ramdisk = !workdir.join("vendor_ramdisk").join("ramdisk.cpio").exists(); - let is_kernelsu_patched = is_kernelsu_patched(&magiskboot, workdir)?; - let is_kernelsu_patched_vendor_init_boot = - is_kernelsu_patched_vendor_init_boot(&magiskboot, workdir)?; - let is_kernelsu_patched_vendor_ramdisk = - is_kernelsu_patched_vendor_ramdisk(&magiskboot, workdir)?; - ensure!( - is_kernelsu_patched - || is_kernelsu_patched_vendor_init_boot - || is_kernelsu_patched_vendor_ramdisk, - "boot image is not patched by KernelSU Next" - ); - - let mut new_boot = None; - let mut from_backup = false; - - #[cfg(target_os = "android")] - if do_cpio_cmd(&magiskboot, workdir, &format!("exists {BACKUP_FILENAME}")).is_ok() { - do_cpio_cmd( - &magiskboot, - workdir, - &format!("extract {BACKUP_FILENAME} {BACKUP_FILENAME}"), - )?; - let sha = std::fs::read(workdir.join(BACKUP_FILENAME))?; - let sha = String::from_utf8(sha)?; - let sha = sha.trim(); - let backup_path = - PathBuf::from(KSU_BACKUP_DIR).join(format!("{KSU_BACKUP_FILE_PREFIX}{sha}")); - if backup_path.is_file() { - new_boot = Some(backup_path); - from_backup = true; - } else { - println!("- Warning: no backup {backup_path:?} found!"); - } - - if let Err(e) = clean_backup(sha) { - println!("- Warning: Cleanup backup image failed: {e}"); - } - } else { - println!("- Backup info is absent!"); - } - - if new_boot.is_none() { - if no_ramdisk { - if !no_vendor_init_boot { - // vendor init_boot restore - do_vendor_init_boot_cpio_cmd(&magiskboot, workdir, "rm kernelsu.ko")?; - - let status = - do_vendor_init_boot_cpio_cmd(&magiskboot, workdir, "exists init.real").is_ok(); - if status { - do_vendor_init_boot_cpio_cmd(&magiskboot, workdir, "mv init.real init")?; - } else { - let vendor_init_boot = workdir.join("vendor_ramdisk").join("init_boot.cpio"); - std::fs::remove_file(vendor_init_boot)?; - } - } else if !no_vendor_ramdisk { - // vendor ramdisk restore - do_vendor_ramdisk_cpio_cmd(&magiskboot, workdir, "rm kernelsu.ko")?; - - let status = - do_vendor_ramdisk_cpio_cmd(&magiskboot, workdir, "exists init.real").is_ok(); - if status { - do_vendor_ramdisk_cpio_cmd(&magiskboot, workdir, "mv init.real init")?; - } else { - let vendor_ramdisk = workdir.join("vendor_ramdisk").join("ramdisk.cpio"); - std::fs::remove_file(vendor_ramdisk)?; - } - } - } else { - // remove kernelsu.ko - do_cpio_cmd(&magiskboot, workdir, "rm kernelsu.ko")?; - - // if init.real exists, restore it - let status = do_cpio_cmd(&magiskboot, workdir, "exists init.real").is_ok(); - if status { - do_cpio_cmd(&magiskboot, workdir, "mv init.real init")?; - } else { - let ramdisk = workdir.join("ramdisk.cpio"); - std::fs::remove_file(ramdisk)?; - } - } - - println!("- Repacking boot image"); - let status = Command::new(&magiskboot) - .current_dir(workdir) - .stdout(Stdio::null()) - .stderr(Stdio::null()) - .arg("repack") - .arg(bootimage.display().to_string()) - .status()?; - ensure!(status.success(), "magiskboot repack failed"); - new_boot = Some(workdir.join("new-boot.img")); - } - - let new_boot = new_boot.unwrap(); - - if image.is_some() { - // if image is specified, write to output file - let output_dir = std::env::current_dir()?; - let now = chrono::Utc::now(); - let output_image = output_dir.join(format!( - "kernelsu_next_restore_{}.img", - now.format("%Y%m%d_%H%M%S") - )); - - if from_backup || std::fs::rename(&new_boot, &output_image).is_err() { - std::fs::copy(&new_boot, &output_image).context("copy out new boot failed")?; - } - println!("- Output file is written to"); - println!("- {}", output_image.display().to_string().trim_matches('"')); - } - if flash { - if from_backup { - println!("- Flashing new boot image from {}", new_boot.display()); - } else { - println!("- Flashing new boot image"); - } - flash_boot(&bootdevice, new_boot)?; - } - println!("- Done!"); - Ok(()) -} - -#[allow(clippy::too_many_arguments)] -pub fn patch( - image: Option, - kernel: Option, - kmod: Option, - init: Option, - ota: bool, - flash: bool, - out: Option, - magiskboot: Option, - kmi: Option, -) -> Result<()> { - let result = do_patch(image, kernel, kmod, init, ota, flash, out, magiskboot, kmi); - if let Err(ref e) = result { - println!("- Install Error: {e}"); - } - result -} - -#[allow(clippy::too_many_arguments)] -fn do_patch( - image: Option, - kernel: Option, - kmod: Option, - init: Option, - ota: bool, - flash: bool, - out: Option, - magiskboot_path: Option, - kmi: Option, -) -> Result<()> { - let patch_file = image.is_some(); - - #[cfg(target_os = "android")] - if !patch_file { - ensure_gki_kernel()?; - } - - let is_replace_kernel = kernel.is_some(); - - if is_replace_kernel { - ensure!( - init.is_none() && kmod.is_none(), - "init and module must not be specified." - ); - } - - let tmpdir = tempfile::Builder::new() - .prefix("KernelSU Next") - .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 { - 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-"); - - let (bootimage, bootdevice) = - find_boot_image(&image, skip_init, ota, is_replace_kernel, workdir)?; - - let bootimage = bootimage.display().to_string(); - - // try extract magiskboot/bootctl - let _ = assets::ensure_binaries(false); - - if let Some(kernel) = kernel { - std::fs::copy(kernel, workdir.join("kernel")).context("copy kernel from failed")?; - } - - println!("- Preparing assets"); - - let kmod_file = workdir.join("kernelsu.ko"); - if let Some(kmod) = kmod { - std::fs::copy(kmod, kmod_file).context("copy kernel module failed")?; - } else { - // If kmod is not specified, extract from assets - println!("- KMI: {kmi}"); - let name = format!("{kmi}_kernelsu.ko"); - assets::copy_assets_to_file(&name, kmod_file) - .with_context(|| format!("Failed to copy {name}"))?; - }; - - let init_file = workdir.join("init"); - if let Some(init) = init { - std::fs::copy(init, init_file).context("copy init failed")?; - } else { - assets::copy_assets_to_file("ksuinit", init_file).context("copy ksuinit failed")?; - } - - println!("- Unpacking boot image"); - let status = Command::new(&magiskboot) - .current_dir(workdir) - .stdout(Stdio::null()) - .stderr(Stdio::null()) - .arg("unpack") - .arg(&bootimage) - .status()?; - ensure!(status.success(), "magiskboot unpack failed"); - - let no_ramdisk = !workdir.join("ramdisk.cpio").exists(); - let no_vendor_init_boot = !workdir - .join("vendor_ramdisk") - .join("init_boot.cpio") - .exists(); - let no_vendor_ramdisk = !workdir.join("vendor_ramdisk").join("ramdisk.cpio").exists(); - if no_ramdisk && no_vendor_init_boot && no_vendor_ramdisk { - bail!("No compatible ramdisk found."); - } - let is_magisk_patched = is_magisk_patched(&magiskboot, workdir)?; - let is_magisk_patched_vendor_init_boot = - is_magisk_patched_vendor_init_boot(&magiskboot, workdir)?; - let is_magisk_patched_vendor_ramdisk = is_magisk_patched_vendor_ramdisk(&magiskboot, workdir)?; - ensure!( - !is_magisk_patched - || !is_magisk_patched_vendor_init_boot - || !is_magisk_patched_vendor_ramdisk, - "Cannot work with Magisk patched image" - ); - - println!("- Adding KernelSU Next LKM"); - let is_kernelsu_patched = is_kernelsu_patched(&magiskboot, workdir)?; - let is_kernelsu_patched_vendor_init_boot = - is_kernelsu_patched_vendor_init_boot(&magiskboot, workdir)?; - let is_kernelsu_patched_vendor_ramdisk = - is_kernelsu_patched_vendor_ramdisk(&magiskboot, workdir)?; - - let mut need_backup = false; - if !is_kernelsu_patched - || (no_ramdisk && !is_kernelsu_patched_vendor_init_boot) - || (no_ramdisk && no_vendor_init_boot && !is_kernelsu_patched_vendor_ramdisk) - { - if no_ramdisk { - if !no_vendor_init_boot { - // vendor init_boot patching - let status = do_vendor_init_boot_cpio_cmd(&magiskboot, workdir, "exists init"); - if status.is_ok() { - do_vendor_init_boot_cpio_cmd(&magiskboot, workdir, "mv init init.real")?; - } - } else if !no_vendor_ramdisk { - // vendor ramdisk patching - let status = do_vendor_ramdisk_cpio_cmd(&magiskboot, workdir, "exists init"); - if status.is_ok() { - do_vendor_ramdisk_cpio_cmd(&magiskboot, workdir, "mv init init.real")?; - } - } - } else { - // kernelsu.ko is not exist, backup init if necessary - let status = do_cpio_cmd(&magiskboot, workdir, "exists init"); - if status.is_ok() { - do_cpio_cmd(&magiskboot, workdir, "mv init init.real")?; - } - need_backup = flash; - } - } - - if no_ramdisk { - if !no_vendor_init_boot { - do_vendor_init_boot_cpio_cmd(&magiskboot, workdir, "add 0755 init init")?; - do_vendor_init_boot_cpio_cmd(&magiskboot, workdir, "add 0755 kernelsu.ko kernelsu.ko")?; - } else if !no_vendor_ramdisk { - do_vendor_ramdisk_cpio_cmd(&magiskboot, workdir, "add 0750 init init")?; - do_vendor_ramdisk_cpio_cmd(&magiskboot, workdir, "add 0750 kernelsu.ko kernelsu.ko")?; - } - } else { - do_cpio_cmd(&magiskboot, workdir, "add 0755 init init")?; - do_cpio_cmd(&magiskboot, workdir, "add 0755 kernelsu.ko kernelsu.ko")?; - } - - #[cfg(target_os = "android")] - if need_backup { - if let Err(e) = do_backup(&magiskboot, workdir, &bootimage) { - println!("- Backup stock image failed: {e}"); - } - } - - println!("- Repacking boot image"); - // magiskboot repack boot.img - let status = Command::new(&magiskboot) - .current_dir(workdir) - .stdout(Stdio::null()) - .stderr(Stdio::null()) - .arg("repack") - .arg(&bootimage) - .status()?; - ensure!(status.success(), "magiskboot repack failed"); - let new_boot = workdir.join("new-boot.img"); - - if patch_file { - // if image is specified, write to output file - let output_dir = out.unwrap_or(std::env::current_dir()?); - let now = chrono::Utc::now(); - let output_image = output_dir.join(format!( - "kernelsu_next_patched_{}.img", - now.format("%Y%m%d_%H%M%S") - )); - - if std::fs::rename(&new_boot, &output_image).is_err() { - std::fs::copy(&new_boot, &output_image).context("copy out new boot failed")?; - } - println!("- Output file is written to"); - println!("- {}", output_image.display().to_string().trim_matches('"')); - } - - if flash { - println!("- Flashing new boot image"); - flash_boot(&bootdevice, new_boot)?; - - if ota { - post_ota()?; - } - } - - println!("- Done!"); - Ok(()) -} - -#[cfg(target_os = "android")] -fn calculate_sha1(file_path: impl AsRef) -> Result { - use sha1::Digest; - use std::io::Read; - let mut file = std::fs::File::open(file_path.as_ref())?; - let mut hasher = sha1::Sha1::new(); - let mut buffer = [0; 1024]; - - loop { - let n = file.read(&mut buffer)?; - if n == 0 { - break; - } - hasher.update(&buffer[..n]); - } - - let result = hasher.finalize(); - Ok(format!("{result:x}")) -} - -#[cfg(target_os = "android")] -fn do_backup(magiskboot: &Path, workdir: &Path, image: &str) -> Result<()> { - let sha1 = calculate_sha1(image)?; - let filename = format!("{KSU_BACKUP_FILE_PREFIX}{sha1}"); - - println!("- Backup stock boot image"); - // magiskboot cpio ramdisk.cpio 'add 0755 $BACKUP_FILENAME' - let target = format!("{KSU_BACKUP_DIR}{filename}"); - std::fs::copy(image, &target).with_context(|| format!("backup to {target}"))?; - std::fs::write(workdir.join(BACKUP_FILENAME), sha1.as_bytes()).context("write sha1")?; - do_cpio_cmd( - magiskboot, - workdir, - &format!("add 0755 {BACKUP_FILENAME} {BACKUP_FILENAME}"), - )?; - println!("- Stock image has been backup to"); - println!("- {target}"); - Ok(()) -} - -#[cfg(target_os = "android")] -fn clean_backup(sha1: &str) -> Result<()> { - println!("- Clean up backup"); - let backup_name = format!("{KSU_BACKUP_FILE_PREFIX}{sha1}"); - let dir = std::fs::read_dir(defs::KSU_BACKUP_DIR)?; - for entry in dir.flatten() { - let path = entry.path(); - if !path.is_file() { - continue; - } - if let Some(name) = path.file_name() { - let name = name.to_string_lossy().to_string(); - if name != backup_name - && name.starts_with(KSU_BACKUP_FILE_PREFIX) - && std::fs::remove_file(path).is_ok() - { - println!("- removed {name}"); - } - } - } - Ok(()) -} - -fn flash_boot(bootdevice: &Option, new_boot: PathBuf) -> Result<()> { - let Some(bootdevice) = bootdevice else { - bail!("boot device not found") - }; - let status = Command::new("blockdev") - .arg("--setrw") - .arg(bootdevice) - .status()?; - ensure!(status.success(), "set boot device rw failed"); - dd(new_boot, bootdevice).context("flash boot failed")?; - Ok(()) -} - -fn find_magiskboot(magiskboot_path: Option, workdir: &Path) -> Result { - let magiskboot = { - if which("magiskboot").is_ok() { - let _ = assets::ensure_binaries(true); - "magiskboot".into() - } else { - // magiskboot is not in $PATH, use builtin or specified one - let magiskboot = if let Some(magiskboot_path) = magiskboot_path { - std::fs::canonicalize(magiskboot_path)? - } else { - let magiskboot_path = workdir.join("magiskboot"); - assets::copy_assets_to_file("magiskboot", &magiskboot_path) - .context("copy magiskboot failed")?; - magiskboot_path - }; - ensure!(magiskboot.exists(), "{magiskboot:?} is not exist"); - #[cfg(unix)] - let _ = std::fs::set_permissions(&magiskboot, std::fs::Permissions::from_mode(0o755)); - magiskboot - } - }; - Ok(magiskboot) -} - -fn find_boot_image( - image: &Option, - skip_init: bool, - ota: bool, - is_replace_kernel: bool, - workdir: &Path, -) -> Result<(PathBuf, Option)> { - let bootimage; - let mut bootdevice = None; - if let Some(ref image) = *image { - ensure!(image.exists(), "boot image not found"); - bootimage = std::fs::canonicalize(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"); - } - let mut slot_suffix = - utils::getprop("ro.boot.slot_suffix").unwrap_or_else(|| String::from("")); - - if !slot_suffix.is_empty() && ota { - if slot_suffix == "_a" { - slot_suffix = "_b".to_string() - } else { - slot_suffix = "_a".to_string() - } - }; - - let init_boot_exist = - Path::new(&format!("/dev/block/by-name/init_boot{slot_suffix}")).exists(); - let vendor_boot_exist = - Path::new(&format!("/dev/block/by-name/vendor_boot{slot_suffix}")).exists(); - let boot_partition = if !is_replace_kernel && init_boot_exist && !skip_init { - format!("/dev/block/by-name/init_boot{slot_suffix}") - } else if !is_replace_kernel && vendor_boot_exist && !skip_init { - format!("/dev/block/by-name/vendor_boot{slot_suffix}") - } else { - format!("/dev/block/by-name/boot{slot_suffix}") - }; - - println!("- Bootdevice: {boot_partition}"); - let tmp_boot_path = workdir.join("boot.img"); - - dd(&boot_partition, &tmp_boot_path)?; - - ensure!(tmp_boot_path.exists(), "boot image not found"); - - bootimage = tmp_boot_path; - bootdevice = Some(boot_partition); - }; - Ok((bootimage, bootdevice)) -} - -fn post_ota() -> Result<()> { - use crate::defs::ADB_DIR; - use assets::BOOTCTL_PATH; - let status = Command::new(BOOTCTL_PATH).arg("hal-info").status()?; - if !status.success() { - return Ok(()); - } - - let current_slot = Command::new(BOOTCTL_PATH) - .arg("get-current-slot") - .output()? - .stdout; - let current_slot = String::from_utf8(current_slot)?; - let current_slot = current_slot.trim(); - let target_slot = if current_slot == "0" { 1 } else { 0 }; - - Command::new(BOOTCTL_PATH) - .arg(format!("set-active-boot-slot {target_slot}")) - .status()?; - - let post_fs_data = std::path::Path::new(ADB_DIR).join("post-fs-data.d"); - utils::ensure_dir_exists(&post_fs_data)?; - let post_ota_sh = post_fs_data.join("post_ota.sh"); - - let sh_content = format!( - r###" -{BOOTCTL_PATH} mark-boot-successful -rm -f {BOOTCTL_PATH} -rm -f /data/adb/post-fs-data.d/post_ota.sh -"### - ); - - std::fs::write(&post_ota_sh, sh_content)?; - #[cfg(unix)] - std::fs::set_permissions(post_ota_sh, std::fs::Permissions::from_mode(0o755))?; - - Ok(()) -} diff --git a/userspace/ksud_magic/src/cli.rs b/userspace/ksud_magic/src/cli.rs deleted file mode 100644 index db82dc72..00000000 --- a/userspace/ksud_magic/src/cli.rs +++ /dev/null @@ -1,394 +0,0 @@ -use anyhow::{Ok, Result}; -use clap::Parser; -use std::path::{Path, PathBuf}; - -#[cfg(target_os = "android")] -use android_logger::Config; -#[cfg(target_os = "android")] -use log::LevelFilter; - -use crate::defs::KSUD_VERBOSE_LOG_FILE; -use crate::{apk_sign, assets, debug, defs, init_event, ksucalls, module, utils}; - -/// KernelSU Next userspace cli -#[derive(Parser, Debug)] -#[command(author, version = defs::VERSION_NAME, about, long_about = None)] -struct Args { - #[command(subcommand)] - command: Commands, - - #[arg(short, long, default_value_t = cfg!(debug_assertions))] - verbose: bool, -} - -#[derive(clap::Subcommand, Debug)] -enum Commands { - /// Manage KernelSU Next modules - Module { - #[command(subcommand)] - command: Module, - }, - - /// Trigger `post-fs-data` event - PostFsData, - - /// Trigger `service` event - Services, - - /// Trigger `boot-complete` event - BootCompleted, - - /// Install KernelSU Next userspace component to system - Install { - #[arg(long, default_value = None)] - magiskboot: Option, - }, - - /// Uninstall KernelSU Next modules and itself(LKM Only) - Uninstall { - /// magiskboot path, if not specified, will search from $PATH - #[arg(long, default_value = None)] - magiskboot: Option, - }, - - /// SELinux policy Patch tool - Sepolicy { - #[command(subcommand)] - command: Sepolicy, - }, - - /// Manage App Profiles - Profile { - #[command(subcommand)] - command: Profile, - }, - - /// Patch boot or init_boot images to apply KernelSU Next - BootPatch { - /// boot image path, if not specified, will try to find the boot image automatically - #[arg(short, long)] - boot: Option, - - /// kernel image path to replace - #[arg(short, long)] - kernel: Option, - - /// LKM module path to replace, if not specified, will use the builtin one - #[arg(short, long)] - module: Option, - - /// init to be replaced - #[arg(short, long, requires("module"))] - init: Option, - - /// will use another slot when boot image is not specified - #[arg(short = 'u', long, default_value = "false")] - ota: bool, - - /// Flash it to boot partition after patch - #[arg(short, long, default_value = "false")] - flash: bool, - - /// output path, if not specified, will use current directory - #[arg(short, long, default_value = None)] - out: Option, - - /// magiskboot path, if not specified, will search from $PATH - #[arg(long, default_value = None)] - magiskboot: Option, - - /// KMI version, if specified, will use the specified KMI - #[arg(long, default_value = None)] - kmi: Option, - }, - - /// Restore boot or init_boot images patched by KernelSU Next - BootRestore { - /// boot image path, if not specified, will try to find the boot image automatically - #[arg(short, long)] - boot: Option, - - /// Flash it to boot partition after patch - #[arg(short, long, default_value = "false")] - flash: bool, - - /// magiskboot path, if not specified, will search from $PATH - #[arg(long, default_value = None)] - magiskboot: Option, - }, - - /// Show boot information - BootInfo { - #[command(subcommand)] - command: BootInfo, - }, - - /// For developers - Debug { - #[command(subcommand)] - command: Debug, - }, -} - -#[derive(clap::Subcommand, Debug)] -enum BootInfo { - /// show current kmi version - CurrentKmi, - - /// show supported kmi versions - SupportedKmi, -} - -#[derive(clap::Subcommand, Debug)] -enum Debug { - /// Set the manager app, kernel CONFIG_KSU_DEBUG should be enabled. - SetManager { - /// manager package name - #[arg(default_value_t = String::from("com.rifsxd.ksunext"))] - apk: String, - }, - - /// Get apk size and hash - GetSign { - /// apk path - apk: String, - }, - - /// Root Shell - Su { - /// switch to gloabl mount namespace - #[arg(short, long, default_value = "false")] - global_mnt: bool, - }, - - /// Get kernel version - Version, - - Mount, - - /// For testing - Test, -} - -#[derive(clap::Subcommand, Debug)] -enum Sepolicy { - /// Patch sepolicy - Patch { - /// sepolicy statements - sepolicy: String, - }, - - /// Apply sepolicy from file - Apply { - /// sepolicy file path - file: String, - }, - - /// Check if sepolicy statement is supported/valid - Check { - /// sepolicy statements - sepolicy: String, - }, -} - -#[derive(clap::Subcommand, Debug)] -enum Module { - /// Install module - Install { - /// module zip file path - zip: String, - }, - - /// Uninstall module - Uninstall { - /// module id - id: String, - }, - - /// Restore module - Restore { - /// module id - id: String, - }, - - /// enable module - Enable { - /// module id - id: String, - }, - - /// disable module - Disable { - // module id - id: String, - }, - - /// run action for module - Action { - // module id - id: String, - }, - - /// current mount system - Mount, - - /// list all modules - List, -} - -#[derive(clap::Subcommand, Debug)] -enum Profile { - /// get root profile's selinux policy of - GetSepolicy { - /// package name - package: String, - }, - - /// set root profile's selinux policy of to - SetSepolicy { - /// package name - package: String, - /// policy statements - policy: String, - }, - - /// get template of - GetTemplate { - /// template id - id: String, - }, - - /// set template of to