drivers: kernelsu: Migrate to KernelSU-Next
Some checks failed
Build on Tag / build (push) Has been cancelled
Some checks failed
Build on Tag / build (push) Has been cancelled
This supports 4.14
This commit is contained in:
5
drivers/staging/kernelsu/.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
5
drivers/staging/kernelsu/.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: Feature Request
|
||||
url: https://github.com/tiann/KernelSU/issues/1705
|
||||
about: "We do not accept external Feature Requests, see this link for more details."
|
||||
@@ -1,39 +0,0 @@
|
||||
name: Feature Request
|
||||
description: "Suggest an idea for this project"
|
||||
title: "[Feature]"
|
||||
labels: "feature"
|
||||
body:
|
||||
- type: markdown
|
||||
id: feature-info
|
||||
attributes:
|
||||
value: "## Feature Infomation"
|
||||
- type: textarea
|
||||
id: feature-main
|
||||
validations:
|
||||
required: true
|
||||
attributes:
|
||||
label: "Is your feature request related to a problem? Please describe."
|
||||
description: "A clear and concise description of what the problem is."
|
||||
placeholder: "I'm always frustrated when [...]"
|
||||
- type: textarea
|
||||
id: feature-solution
|
||||
validations:
|
||||
required: true
|
||||
attributes:
|
||||
label: "Describe the solution you'd like."
|
||||
description: "A clear and concise description of what you want to happen."
|
||||
- type: textarea
|
||||
id: feature-describe
|
||||
validations:
|
||||
required: true
|
||||
attributes:
|
||||
label: "Describe alternatives you've considered."
|
||||
description: "A clear and concise description of any alternative solutions or features you've considered."
|
||||
- type: textarea
|
||||
id: feature-extra
|
||||
validations:
|
||||
required: false
|
||||
attributes:
|
||||
label: "Additional context"
|
||||
description: "Add any other context or screenshots about the feature request here."
|
||||
|
||||
23
drivers/staging/kernelsu/.github/dependabot.yml
vendored
23
drivers/staging/kernelsu/.github/dependabot.yml
vendored
@@ -4,18 +4,35 @@ updates:
|
||||
directory: /
|
||||
schedule:
|
||||
interval: daily
|
||||
|
||||
groups:
|
||||
actions:
|
||||
patterns:
|
||||
- "*"
|
||||
- package-ecosystem: cargo
|
||||
directory: userspace/ksud
|
||||
schedule:
|
||||
interval: daily
|
||||
|
||||
allow:
|
||||
- dependency-type: "all"
|
||||
groups:
|
||||
crates:
|
||||
patterns:
|
||||
- "*"
|
||||
- package-ecosystem: gradle
|
||||
directory: manager
|
||||
schedule:
|
||||
interval: daily
|
||||
|
||||
groups:
|
||||
maven:
|
||||
patterns:
|
||||
- "*"
|
||||
- package-ecosystem: npm
|
||||
directory: website
|
||||
schedule:
|
||||
interval: daily
|
||||
allow:
|
||||
- dependency-type: "all"
|
||||
groups:
|
||||
npm:
|
||||
patterns:
|
||||
- "*"
|
||||
|
||||
@@ -51,7 +51,7 @@ build_from_image() {
|
||||
echo "[+] Images to upload"
|
||||
find . -type f -name "*.gz"
|
||||
|
||||
# find . -type f -name "*.gz" -exec python3 "$GITHUB_WORKSPACE"/KernelSU/scripts/ksubot.py {} +
|
||||
# find . -type f -name "*.gz" -exec python3 "$GITHUB_WORKSPACE"/KernelSU-Next/scripts/ksubot.py {} +
|
||||
}
|
||||
|
||||
for dir in Image*; do
|
||||
|
||||
@@ -30,7 +30,7 @@ build_from_image() {
|
||||
echo '[+] Images to upload'
|
||||
find . -type f -name "*.gz"
|
||||
|
||||
# find . -type f -name "*.gz" -exec python3 "$GITHUB_WORKSPACE"/KernelSU/scripts/ksubot.py {} +
|
||||
# find . -type f -name "*.gz" -exec python3 "$GITHUB_WORKSPACE"/KernelSU-Next/scripts/ksubot.py {} +
|
||||
}
|
||||
|
||||
for dir in Image*; do
|
||||
|
||||
@@ -26,7 +26,7 @@ jobs:
|
||||
- name: Make pull request
|
||||
if: steps.handle-add-device.outputs.success == 'true'
|
||||
id: cpr
|
||||
uses: peter-evans/create-pull-request@v6
|
||||
uses: peter-evans/create-pull-request@v7
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
commit-message: "[add device]: ${{ steps.handle-add-device.outputs.device }}"
|
||||
@@ -37,6 +37,7 @@ jobs:
|
||||
branch: "add-device-${{ github.event.issue.number }}"
|
||||
labels: add-device
|
||||
delete-branch: true
|
||||
sign-commits: true
|
||||
- name: Check outputs
|
||||
if: ${{ steps.cpr.outputs.pull-request-number }}
|
||||
run: |
|
||||
|
||||
@@ -51,7 +51,7 @@ jobs:
|
||||
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
path: KernelSU
|
||||
path: KernelSU-Next
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Setup need_upload
|
||||
@@ -70,37 +70,37 @@ jobs:
|
||||
cd $GITHUB_WORKSPACE
|
||||
sudo apt-get install repo -y
|
||||
mkdir android-kernel && cd android-kernel
|
||||
repo init --depth=1 -u https://android.googlesource.com/kernel/manifest -m "$GITHUB_WORKSPACE/KernelSU/.github/manifests/${{ inputs.manifest_name }}" --repo-rev=v2.16
|
||||
repo 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
|
||||
- name: Setup KernelSU-Next
|
||||
env:
|
||||
PATCH_PATH: ${{ inputs.patch_path }}
|
||||
IS_DEBUG_KERNEL: ${{ inputs.debug }}
|
||||
run: |
|
||||
cd $GITHUB_WORKSPACE/android-kernel
|
||||
echo "[+] KernelSU setup"
|
||||
echo "[+] KernelSU-Next setup"
|
||||
GKI_ROOT=$(pwd)
|
||||
echo "[+] GKI_ROOT: $GKI_ROOT"
|
||||
echo "[+] Copy KernelSU driver to $GKI_ROOT/common/drivers"
|
||||
ln -sf $GITHUB_WORKSPACE/KernelSU/kernel $GKI_ROOT/common/drivers/kernelsu
|
||||
echo "[+] Add KernelSU driver to Makefile"
|
||||
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 patches"
|
||||
cd $GKI_ROOT/common/ && git apply $GITHUB_WORKSPACE/KernelSU/.github/patches/$PATCH_PATH/*.patch || echo "[-] No patch found"
|
||||
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/kernel/Makefile
|
||||
printf "\nccflags-y += -DCONFIG_KSU_DEBUG\n" >> $GITHUB_WORKSPACE/KernelSU-Next/kernel/Makefile
|
||||
fi
|
||||
repo status
|
||||
echo "[+] KernelSU setup done."
|
||||
cd $GITHUB_WORKSPACE/KernelSU
|
||||
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
|
||||
@@ -110,8 +110,8 @@ jobs:
|
||||
run: |
|
||||
rm common/android/abi_gki_protected_exports_* || echo "No protected exports!"
|
||||
git config --global user.email "bot@kernelsu.org"
|
||||
git config --global user.name "KernelSUBot"
|
||||
cd common/ && git add -A && git commit -a -m "Add KernelSU"
|
||||
git config --global user.name "KernelSU-NextBot"
|
||||
cd common/ && git add -A && git commit -a -m "Add KernelSU-Next"
|
||||
repo status
|
||||
|
||||
- name: Build kernel
|
||||
|
||||
@@ -7,9 +7,9 @@ jobs:
|
||||
uses: ./.github/workflows/gki-kernel.yml
|
||||
with:
|
||||
version: android12-5.10
|
||||
version_name: android12-5.10.209
|
||||
tag: android12-5.10-2024-05
|
||||
os_patch_level: 2024-05
|
||||
version_name: android12-5.10.218
|
||||
tag: android12-5.10-2024-08
|
||||
os_patch_level: 2024-08
|
||||
patch_path: "5.10"
|
||||
debug: true
|
||||
build-debug-kernel-a13:
|
||||
@@ -17,11 +17,11 @@ jobs:
|
||||
matrix:
|
||||
include:
|
||||
- version: "5.10"
|
||||
sub_level: 209
|
||||
os_patch_level: 2024-05
|
||||
sub_level: 218
|
||||
os_patch_level: 2024-08
|
||||
- version: "5.15"
|
||||
sub_level: 148
|
||||
os_patch_level: 2024-05
|
||||
sub_level: 151
|
||||
os_patch_level: 2024-08
|
||||
uses: ./.github/workflows/gki-kernel.yml
|
||||
with:
|
||||
version: android13-${{ matrix.version }}
|
||||
@@ -34,15 +34,29 @@ jobs:
|
||||
matrix:
|
||||
include:
|
||||
- version: "5.15"
|
||||
sub_level: 148
|
||||
os_patch_level: 2024-05
|
||||
sub_level: 158
|
||||
os_patch_level: 2024-08
|
||||
- version: "6.1"
|
||||
sub_level: 75
|
||||
os_patch_level: 2024-05
|
||||
sub_level: 90
|
||||
os_patch_level: 2024-08
|
||||
uses: ./.github/workflows/gki-kernel.yml
|
||||
with:
|
||||
version: android14-${{ matrix.version }}
|
||||
version_name: android14-${{ matrix.version }}.${{ matrix.sub_level }}
|
||||
tag: android14-${{ matrix.version }}-${{ matrix.os_patch_level }}
|
||||
patch_path: ${{ matrix.version }}
|
||||
debug: true
|
||||
build-debug-kernel-a15:
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- version: "6.6"
|
||||
sub_level: 30
|
||||
os_patch_level: 2024-08
|
||||
uses: ./.github/workflows/gki-kernel.yml
|
||||
with:
|
||||
version: android15-${{ matrix.version }}
|
||||
version_name: android15-${{ matrix.version }}.${{ matrix.sub_level }}
|
||||
tag: android15-${{ matrix.version }}-${{ matrix.os_patch_level }}
|
||||
patch_path: ${{ matrix.version }}
|
||||
debug: true
|
||||
@@ -1,14 +1,14 @@
|
||||
name: Build Kernel - Android 12
|
||||
on:
|
||||
push:
|
||||
branches: ["main", "ci", "checkci"]
|
||||
branches: [ "next" ]
|
||||
paths:
|
||||
- ".github/workflows/build-kernel-a12.yml"
|
||||
- ".github/workflows/gki-kernel.yml"
|
||||
- ".github/scripts/build_a12.sh"
|
||||
- "kernel/**"
|
||||
pull_request:
|
||||
branches: ["main"]
|
||||
branches: [ "next" ]
|
||||
paths:
|
||||
- ".github/workflows/build-kernel-a12.yml"
|
||||
- ".github/workflows/gki-kernel.yml"
|
||||
@@ -17,7 +17,7 @@ on:
|
||||
workflow_call:
|
||||
jobs:
|
||||
build-kernel:
|
||||
if: github.event_name != 'pull_request' && github.ref != 'refs/heads/checkci'
|
||||
if: github.event_name != 'pull_request' && github.ref != 'refs/heads/next'
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
@@ -27,6 +27,8 @@ jobs:
|
||||
os_patch_level: 2024-03
|
||||
- sub_level: 209
|
||||
os_patch_level: 2024-05
|
||||
- sub_level: 218
|
||||
os_patch_level: 2024-08
|
||||
uses: ./.github/workflows/gki-kernel.yml
|
||||
secrets: inherit
|
||||
with:
|
||||
@@ -39,7 +41,7 @@ jobs:
|
||||
upload-artifacts:
|
||||
needs: build-kernel
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ ( github.event_name != 'pull_request' && github.ref == 'refs/heads/main' ) || github.ref_type == 'tag' || github.ref == 'refs/heads/ci' }}
|
||||
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 }}
|
||||
@@ -53,7 +55,7 @@ jobs:
|
||||
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
path: KernelSU
|
||||
path: KernelSU-Next
|
||||
fetch-depth: 0
|
||||
|
||||
- name: List artifacts
|
||||
@@ -63,7 +65,7 @@ jobs:
|
||||
- name: Download prebuilt toolchain
|
||||
run: |
|
||||
AOSP_MIRROR=https://android.googlesource.com
|
||||
BRANCH=main-kernel-build-2023
|
||||
BRANCH=main-kernel-build-2024
|
||||
git clone $AOSP_MIRROR/platform/prebuilts/build-tools -b $BRANCH --depth 1 build-tools
|
||||
git clone $AOSP_MIRROR/kernel/prebuilts/build-tools -b $BRANCH --depth 1 kernel-build-tools
|
||||
git clone $AOSP_MIRROR/platform/system/tools/mkbootimg -b $BRANCH --depth 1
|
||||
@@ -92,11 +94,11 @@ jobs:
|
||||
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
|
||||
cd $GITHUB_WORKSPACE/KernelSU-Next
|
||||
export VERSION=$(($(git rev-list --count HEAD) + 10200))
|
||||
echo "VERSION: $VERSION"
|
||||
cd -
|
||||
bash $GITHUB_WORKSPACE/KernelSU/.github/scripts/build_a12.sh
|
||||
bash $GITHUB_WORKSPACE/KernelSU-Next/.github/scripts/build_a12.sh
|
||||
|
||||
- name: Display structure of boot files
|
||||
run: ls -R
|
||||
@@ -108,11 +110,11 @@ jobs:
|
||||
path: Image-android12*/*.img.gz
|
||||
|
||||
check-build-kernel:
|
||||
if: (github.event_name == 'pull_request' && !github.event.pull_request.draft) || github.ref == 'refs/heads/checkci'
|
||||
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.209
|
||||
tag: android12-5.10-2024-05
|
||||
os_patch_level: 2024-05
|
||||
version_name: android12-5.10.218
|
||||
tag: android12-5.10-2024-08
|
||||
os_patch_level: 2024-08
|
||||
patch_path: "5.10"
|
||||
@@ -1,14 +1,14 @@
|
||||
name: Build Kernel - Android 13
|
||||
on:
|
||||
push:
|
||||
branches: ["main", "ci", "checkci"]
|
||||
branches: [ "next" ]
|
||||
paths:
|
||||
- ".github/workflows/build-kernel-a13.yml"
|
||||
- ".github/workflows/gki-kernel.yml"
|
||||
- ".github/scripts/build_a13.sh"
|
||||
- "kernel/**"
|
||||
pull_request:
|
||||
branches: ["main"]
|
||||
branches: [ "next" ]
|
||||
paths:
|
||||
- ".github/workflows/build-kernel-a13.yml"
|
||||
- ".github/workflows/gki-kernel.yml"
|
||||
@@ -17,7 +17,7 @@ on:
|
||||
workflow_call:
|
||||
jobs:
|
||||
build-kernel:
|
||||
if: github.event_name != 'pull_request' && github.ref != 'refs/heads/checkci'
|
||||
if: github.event_name != 'pull_request' && github.ref != 'refs/heads/next'
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
@@ -33,6 +33,15 @@ jobs:
|
||||
- version: "5.10"
|
||||
sub_level: 209
|
||||
os_patch_level: 2024-05
|
||||
- version: "5.10"
|
||||
sub_level: 210
|
||||
os_patch_level: 2024-06
|
||||
- version: "5.10"
|
||||
sub_level: 214
|
||||
os_patch_level: 2024-07
|
||||
- version: "5.10"
|
||||
sub_level: 218
|
||||
os_patch_level: 2024-08
|
||||
- version: "5.15"
|
||||
sub_level: 123
|
||||
os_patch_level: 2023-11
|
||||
@@ -45,6 +54,12 @@ jobs:
|
||||
- version: "5.15"
|
||||
sub_level: 148
|
||||
os_patch_level: 2024-05
|
||||
- version: "5.15"
|
||||
sub_level: 149
|
||||
os_patch_level: 2024-07
|
||||
- version: "5.15"
|
||||
sub_level: 151
|
||||
os_patch_level: 2024-08
|
||||
uses: ./.github/workflows/gki-kernel.yml
|
||||
secrets: inherit
|
||||
with:
|
||||
@@ -57,7 +72,7 @@ jobs:
|
||||
upload-artifacts:
|
||||
needs: build-kernel
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ ( github.event_name != 'pull_request' && github.ref == 'refs/heads/main' ) || github.ref_type == 'tag' || github.ref == 'refs/heads/ci' }}
|
||||
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 }}
|
||||
@@ -71,7 +86,7 @@ jobs:
|
||||
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
path: KernelSU
|
||||
path: KernelSU-Next
|
||||
fetch-depth: 0
|
||||
|
||||
- name: List artifacts
|
||||
@@ -81,7 +96,7 @@ jobs:
|
||||
- name: Download prebuilt toolchain
|
||||
run: |
|
||||
AOSP_MIRROR=https://android.googlesource.com
|
||||
BRANCH=main-kernel-build-2023
|
||||
BRANCH=main-kernel-build-2024
|
||||
git clone $AOSP_MIRROR/platform/prebuilts/build-tools -b $BRANCH --depth 1 build-tools
|
||||
git clone $AOSP_MIRROR/kernel/prebuilts/build-tools -b $BRANCH --depth 1 kernel-build-tools
|
||||
git clone $AOSP_MIRROR/platform/system/tools/mkbootimg -b $BRANCH --depth 1
|
||||
@@ -110,11 +125,11 @@ jobs:
|
||||
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
|
||||
cd $GITHUB_WORKSPACE/KernelSU-Next
|
||||
export VERSION=$(($(git rev-list --count HEAD) + 10200))
|
||||
echo "VERSION: $VERSION"
|
||||
cd -
|
||||
bash $GITHUB_WORKSPACE/KernelSU/.github/scripts/build_a13.sh
|
||||
bash $GITHUB_WORKSPACE/KernelSU-Next/.github/scripts/build_a13.sh
|
||||
|
||||
- name: Display structure of boot files
|
||||
run: ls -R
|
||||
@@ -126,16 +141,16 @@ jobs:
|
||||
path: Image-android13*/*.img.gz
|
||||
|
||||
check-build-kernel:
|
||||
if: (github.event_name == 'pull_request' && !github.event.pull_request.draft) || github.ref == 'refs/heads/checkci'
|
||||
if: (github.event_name == 'pull_request' && !github.event.pull_request.draft) || github.ref == 'refs/heads/next'
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- version: "5.10"
|
||||
sub_level: 209
|
||||
os_patch_level: 2024-05
|
||||
sub_level: 218
|
||||
os_patch_level: 2024-08
|
||||
- version: "5.15"
|
||||
sub_level: 148
|
||||
os_patch_level: 2024-05
|
||||
sub_level: 151
|
||||
os_patch_level: 2024-08
|
||||
uses: ./.github/workflows/gki-kernel.yml
|
||||
with:
|
||||
version: android13-${{ matrix.version }}
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
name: Build Kernel - Android 14
|
||||
on:
|
||||
push:
|
||||
branches: ["main", "ci", "checkci"]
|
||||
branches: [ "next" ]
|
||||
paths:
|
||||
- ".github/workflows/build-kernel-a14.yml"
|
||||
- ".github/workflows/gki-kernel.yml"
|
||||
- ".github/scripts/build_a13.sh"
|
||||
- "kernel/**"
|
||||
pull_request:
|
||||
branches: ["main"]
|
||||
branches: [ "next" ]
|
||||
paths:
|
||||
- ".github/workflows/build-kernel-a14.yml"
|
||||
- ".github/workflows/gki-kernel.yml"
|
||||
@@ -17,7 +17,7 @@ on:
|
||||
workflow_call:
|
||||
jobs:
|
||||
build-kernel:
|
||||
if: github.event_name != 'pull_request' && github.ref != 'refs/heads/checkci'
|
||||
if: github.event_name != 'pull_request' && github.ref != 'refs/heads/next'
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
@@ -33,6 +33,15 @@ jobs:
|
||||
- version: "5.15"
|
||||
sub_level: 148
|
||||
os_patch_level: 2024-05
|
||||
- version: "5.15"
|
||||
sub_level: 149
|
||||
os_patch_level: 2024-06
|
||||
- version: "5.15"
|
||||
sub_level: 153
|
||||
os_patch_level: 2024-07
|
||||
- version: "5.15"
|
||||
sub_level: 158
|
||||
os_patch_level: 2024-08
|
||||
- version: "6.1"
|
||||
sub_level: 25
|
||||
os_patch_level: 2023-10
|
||||
@@ -48,6 +57,15 @@ jobs:
|
||||
- version: "6.1"
|
||||
sub_level: 75
|
||||
os_patch_level: 2024-05
|
||||
- version: "6.1"
|
||||
sub_level: 78
|
||||
os_patch_level: 2024-06
|
||||
- version: "6.1"
|
||||
sub_level: 84
|
||||
os_patch_level: 2024-07
|
||||
- version: "6.1"
|
||||
sub_level: 90
|
||||
os_patch_level: 2024-08
|
||||
uses: ./.github/workflows/gki-kernel.yml
|
||||
secrets: inherit
|
||||
with:
|
||||
@@ -60,7 +78,7 @@ jobs:
|
||||
upload-artifacts:
|
||||
needs: build-kernel
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ ( github.event_name != 'pull_request' && github.ref == 'refs/heads/main' ) || github.ref_type == 'tag' || github.ref == 'refs/heads/ci' }}
|
||||
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 }}
|
||||
@@ -74,7 +92,7 @@ jobs:
|
||||
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
path: KernelSU
|
||||
path: KernelSU-Next
|
||||
fetch-depth: 0
|
||||
|
||||
- name: List artifacts
|
||||
@@ -84,7 +102,7 @@ jobs:
|
||||
- name: Download prebuilt toolchain
|
||||
run: |
|
||||
AOSP_MIRROR=https://android.googlesource.com
|
||||
BRANCH=main-kernel-build-2023
|
||||
BRANCH=main-kernel-build-2024
|
||||
git clone $AOSP_MIRROR/platform/prebuilts/build-tools -b $BRANCH --depth 1 build-tools
|
||||
git clone $AOSP_MIRROR/kernel/prebuilts/build-tools -b $BRANCH --depth 1 kernel-build-tools
|
||||
git clone $AOSP_MIRROR/platform/system/tools/mkbootimg -b $BRANCH --depth 1
|
||||
@@ -113,11 +131,11 @@ jobs:
|
||||
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
|
||||
cd $GITHUB_WORKSPACE/KernelSU-Next
|
||||
export VERSION=$(($(git rev-list --count HEAD) + 10200))
|
||||
echo "VERSION: $VERSION"
|
||||
cd -
|
||||
bash $GITHUB_WORKSPACE/KernelSU/.github/scripts/build_a13.sh
|
||||
bash $GITHUB_WORKSPACE/KernelSU-Next/.github/scripts/build_a13.sh
|
||||
|
||||
- name: Display structure of boot files
|
||||
run: ls -R
|
||||
@@ -129,16 +147,16 @@ jobs:
|
||||
path: Image-android14*/*.img.gz
|
||||
|
||||
check-build-kernel:
|
||||
if: (github.event_name == 'pull_request' && !github.event.pull_request.draft) || github.ref == 'refs/heads/checkci'
|
||||
if: (github.event_name == 'pull_request' && !github.event.pull_request.draft) || github.ref == 'refs/heads/next'
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- version: "5.15"
|
||||
sub_level: 148
|
||||
os_patch_level: 2024-05
|
||||
sub_level: 158
|
||||
os_patch_level: 2024-08
|
||||
- version: "6.1"
|
||||
sub_level: 75
|
||||
os_patch_level: 2024-05
|
||||
sub_level: 90
|
||||
os_patch_level: 2024-08
|
||||
uses: ./.github/workflows/gki-kernel.yml
|
||||
with:
|
||||
version: android14-${{ matrix.version }}
|
||||
|
||||
121
drivers/staging/kernelsu/.github/workflows/build-kernel-a15.yml
vendored
Normal file
121
drivers/staging/kernelsu/.github/workflows/build-kernel-a15.yml
vendored
Normal file
@@ -0,0 +1,121 @@
|
||||
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
|
||||
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/ksubot.session
|
||||
key: ${{ runner.os }}-bot-session
|
||||
|
||||
- name: Build boot images
|
||||
run: |
|
||||
export AVBTOOL=$GITHUB_WORKSPACE/kernel-build-tools/linux-x86/bin/avbtool
|
||||
export GZIP=$GITHUB_WORKSPACE/build-tools/path/linux-x86/gzip
|
||||
export LZ4=$GITHUB_WORKSPACE/build-tools/path/linux-x86/lz4
|
||||
export MKBOOTIMG=$GITHUB_WORKSPACE/mkbootimg/mkbootimg.py
|
||||
export UNPACK_BOOTIMG=$GITHUB_WORKSPACE/mkbootimg/unpack_bootimg.py
|
||||
cd $GITHUB_WORKSPACE/KernelSU-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: 30
|
||||
os_patch_level: 2024-08
|
||||
uses: ./.github/workflows/gki-kernel.yml
|
||||
with:
|
||||
version: android15-${{ matrix.version }}
|
||||
version_name: android15-${{ matrix.version }}.${{ matrix.sub_level }}
|
||||
tag: android15-${{ matrix.version }}-${{ matrix.os_patch_level }}
|
||||
os_patch_level: ${{ matrix.os_patch_level }}
|
||||
patch_path: ${{ matrix.version }}
|
||||
@@ -1,135 +0,0 @@
|
||||
name: Build Kernel - ChromeOS ARCVM
|
||||
on:
|
||||
push:
|
||||
branches: ["main", "ci", "checkci"]
|
||||
paths:
|
||||
- ".github/workflows/build-kernel-arcvm.yml"
|
||||
- "kernel/**"
|
||||
pull_request:
|
||||
branches: ["main"]
|
||||
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-20.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=12
|
||||
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
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
path: KernelSU
|
||||
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
|
||||
working-directory: kernel
|
||||
run: |
|
||||
echo "[+] KernelSU setup"
|
||||
KERNEL_ROOT=$GITHUB_WORKSPACE/kernel
|
||||
echo "[+] KERNEL_ROOT: $KERNEL_ROOT"
|
||||
echo "[+] Copy KernelSU driver to $KERNEL_ROOT/drivers"
|
||||
ln -sf $GITHUB_WORKSPACE/KernelSU/kernel $KERNEL_ROOT/drivers/kernelsu
|
||||
|
||||
echo "[+] Add KernelSU driver to Makefile"
|
||||
DRIVER_MAKEFILE=$KERNEL_ROOT/drivers/Makefile
|
||||
grep -q "kernelsu" $DRIVER_MAKEFILE || echo "obj-y += kernelsu/" >> $DRIVER_MAKEFILE
|
||||
|
||||
echo "[+] Apply KernelSU patches"
|
||||
cd $KERNEL_ROOT && git apply $GITHUB_WORKSPACE/KernelSU/.github/patches/5.10/*.patch || echo "[-] No patch found"
|
||||
|
||||
echo "[+] Patch script/setlocalversion"
|
||||
sed -i 's/-dirty//g' $KERNEL_ROOT/scripts/setlocalversion
|
||||
|
||||
echo "[+] KernelSU setup done."
|
||||
cd $GITHUB_WORKSPACE/KernelSU
|
||||
KSU_VERSION=$(($(git rev-list --count HEAD) + 10200))
|
||||
echo "KernelSU version: $KSU_VERSION"
|
||||
echo "kernelsu_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 }}"
|
||||
@@ -1,40 +0,0 @@
|
||||
name: Build Kernel - AVD
|
||||
on:
|
||||
push:
|
||||
branches: ["main", "ci", "checkci"]
|
||||
paths:
|
||||
- ".github/workflows/build-kernel-avd.yml"
|
||||
- ".github/workflows/avd-kernel.yml"
|
||||
- ".github/workflows/manifests/*xml"
|
||||
- "kernel/**"
|
||||
pull_request:
|
||||
branches: ["main"]
|
||||
paths:
|
||||
- ".github/workflows/build-kernel-avd.yml"
|
||||
- ".github/workflows/avd-kernel.yml"
|
||||
- ".github/workflows/manifests/*.xml"
|
||||
- "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
|
||||
@@ -1,38 +0,0 @@
|
||||
name: Build Kernel - WSA
|
||||
on:
|
||||
push:
|
||||
branches: ["main", "ci", "checkci"]
|
||||
paths:
|
||||
- ".github/workflows/build-kernel-wsa.yml"
|
||||
- ".github/workflows/wsa-kernel.yml"
|
||||
- "kernel/**"
|
||||
pull_request:
|
||||
branches: ["main"]
|
||||
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"
|
||||
@@ -1,11 +1,11 @@
|
||||
name: Build LKM for KernelSU
|
||||
name: Build LKM for KernelSU-Next
|
||||
on:
|
||||
push:
|
||||
branches: ["main", "ci", "checkci"]
|
||||
branches: [ "next" ]
|
||||
paths:
|
||||
- ".github/workflows/build-lkm.yml"
|
||||
pull_request:
|
||||
branches: ["main"]
|
||||
branches: [ "next" ]
|
||||
paths:
|
||||
- ".github/workflows/build-lkm.yml"
|
||||
workflow_call:
|
||||
@@ -15,20 +15,23 @@ jobs:
|
||||
matrix:
|
||||
include:
|
||||
- version: "android12-5.10"
|
||||
sub_level: 209
|
||||
os_patch_level: 2024-05
|
||||
sub_level: 218
|
||||
os_patch_level: 2024-08
|
||||
- version: "android13-5.10"
|
||||
sub_level: 209
|
||||
os_patch_level: 2024-05
|
||||
sub_level: 218
|
||||
os_patch_level: 2024-08
|
||||
- version: "android13-5.15"
|
||||
sub_level: 148
|
||||
os_patch_level: 2024-05
|
||||
sub_level: 151
|
||||
os_patch_level: 2024-08
|
||||
- version: "android14-5.15"
|
||||
sub_level: 148
|
||||
os_patch_level: 2024-05
|
||||
sub_level: 158
|
||||
os_patch_level: 2024-08
|
||||
- version: "android14-6.1"
|
||||
sub_level: 75
|
||||
os_patch_level: 2024-05
|
||||
sub_level: 90
|
||||
os_patch_level: 2024-08
|
||||
- version: "android15-6.6"
|
||||
sub_level: 30
|
||||
os_patch_level: 2024-08
|
||||
uses: ./.github/workflows/gki-kernel.yml
|
||||
with:
|
||||
version: ${{ matrix.version }}
|
||||
|
||||
@@ -2,17 +2,18 @@ name: Build Manager
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ "main", "ci" ]
|
||||
branches: [ "next" ]
|
||||
paths:
|
||||
- '.github/workflows/build-manager.yml'
|
||||
- 'manager/**'
|
||||
- 'kernel/**'
|
||||
- 'userspace/ksud/**'
|
||||
pull_request:
|
||||
branches: [ "main" ]
|
||||
branches: [ "next" ]
|
||||
paths:
|
||||
- 'manager/**'
|
||||
workflow_call:
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
build-lkm:
|
||||
@@ -26,18 +27,6 @@ jobs:
|
||||
include:
|
||||
- target: aarch64-linux-android
|
||||
os: ubuntu-latest
|
||||
- target: x86_64-linux-android
|
||||
os: ubuntu-latest
|
||||
- target: x86_64-pc-windows-gnu # windows pc
|
||||
os: ubuntu-latest
|
||||
- target: x86_64-apple-darwin # Intel mac
|
||||
os: macos-latest
|
||||
- target: aarch64-apple-darwin # M chip mac
|
||||
os: macos-latest
|
||||
- target: aarch64-unknown-linux-musl # arm64 Linux
|
||||
os: ubuntu-latest
|
||||
- target: x86_64-unknown-linux-musl # x86 Linux
|
||||
os: ubuntu-latest
|
||||
uses: ./.github/workflows/ksud.yml
|
||||
with:
|
||||
target: ${{ matrix.target }}
|
||||
@@ -56,17 +45,7 @@ jobs:
|
||||
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
|
||||
if: ${{ ( github.event_name != 'pull_request' && github.ref == 'refs/heads/main' ) || github.ref_type == 'tag' }}
|
||||
run: |
|
||||
if [ ! -z "${{ secrets.KEYSTORE }}" ]; then
|
||||
{
|
||||
@@ -85,9 +64,10 @@ jobs:
|
||||
java-version: 21
|
||||
|
||||
- name: Setup Gradle
|
||||
uses: gradle/actions/setup-gradle@v3
|
||||
with:
|
||||
gradle-home-cache-cleanup: true
|
||||
uses: gradle/actions/setup-gradle@v4
|
||||
|
||||
- name: Setup Android SDK
|
||||
uses: android-actions/setup-android@v3
|
||||
|
||||
- name: Download arm64 ksud
|
||||
uses: actions/download-artifact@v4
|
||||
@@ -95,18 +75,10 @@ jobs:
|
||||
name: ksud-aarch64-linux-android
|
||||
path: .
|
||||
|
||||
- name: Download x86_64 ksud
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: ksud-x86_64-linux-android
|
||||
path: .
|
||||
|
||||
- name: Copy ksud to app jniLibs
|
||||
run: |
|
||||
mkdir -p app/src/main/jniLibs/arm64-v8a
|
||||
mkdir -p app/src/main/jniLibs/x86_64
|
||||
cp -f ../aarch64-linux-android/release/ksud ../manager/app/src/main/jniLibs/arm64-v8a/libksud.so
|
||||
cp -f ../x86_64-linux-android/release/ksud ../manager/app/src/main/jniLibs/x86_64/libksud.so
|
||||
|
||||
- name: Build with Gradle
|
||||
run: |
|
||||
@@ -121,40 +93,12 @@ jobs:
|
||||
|
||||
- name: Upload build artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
if: ${{ ( github.event_name != 'pull_request' && github.ref == 'refs/heads/main' ) || github.ref_type == 'tag' }}
|
||||
with:
|
||||
name: manager
|
||||
path: manager/app/build/outputs/apk/release/*.apk
|
||||
|
||||
- name: Upload mappings
|
||||
uses: actions/upload-artifact@v4
|
||||
if: ${{ ( github.event_name != 'pull_request' && github.ref == 'refs/heads/main' ) || github.ref_type == 'tag' }}
|
||||
with:
|
||||
name: "mappings"
|
||||
path: "manager/app/build/outputs/mapping/release/"
|
||||
|
||||
- name: Bot session cache
|
||||
if: github.event_name != 'pull_request' && steps.need_upload.outputs.UPLOAD == 'true'
|
||||
id: bot_session_cache
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: scripts/ksubot.session
|
||||
key: ${{ runner.os }}-bot-session
|
||||
|
||||
- name: Upload to telegram
|
||||
if: github.event_name != 'pull_request' && steps.need_upload.outputs.UPLOAD == 'true'
|
||||
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 }}
|
||||
TITLE: Manager
|
||||
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/ksubot.py $APK
|
||||
fi
|
||||
@@ -1,13 +1,13 @@
|
||||
name: Build SU
|
||||
on:
|
||||
push:
|
||||
branches: [ "main", "ci" ]
|
||||
branches: [ "next" ]
|
||||
paths:
|
||||
- '.github/workflows/build-su.yml'
|
||||
- 'userspace/su/**'
|
||||
- 'scripts/ksubot.py'
|
||||
pull_request:
|
||||
branches: [ "main" ]
|
||||
branches: [ "next" ]
|
||||
paths:
|
||||
- 'userspace/su/**'
|
||||
jobs:
|
||||
@@ -26,14 +26,11 @@ jobs:
|
||||
else
|
||||
echo "UPLOAD=false" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
- uses: nttld/setup-ndk@v1
|
||||
with:
|
||||
ndk-version: r26d
|
||||
- name: Build su
|
||||
working-directory: ./userspace/su
|
||||
run: ndk-build
|
||||
run: $ANDROID_NDK/ndk-build
|
||||
- name: Upload a Build Artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: su
|
||||
path: ./userspace/su/libs
|
||||
path: ./userspace/su/libs
|
||||
|
||||
@@ -3,7 +3,7 @@ name: Clippy check
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- next
|
||||
paths:
|
||||
- '.github/workflows/clippy.yml'
|
||||
- 'userspace/ksud/**'
|
||||
|
||||
@@ -84,7 +84,7 @@ jobs:
|
||||
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
path: KernelSU
|
||||
path: KernelSU-Next
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Setup need_upload
|
||||
@@ -115,41 +115,41 @@ jobs:
|
||||
repo --trace sync -c -j$(nproc --all) --no-tags
|
||||
df -h
|
||||
|
||||
- name: Setup KernelSU
|
||||
- name: Setup KernelSU-Next
|
||||
env:
|
||||
PATCH_PATH: ${{ inputs.patch_path }}
|
||||
IS_DEBUG_KERNEL: ${{ inputs.debug }}
|
||||
run: |
|
||||
cd $GITHUB_WORKSPACE/android-kernel
|
||||
echo "[+] KernelSU setup"
|
||||
echo "[+] KernelSU-Next setup"
|
||||
GKI_ROOT=$(pwd)
|
||||
echo "[+] GKI_ROOT: $GKI_ROOT"
|
||||
echo "[+] Copy KernelSU driver to $GKI_ROOT/common/drivers"
|
||||
ln -sf $GITHUB_WORKSPACE/KernelSU/kernel $GKI_ROOT/common/drivers/kernelsu
|
||||
echo "[+] Add KernelSU driver to Makefile"
|
||||
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 patches"
|
||||
cd $GKI_ROOT/common/ && git apply $GITHUB_WORKSPACE/KernelSU/.github/patches/$PATCH_PATH/*.patch || echo "[-] No patch found"
|
||||
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/kernel/Makefile
|
||||
printf "\nccflags-y += -DCONFIG_KSU_DEBUG\n" >> $GITHUB_WORKSPACE/KernelSU-Next/kernel/Makefile
|
||||
fi
|
||||
repo status
|
||||
echo "[+] KernelSU setup done."
|
||||
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
|
||||
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 symbols"
|
||||
echo "[+] Add KernelSU-Next symbols"
|
||||
cat $KSU_ROOT/kernel/export_symbol.txt | awk '{sub("[ \t]+","");print " "$0}' >> $SYMBOL_LIST
|
||||
|
||||
- name: Setup ccache
|
||||
@@ -193,8 +193,8 @@ jobs:
|
||||
run: |
|
||||
rm common/android/abi_gki_protected_exports_* || echo "No protected exports!"
|
||||
git config --global user.email "bot@kernelsu.org"
|
||||
git config --global user.name "KernelSUBot"
|
||||
cd common/ && git add -A && git commit -a -m "Add KernelSU"
|
||||
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
|
||||
|
||||
@@ -18,11 +18,8 @@ jobs:
|
||||
build-a14-kernel:
|
||||
uses: ./.github/workflows/build-kernel-a14.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
|
||||
build-a15-kernel:
|
||||
uses: ./.github/workflows/build-kernel-a15.yml
|
||||
secrets: inherit
|
||||
release:
|
||||
needs:
|
||||
@@ -30,8 +27,6 @@ jobs:
|
||||
- build-a12-kernel
|
||||
- build-a13-kernel
|
||||
- build-a14-kernel
|
||||
- build-wsa-kernel
|
||||
- build-arcvm-kernel
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Download artifacts
|
||||
@@ -45,24 +40,6 @@ jobs:
|
||||
fi
|
||||
done
|
||||
|
||||
- name: Zip WSA kernel
|
||||
run: |
|
||||
for dir in kernel-WSA-*; do
|
||||
if [ -d "$dir" ]; then
|
||||
echo "------ Zip $dir ----------"
|
||||
(cd $dir && zip -r9 "$dir".zip ./* -x .git .gitignore ./*.zip && mv *.zip ..)
|
||||
fi
|
||||
done
|
||||
|
||||
- name: Zip ChromeOS ARCVM kernel
|
||||
run: |
|
||||
for dir in kernel-ARCVM-*; do
|
||||
if [ -d "$dir" ]; then
|
||||
echo "------ Zip $dir ----------"
|
||||
(cd $dir && zip -r9 "$dir".zip ./* -x .git .gitignore ./*.zip && mv *.zip ..)
|
||||
fi
|
||||
done
|
||||
|
||||
- name: Display structure of downloaded files
|
||||
run: ls -R
|
||||
|
||||
@@ -74,6 +51,4 @@ jobs:
|
||||
android*-lkm/*_kernelsu.ko
|
||||
AnyKernel3-*.zip
|
||||
boot-images-*/Image-*/*.img.gz
|
||||
kernel-WSA*.zip
|
||||
kernel-ARCVM*.zip
|
||||
ksud-*
|
||||
ksud-*
|
||||
@@ -3,13 +3,13 @@ name: Rustfmt check
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- 'main'
|
||||
- 'next'
|
||||
paths:
|
||||
- '.github/workflows/rustfmt.yml'
|
||||
- 'userspace/ksud/**'
|
||||
pull_request:
|
||||
branches:
|
||||
- 'main'
|
||||
- 'next'
|
||||
paths:
|
||||
- '.github/workflows/rustfmt.yml'
|
||||
- 'userspace/ksud/**'
|
||||
|
||||
@@ -3,13 +3,13 @@ name: ShellCheck
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- 'main'
|
||||
- 'next'
|
||||
paths:
|
||||
- '.github/workflows/shellcheck.yml'
|
||||
- '**/*.sh'
|
||||
pull_request:
|
||||
branches:
|
||||
- 'main'
|
||||
- 'next'
|
||||
paths:
|
||||
- '.github/workflows/shellcheck.yml'
|
||||
- '**/*.sh'
|
||||
|
||||
49
drivers/staging/kernelsu/README.md
Normal file
49
drivers/staging/kernelsu/README.md
Normal file
@@ -0,0 +1,49 @@
|
||||
# KernelSU Next
|
||||
|
||||
<img src="assets/kernelsu_next.png" style="width: 96px;" alt="logo">
|
||||
|
||||
A Kernel-based root solution for Android devices.
|
||||
|
||||
[](https://github.com/rifsxd/KernelSU/releases/latest)
|
||||
[](https://nightly.link/rifsxd/KernelSU/workflows/build-manager/next/manager)
|
||||
[](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html)
|
||||
[](/LICENSE)
|
||||
|
||||
## Features
|
||||
|
||||
1. Kernel-based `su` and root access management.
|
||||
2. Module system based on [Magic Mount](https://github.com/topjohnwu/Magisk/blob/c512496847d182526f2043295ecfd275398eccac/docs/releases/26100.md#new-magic-mount-implementation).
|
||||
3. [App Profile](https://kernelsu.org/guide/app-profile.html): Lock up the root power in a cage.
|
||||
|
||||
## Compatibility State
|
||||
|
||||
KernelSU Next officially supports most Android kernels starting from 4.14 upto 6.6.
|
||||
- GKI 2.0 (5.10+) kernels can run pre-built images and LKM/KMI.
|
||||
- GKI 1.0 (4.19 - 5.4) kernels need to rebuilt with KernelSU driver.
|
||||
- EOL (<4.19) kernels also need to be rebuilt with KernelSU driver. (3.18+ is experimental)
|
||||
|
||||
Currently, only `arm64-v8a` is supported.
|
||||
|
||||
## Usage
|
||||
|
||||
- [Installation Instruction](https://kernelsu.org/guide/installation.html)
|
||||
- [How to build?](https://kernelsu.org/guide/how-to-build.html)
|
||||
- [Official Website](https://kernelsu.org/)
|
||||
|
||||
## Security
|
||||
|
||||
For information on reporting security vulnerabilities in KernelSU, see [SECURITY.md](/SECURITY.md).
|
||||
|
||||
## License
|
||||
|
||||
- Files under the `kernel` directory are [GPL-2.0-only](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html).
|
||||
- All other parts except the `kernel` directory are [GPL-3.0-or-later](https://www.gnu.org/licenses/gpl-3.0.html).
|
||||
|
||||
## Credits
|
||||
|
||||
- [kernel-assisted-superuser](https://git.zx2c4.com/kernel-assisted-superuser/about/): the KernelSU idea.
|
||||
- [Magisk](https://github.com/topjohnwu/Magisk): the powerful root tool.
|
||||
- [genuine](https://github.com/brevent/genuine/): apk v2 signature validation.
|
||||
- [Diamorphine](https://github.com/m0nad/Diamorphine): some rootkit skills.
|
||||
- [KernelSU](https://github.com/tiann/KernelSU): thanks to tiann or else KernelSU Next wouldn't even exist.
|
||||
- [Magic Mount Port](https://github.com/5ec1cff/KernelSU/blob/main/userspace/ksud/src/magic_mount.rs): 💜 5ec1cff for saving KernelSU!
|
||||
BIN
drivers/staging/kernelsu/assets/kernelsu_next.png
Executable file
BIN
drivers/staging/kernelsu/assets/kernelsu_next.png
Executable file
Binary file not shown.
|
After Width: | Height: | Size: 13 KiB |
@@ -1,57 +0,0 @@
|
||||
**English** | [Español](README_ES.md) | [简体中文](README_CN.md) | [繁體中文](README_TW.md) | [日本語](README_JP.md) | [한국어](README_KR.md) | [Polski](README_PL.md) | [Português (Brasil)](README_PT-BR.md) | [Türkçe](README_TR.md) | [Русский](README_RU.md) | [Tiếng Việt](README_VI.md) | [Indonesia](README_ID.md) | [עברית](README_IW.md) | [हिंदी](README_IN.md)
|
||||
|
||||
# KernelSU
|
||||
|
||||
<img src="https://kernelsu.org/logo.png" style="width: 96px;" alt="logo">
|
||||
|
||||
A Kernel-based root solution for Android devices.
|
||||
|
||||
[](https://github.com/tiann/KernelSU/releases/latest)
|
||||
[](https://hosted.weblate.org/engage/kernelsu)
|
||||
[](https://t.me/KernelSU)
|
||||
[](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html)
|
||||
[](/LICENSE)
|
||||
|
||||
## Features
|
||||
|
||||
1. Kernel-based `su` and root access management.
|
||||
2. Module system based on [OverlayFS](https://en.wikipedia.org/wiki/OverlayFS).
|
||||
3. [App Profile](https://kernelsu.org/guide/app-profile.html): Lock up the root power in a cage.
|
||||
|
||||
## Compatibility State
|
||||
|
||||
KernelSU officially supports Android GKI 2.0 devices (kernel 5.10+). Older kernels (4.14+) are also compatible, but the kernel will have to be built manually.
|
||||
|
||||
With this, WSA, ChromeOS, and container-based Android are all supported.
|
||||
|
||||
Currently, only `arm64-v8a` and `x86_64` are supported.
|
||||
|
||||
## Usage
|
||||
|
||||
- [Installation Instruction](https://kernelsu.org/guide/installation.html)
|
||||
- [How to build?](https://kernelsu.org/guide/how-to-build.html)
|
||||
- [Official Website](https://kernelsu.org/)
|
||||
|
||||
## Translation
|
||||
|
||||
To help translate KernelSU or improve existing translations, please use [Weblate](https://hosted.weblate.org/engage/kernelsu/). PR of Manager's translation is no longer accepted, because it will conflict with Weblate.
|
||||
|
||||
## Discussion
|
||||
|
||||
- Telegram: [@KernelSU](https://t.me/KernelSU)
|
||||
|
||||
## Security
|
||||
|
||||
For information on reporting security vulnerabilities in KernelSU, see [SECURITY.md](/SECURITY.md).
|
||||
|
||||
## License
|
||||
|
||||
- Files under the `kernel` directory are [GPL-2.0-only](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html).
|
||||
- All other parts except the `kernel` directory are [GPL-3.0-or-later](https://www.gnu.org/licenses/gpl-3.0.html).
|
||||
|
||||
## Credits
|
||||
|
||||
- [kernel-assisted-superuser](https://git.zx2c4.com/kernel-assisted-superuser/about/): the KernelSU idea.
|
||||
- [Magisk](https://github.com/topjohnwu/Magisk): the powerful root tool.
|
||||
- [genuine](https://github.com/brevent/genuine/): apk v2 signature validation.
|
||||
- [Diamorphine](https://github.com/m0nad/Diamorphine): some rootkit skills.
|
||||
@@ -1,57 +0,0 @@
|
||||
[English](README.md) | [Español](README_ES.md) | **简体中文** | [繁體中文](README_TW.md) | [日本語](README_JP.md) | [한국어](README_KR.md) | [Polski](README_PL.md) | [Português (Brasil)](README_PT-BR.md) | [Türkçe](README_TR.md) | [Русский](README_RU.md) | [Tiếng Việt](README_VI.md) | [Indonesia](README_ID.md) | [עברית](README_IW.md) | [हिंदी](README_IN.md)
|
||||
|
||||
# KernelSU
|
||||
|
||||
<img src="https://kernelsu.org/logo.png" style="width: 96px;" alt="logo">
|
||||
|
||||
一个 Android 上基于内核的 root 方案。
|
||||
|
||||
[](https://github.com/tiann/KernelSU/releases/latest)
|
||||
[](https://hosted.weblate.org/engage/kernelsu)
|
||||
[](https://t.me/KernelSU)
|
||||
[](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html)
|
||||
[](/LICENSE)
|
||||
|
||||
## 特性
|
||||
|
||||
- 基于内核的 `su` 和权限管理。
|
||||
- 基于 [OverlayFS](https://en.wikipedia.org/wiki/OverlayFS) 的模块系统。
|
||||
- [App Profile](https://kernelsu.org/zh_CN/guide/app-profile.html): 把 Root 权限关进笼子里。
|
||||
|
||||
## 兼容状态
|
||||
|
||||
KernelSU 官方支持 GKI 2.0 的设备(内核版本5.10以上);旧内核也是兼容的(最低4.14+),不过需要自己编译内核。
|
||||
|
||||
WSA, ChromeOS 和运行在容器上的 Android 也可以与 KernelSU 一起工作。
|
||||
|
||||
目前支持架构 : `arm64-v8a` 和 `x86_64`。
|
||||
|
||||
## 使用方法
|
||||
|
||||
- [安装教程](https://kernelsu.org/zh_CN/guide/installation.html)
|
||||
- [如何构建?](https://kernelsu.org/zh_CN/guide/how-to-build.html)
|
||||
- [官方网站](https://kernelsu.org/zh_CN/)
|
||||
|
||||
## 参与翻译
|
||||
|
||||
要将 KernelSU 翻译成您的语言,或完善现有的翻译,请使用 [Weblate](https://hosted.weblate.org/engage/kernelsu/)。现已不再接受有关管理器翻译的PR,因为这会与Weblate冲突。
|
||||
|
||||
## 讨论
|
||||
|
||||
- Telegram: [@KernelSU](https://t.me/KernelSU)
|
||||
|
||||
## 安全性
|
||||
|
||||
有关报告 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)。
|
||||
|
||||
## 鸣谢
|
||||
|
||||
- [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 技巧。
|
||||
@@ -1,56 +0,0 @@
|
||||
[English](README.md) | **Español** | [简体中文](README_CN.md) | [繁體中文](README_TW.md) | [日本語](README_JP.md) | [한국어](README_KR.md) | [Polski](README_PL.md) | [Português (Brasil)](README_PT-BR.md) | [Türkçe](README_TR.md) | [Русский](README_RU.md) | [Tiếng Việt](README_VI.md) | [Indonesia](README_ID.md) | [עברית](README_IW.md) | [हिंदी](README_IN.md)
|
||||
|
||||
# KernelSU
|
||||
|
||||
<img src="https://kernelsu.org/logo.png" style="width: 96px;" alt="logo">
|
||||
|
||||
Una solución root basada en el kernel para dispositivos Android.
|
||||
|
||||
[](https://github.com/tiann/KernelSU/releases/latest)
|
||||
[](https://hosted.weblate.org/engage/kernelsu)
|
||||
[](https://t.me/KernelSU)
|
||||
[](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html)
|
||||
[](/LICENSE)
|
||||
|
||||
## Características
|
||||
|
||||
1. Binario `su` basado en el kernel y gestión de acceso root.
|
||||
2. Sistema de módulos basado en [OverlayFS](https://en.wikipedia.org/wiki/OverlayFS).
|
||||
|
||||
## Estado de compatibilidad
|
||||
|
||||
**KernelSU** soporta de forma oficial dispositivos Android con **GKI 2.0** (a partir de la versión **5.10** del kernel). Los kernels antiguos (a partir de la versión **4.14**) también son compatibles, pero necesitas compilarlos por tu cuenta.
|
||||
|
||||
Con esto, WSA, ChromeOS y Android basado en contenedores están todos compatibles.
|
||||
|
||||
Actualmente, solo se admiten las arquitecturas `arm64-v8a` y `x86_64`.
|
||||
|
||||
## Uso
|
||||
|
||||
- [¿Cómo instalarlo?](https://kernelsu.org/guide/installation.html)
|
||||
- [¿Cómo compilarlo?](https://kernelsu.org/guide/how-to-build.html)
|
||||
- [Site oficial](https://kernelsu.org/)
|
||||
|
||||
## Traducción
|
||||
|
||||
Para ayudar a traducir KernelSU o mejorar las traducciones existentes, utilice [Weblate](https://hosted.weblate.org/engage/kernelsu/). Ya no se aceptan PR de la traducción de Manager porque entrará en conflicto con Weblate.
|
||||
|
||||
## Discusión
|
||||
|
||||
- Telegram: [@KernelSU](https://t.me/KernelSU)
|
||||
|
||||
## Seguridad
|
||||
|
||||
Para obtener información sobre cómo informar vulnerabilidades de seguridad en KernelSU, consulte [SECURITY.md](/SECURITY.md).
|
||||
|
||||
## Licencia
|
||||
|
||||
- Los archivos bajo el directorio `kernel` están licenciados bajo [GPL-2-only](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html).
|
||||
- Todas las demás partes, a excepción del directorio `kernel`, están licenciados bajo [GPL-3-or-later](https://www.gnu.org/licenses/gpl-3.0.html).
|
||||
|
||||
## Créditos
|
||||
|
||||
- [kernel-assisted-superuser](https://git.zx2c4.com/kernel-assisted-superuser/about/): la idea de KernelSU.
|
||||
- [Magisk](https://github.com/topjohnwu/Magisk): la poderosa herramienta root.
|
||||
- [genuine](https://github.com/brevent/genuine/): validación de firma apk v2.
|
||||
- [Diamorphine](https://github.com/m0nad/Diamorphine): algunas habilidades de rootkit.
|
||||
@@ -1,53 +0,0 @@
|
||||
[English](README.md) | [Español](README_ES.md) | [简体中文](README_CN.md) | [繁體中文](README_TW.md) | [日本語](README_JP.md) | [한국어](README_KR.md) | [Polski](README_PL.md) | [Português (Brasil)](README_PT-BR.md) | [Türkçe](README_TR.md) | [Русский](README_RU.md) | [Tiếng Việt](README_VI.md) | **Indonesia** | [עברית](README_IW.md) | [हिंदी](README_IN.md)
|
||||
|
||||
# KernelSU
|
||||
|
||||
<img src="https://kernelsu.org/logo.png" style="width: 96px;" alt="logo">
|
||||
|
||||
Solusi root berbasis Kernel untuk perangkat Android.
|
||||
|
||||
[](https://github.com/tiann/KernelSU/releases/latest)
|
||||
[](https://hosted.weblate.org/engage/kernelsu)
|
||||
[](https://t.me/KernelSU)
|
||||
[](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html)
|
||||
[](/LICENSE)
|
||||
|
||||
## Fitur
|
||||
|
||||
1. Manajemen akses root dan `su` berbasis kernel.
|
||||
2. Sistem modul berdasarkan [OverlayFS](https://en.wikipedia.org/wiki/OverlayFS).
|
||||
3. [Profil Aplikasi](https://kernelsu.org/guide/app-profile.html): Kunci daya root di dalam sangkar.
|
||||
|
||||
## Status Kompatibilitas
|
||||
|
||||
KernelSU secara resmi mendukung perangkat Android GKI 2.0 (dengan kernel 5.10+), kernel lama (4.14+) juga kompatibel, tetapi Anda perlu membuat kernel sendiri.
|
||||
|
||||
WSA, ChromeOS, dan Android berbasis wadah juga dapat bekerja dengan KernelSU terintegrasi.
|
||||
|
||||
Dan ABI yang didukung saat ini adalah: `arm64-v8a` dan `x86_64`
|
||||
|
||||
## Penggunaan
|
||||
|
||||
- [Petunjuk Instalasi](https://kernelsu.org/id_ID/guide/installation.html)
|
||||
- [Bagaimana cara membuat?](https://kernelsu.org/id_ID/guide/how-to-build.html)
|
||||
- [Situs Web Resmi](https://kernelsu.org/id_ID/)
|
||||
|
||||
## Terjemahan
|
||||
|
||||
Untuk menerjemahkan KernelSU ke dalam bahasa Anda atau menyempurnakan terjemahan yang sudah ada, harap gunakan [Weblat](https://hosted.weblate.org/engage/kernelsu/).
|
||||
|
||||
## Diskusi
|
||||
|
||||
- Telegram: [@KernelSU](https://t.me/KernelSU)
|
||||
|
||||
## Lisensi
|
||||
|
||||
- File di bawah direktori `kernel` adalah [GPL-2-only](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html).
|
||||
- Semua bagian lain kecuali direktori `kernel` adalah [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 ampuh.
|
||||
- [genuine](https://github.com/brevent/genuine/): validasi tanda tangan apk v2.
|
||||
- [Diamorphine](https://github.com/m0nad/Diamorphine): beberapa keterampilan rootkit.
|
||||
@@ -1,53 +0,0 @@
|
||||
[English](README.md) | [Español](README_ES.md) | [简体中文](README_CN.md) | [繁體中文](README_TW.md) | [日本語](README_JP.md) | [한국어](README_KR.md) | [Polski](README_PL.md) | [Português (Brasil)](README_PT-BR.md) | [Türkçe](README_TR.md) | [Русский](README_RU.md) | [Tiếng Việt](README_VI.md) | [Indonesia](README_ID.md) | [עברית](README_IW.md) | **हिंदी**
|
||||
|
||||
# KernelSU
|
||||
|
||||
<img src="https://kernelsu.org/logo.png" style="width: 96px;" alt="logo">
|
||||
|
||||
Android उपकरणों के लिए कर्नेल-आधारित रूट समाधान।
|
||||
|
||||
[](https://github.com/tiann/KernelSU/releases/latest)
|
||||
[](https://hosted.weblate.org/engage/kernelsu)
|
||||
[](https://t.me/KernelSU)
|
||||
[](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html)
|
||||
[](/LICENSE)
|
||||
|
||||
## विशेषताएँ
|
||||
|
||||
1. कर्नेल-आधारित `su` और रूट एक्सेस प्रबंधन।
|
||||
2. [OverlayFS](https://en.wikipedia.org/wiki/OverlayFS) पर आधारित मॉड्यूल प्रणाली।
|
||||
3. [App Profile](https://kernelsu.org/guide/app-profile.html): Root शक्ति को पिंजरे में बंद कर दो।
|
||||
|
||||
## अनुकूलता अवस्था
|
||||
|
||||
KernelSU आधिकारिक तौर पर Android GKI 2.0 डिवाइस (कर्नेल 5.10+) का समर्थन करता है। पुराने कर्नेल (4.14+) भी संगत हैं, लेकिन कर्नेल को मैन्युअल रूप से बनाना होगा।
|
||||
|
||||
इसके साथ, WSA, ChromeOS और कंटेनर-आधारित Android सभी समर्थित हैं।
|
||||
|
||||
वर्तमान में, केवल `arm64-v8a` और `x86_64` समर्थित हैं।
|
||||
|
||||
## प्रयोग
|
||||
|
||||
- [स्थापना निर्देश](https://kernelsu.org/guide/installation.html)
|
||||
- [कैसे बनाना है ?](https://kernelsu.org/guide/how-to-build.html)
|
||||
- [आधिकारिक वेबसाइट](https://kernelsu.org/)
|
||||
|
||||
## अनुवाद करना
|
||||
|
||||
KernelSU का अनुवाद करने या मौजूदा अनुवादों को बेहतर बनाने में सहायता के लिए, कृपया इसका उपयोग करें [Weblate](https://hosted.weblate.org/engage/kernelsu/).
|
||||
|
||||
## बहस
|
||||
|
||||
- Telegram: [@KernelSU](https://t.me/KernelSU)
|
||||
|
||||
## लाइसेंस
|
||||
|
||||
- `Kernel` निर्देशिका के अंतर्गत फ़ाइलें हैं [GPL-2-only](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html)
|
||||
- `Kernel` निर्देशिका को छोड़कर अन्य सभी भाग हैं [GPL-3.0-or-later](https://www.gnu.org/licenses/gpl-3.0.html)
|
||||
|
||||
## आभार सूची
|
||||
|
||||
- [kernel-assisted-superuser](https://git.zx2c4.com/kernel-assisted-superuser/about/): KernelSU विचार।
|
||||
- [Magisk](https://github.com/topjohnwu/Magisk): शक्तिशाली root उपकरण।
|
||||
- [genuine](https://github.com/brevent/genuine/): apk v2 हस्ताक्षर सत्यापन।
|
||||
- [Diamorphine](https://github.com/m0nad/Diamorphine): कुछ रूटकिट कौशल।
|
||||
@@ -1,53 +0,0 @@
|
||||
[English](README.md) | [Español](README_ES.md) | [简体中文](README_CN.md) | [繁體中文](README_TW.md) | [日本語](README_JP.md) | [한국어](README_KR.md) | [Polski](README_PL.md) | [Português (Brasil)](README_PT-BR.md) | [Türkçe](README_TR.md) | [Русский](README_RU.md) | [Tiếng Việt](README_VI.md) | [Indonesia](README_ID.md) | **עברית** | [हिंदी](README_IN.md)
|
||||
|
||||
# KernelSU
|
||||
|
||||
<img src="https://kernelsu.org/logo.png" style="width: 96px;" alt="logo">
|
||||
|
||||
פתרון לניהול root מבוסס על Kernel עבור מכשירי Android.
|
||||
|
||||
[](https://github.com/tiann/KernelSU/releases/latest)
|
||||
[](https://hosted.weblate.org/engage/kernelsu)
|
||||
[](https://t.me/KernelSU)
|
||||
[](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html)
|
||||
[](/LICENSE)
|
||||
|
||||
## תכונות
|
||||
|
||||
1. ניהול root ו־`su` מבוססים על Kernel.
|
||||
2. מערכת מודולים מבוססת [OverlayFS](https://en.wikipedia.org/wiki/OverlayFS).
|
||||
3. [פרופיל אפליקציה](https://kernelsu.org/guide/app-profile.html): נעילת גישת root בכלוב.
|
||||
|
||||
## מצב תאימות
|
||||
|
||||
KernelSU תומך במכשירי Android GKI 2.0 (kernel 5.10+) באופן רשמי. לליבות ישנות (4.14+) יש גם תאימות, אך יידרש לבנות את הליבה באופן ידני.
|
||||
|
||||
באמצעות זה, תמיכה זמינה גם ל-WSA, ChromeOS ומכשירי Android המבוססים על מיכלים.
|
||||
|
||||
כרגע, רק `arm64-v8a` ו־`x86_64` נתמכים.
|
||||
|
||||
## שימוש
|
||||
|
||||
- [הוראות התקנה](https://kernelsu.org/guide/installation.html)
|
||||
- [איך לבנות?](https://kernelsu.org/guide/how-to-build.html)
|
||||
- [האתר רשמי](https://kernelsu.org/)
|
||||
|
||||
## תרגום
|
||||
|
||||
כדי לעזור בתרגום של KernelSU או לשפר תרגומים קיימים, יש להשתמש ב-[Weblate](https://hosted.weblate.org/engage/kernelsu/).
|
||||
|
||||
## דיון
|
||||
|
||||
- Telegram: [@KernelSU](https://t.me/KernelSU)
|
||||
|
||||
## רשיון
|
||||
|
||||
- קבצים תחת הספרייה `kernel` מוגנים על פי [GPL-2.0-only](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html).
|
||||
- כל החלקים האחרים, למעט הספרייה `kernel`, מוגנים על פי [GPL-3.0-or-later](https://www.gnu.org/licenses/gpl-3.0.html).
|
||||
|
||||
## קרדיטים
|
||||
|
||||
- [kernel-assisted-superuser](https://git.zx2c4.com/kernel-assisted-superuser/about/): הרעיון של KernelSU.
|
||||
- [Magisk](https://github.com/topjohnwu/Magisk): הכלי הסופר חזק לניהול root.
|
||||
- [genuine](https://github.com/brevent/genuine/): אימות חתימת apk v2.
|
||||
- [Diamorphine](https://github.com/m0nad/Diamorphine): כמה יכולות רוט.
|
||||
@@ -1,53 +0,0 @@
|
||||
[English](README.md) | [Español](README_ES.md) | [简体中文](README_CN.md) | [繁體中文](README_TW.md) | **日本語** | [한국어](README_KR.md) | [Polski](README_PL.md) | [Português (Brasil)](README_PT-BR.md) | [Türkçe](README_TR.md) | [Русский](README_RU.md) | [Tiếng Việt](README_VI.md) | [Indonesia](README_ID.md) | [עברית](README_IW.md) | [हिंदी](README_IN.md)
|
||||
|
||||
# KernelSU
|
||||
|
||||
<img src="https://kernelsu.org/logo.png" style="width: 96px;" alt="logo">
|
||||
|
||||
Android におけるカーネルベースの root ソリューションです。
|
||||
|
||||
[](https://github.com/tiann/KernelSU/releases/latest)
|
||||
[](https://hosted.weblate.org/engage/kernelsu)
|
||||
[](https://t.me/KernelSU)
|
||||
[](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html)
|
||||
[](/LICENSE)
|
||||
|
||||
## 特徴
|
||||
|
||||
1. カーネルベースの `su` と権限管理。
|
||||
2. [OverlayFS](https://en.wikipedia.org/wiki/OverlayFS) に基づくモジュールシステム。
|
||||
3. [アプリのプロファイル](https://kernelsu.org/guide/app-profile.html): root の権限をケージ内に閉じ込めます。
|
||||
|
||||
## 対応状況
|
||||
|
||||
KernelSU は GKI 2.0 デバイス(カーネルバージョン 5.10 以上)を公式にサポートしています。古いカーネル(4.14以上)とも互換性がありますが、自分でカーネルをビルドする必要があります。
|
||||
|
||||
WSA 、ChromeOS とコンテナ上で動作する Android でも KernelSU を統合して動かせます。
|
||||
|
||||
現在サポートしているアーキテクチャは `arm64-v8a` および `x86_64` です。
|
||||
|
||||
## 使用方法
|
||||
|
||||
- [インストール方法はこちら](https://kernelsu.org/ja_JP/guide/installation.html)
|
||||
- [ビルド方法はこちら](https://kernelsu.org/guide/how-to-build.html)
|
||||
- [公式サイト](https://kernelsu.org/ja_JP/)
|
||||
|
||||
## 翻訳
|
||||
|
||||
KernelSU をあなたの言語に翻訳するか、既存の翻訳を改善するには、[Weblate](https://hosted.weblate.org/engage/kernelsu/) を使用してください。Manager翻訳した PR は、Weblate と競合するため受け入れられなくなりました。
|
||||
|
||||
## ディスカッション
|
||||
|
||||
- Telegram: [@KernelSU](https://t.me/KernelSU)
|
||||
|
||||
## ライセンス
|
||||
|
||||
- `kernel` ディレクトリの下にあるすべてのファイル: [GPL-2-only](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html)。
|
||||
- `kernel` ディレクトリ以外のすべてのファイル: [GPL-3.0-or-later](https://www.gnu.org/licenses/gpl-3.0.html)。
|
||||
|
||||
## クレジット
|
||||
|
||||
- [kernel-assisted-superuser](https://git.zx2c4.com/kernel-assisted-superuser/about/):KernelSU のアイデア元。
|
||||
- [Magisk](https://github.com/topjohnwu/Magisk):強力な root ツール。
|
||||
- [genuine](https://github.com/brevent/genuine/):apk v2 の署名検証。
|
||||
- [Diamorphine](https://github.com/m0nad/Diamorphine): rootkit のスキル。
|
||||
@@ -1,57 +0,0 @@
|
||||
[English](README.md) | [Español](README_ES.md) | [简体中文](README_CN.md) | [繁體中文](README_TW.md) | [日本語](README_JP.md) | **한국어** | [Polski](README_PL.md) | [Português (Brasil)](README_PT-BR.md) | [Türkçe](README_TR.md) | [Русский](README_RU.md) | [Tiếng Việt](README_VI.md) | [Indonesia](README_ID.md) | [עברית](README_IW.md) | [हिंदी](README_IN.md)
|
||||
|
||||
# KernelSU
|
||||
|
||||
<img src="https://kernelsu.org/logo.png" style="width: 96px;" alt="logo">
|
||||
|
||||
안드로이드 기기에서 사용되는 커널 기반 루팅 솔루션입니다.
|
||||
|
||||
[](https://github.com/tiann/KernelSU/releases/latest)
|
||||
[](https://hosted.weblate.org/engage/kernelsu)
|
||||
[](https://t.me/KernelSU)
|
||||
[](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html)
|
||||
[](/LICENSE)
|
||||
|
||||
## 기능들
|
||||
|
||||
1. 커널 기반 `su` 및 루트 액세스 관리.
|
||||
2. [OverlayFS](https://en.wikipedia.org/wiki/OverlayFS) 기반 모듈 시스템.
|
||||
3. [App Profile](https://kernelsu.org/guide/app-profile.html): 루트 권한을 케이지에 가둡니다.
|
||||
|
||||
## 호환 상태
|
||||
|
||||
KernelSU는 공식적으로 안드로이드 GKI 2.0 디바이스(커널 5.10 이상)를 지원합니다. 오래된 커널(4.14 이상)도 사용할 수 있지만, 커널을 수동으로 빌드해야 합니다.
|
||||
|
||||
KernelSU는 WSA, ChromeOS, 컨테이너 기반 안드로이드 모두를 지원합니다.
|
||||
|
||||
현재는 `arm64-v8a`와 `x86_64`만 지원됩니다.
|
||||
|
||||
## 사용 방법
|
||||
|
||||
- [설치 방법](https://kernelsu.org/guide/installation.html)
|
||||
- [어떻게 빌드하나요?](https://kernelsu.org/guide/how-to-build.html)
|
||||
- [공식 웹사이트](https://kernelsu.org/)
|
||||
|
||||
## 번역
|
||||
|
||||
KernelSU 번역을 돕거나 기존 번역을 개선하려면 [Weblate](https://hosted.weblate.org/engage/kernelsu/)를 이용해 주세요. 매니저의 번역은 Weblate와 충돌할 수 있으므로 더 이상 허용되지 않습니다.
|
||||
|
||||
## 토론
|
||||
|
||||
- 텔레그램: [@KernelSU](https://t.me/KernelSU)
|
||||
|
||||
## 보안
|
||||
|
||||
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 스킬.
|
||||
@@ -1,55 +0,0 @@
|
||||
[English](README.md) | [Español](README_ES.md) | [简体中文](README_CN.md) | [繁體中文](README_TW.md) | [日本語](README_JP.md) | [한국어](README_KR.md) | **Polski** | [Português (Brasil)](README_PT-BR.md) | [Türkçe](README_TR.md) | [Русский](README_RU.md) | [Tiếng Việt](README_VI.md) | [Indonesia](README_ID.md) | [עברית](README_IW.md) | [हिंदी](README_IN.md)
|
||||
|
||||
# KernelSU
|
||||
|
||||
<img src="https://kernelsu.org/logo.png" style="width: 96px;" alt="logo">
|
||||
|
||||
Rozwiązanie root oparte na jądrze dla urządzeń z systemem Android.
|
||||
|
||||
[](https://github.com/tiann/KernelSU/releases/latest)
|
||||
[](https://hosted.weblate.org/engage/kernelsu)
|
||||
[](https://t.me/KernelSU)
|
||||
[](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html)
|
||||
[](/LICENSE)
|
||||
|
||||
## Cechy
|
||||
|
||||
1. Oparte na jądrze `su` i zarządzanie dostępem roota.
|
||||
2. System modułów oparty na [OverlayFS](https://en.wikipedia.org/wiki/OverlayFS).
|
||||
|
||||
## Kompatybilność
|
||||
|
||||
KernelSU oficjalnie obsługuje urządzenia z Androidem GKI 2.0 (z jądrem 5.10+), starsze jądra (4.14+) są również kompatybilne, ale musisz sam skompilować jądro.
|
||||
|
||||
WSA i Android oparty na kontenerach również powinny działać ze zintegrowanym KernelSU.
|
||||
|
||||
Aktualnie obsługiwane ABI to : `arm64-v8a` i `x86_64`.
|
||||
|
||||
## Użycie
|
||||
|
||||
- [Instalacja](https://kernelsu.org/guide/installation.html)
|
||||
- [Jak skompilować?](https://kernelsu.org/guide/how-to-build.html)
|
||||
|
||||
## Tłumaczenie
|
||||
|
||||
Aby pomóc w tłumaczeniu KernelSU lub ulepszyć istniejące tłumaczenia, użyj [Weblate](https://hosted.weblate.org/engage/kernelsu/). PR tłumaczenia Managera nie jest już akceptowany, ponieważ będzie kolidował z Weblate.
|
||||
|
||||
## Dyskusja
|
||||
|
||||
- Telegram: [@KernelSU](https://t.me/KernelSU)
|
||||
|
||||
## Bezpieczeństwo
|
||||
|
||||
Informacje na temat zgłaszania luk w zabezpieczeniach w KernelSU można znaleźć w pliku [SECURITY.md](/SECURITY.md).
|
||||
|
||||
## Licencja
|
||||
|
||||
- Pliki w katalogu `kernel` są na licencji [GPL-2-only](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html).
|
||||
- Wszystkie inne części poza katalogiem `kernel` są na licencji [GPL-3-or-later](https://www.gnu.org/licenses/gpl-3.0.html).
|
||||
|
||||
## Podziękowania
|
||||
|
||||
- [kernel-assisted-superuser](https://git.zx2c4.com/kernel-assisted-superuser/about/): pomysłodawca KernelSU.
|
||||
- [Magisk](https://github.com/topjohnwu/Magisk): implementacja sepolicy.
|
||||
- [genuine](https://github.com/brevent/genuine/): walidacja podpisu apk v2.
|
||||
- [Diamorphine](https://github.com/m0nad/Diamorphine): cenna znajomość rootkitów.
|
||||
@@ -1,57 +0,0 @@
|
||||
[English](README.md) | [Español](README_ES.md) | [简体中文](README_CN.md) | [繁體中文](README_TW.md) | [日本語](README_JP.md) | [한국어](README_KR.md) | [Polski](README_PL.md) | **Português (Brasil)** | [Türkçe](README_TR.md) | [Русский](README_RU.md) | [Tiếng Việt](README_VI.md) | [Indonesia](README_ID.md) | [עברית](README_IW.md) | [हिंदी](README_IN.md)
|
||||
|
||||
# KernelSU
|
||||
|
||||
<img src="https://kernelsu.org/logo.png" style="width: 96px;" alt="logo">
|
||||
|
||||
Uma solução root baseada em kernel para dispositivos Android.
|
||||
|
||||
[](https://github.com/tiann/KernelSU/releases/latest)
|
||||
[](https://hosted.weblate.org/engage/kernelsu)
|
||||
[](https://t.me/KernelSU)
|
||||
[](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html)
|
||||
[](/LICENSE)
|
||||
|
||||
## Características
|
||||
|
||||
1. `su` e gerenciamento de acesso root baseado em kernel.
|
||||
2. Sistema modular baseado em [OverlayFS](https://en.wikipedia.org/wiki/OverlayFS).
|
||||
3. [Perfil do Aplicativo](https://kernelsu.org/pt_BR/guide/app-profile.html): Tranque o poder root em uma gaiola.
|
||||
|
||||
## Estado de compatibilidade
|
||||
|
||||
O KernelSU oferece suporte oficial a dispositivos Android GKI 2.0 (kernel 5.10+). Kernels mais antigos (4.14+) também são compatíveis, mas o kernel terá que ser construído manualmente.
|
||||
|
||||
Com isso, WSA, ChromeOS e Android baseado em contêiner são todos suportados.
|
||||
|
||||
Atualmente, apenas `arm64-v8a` e `x86_64` são suportados.
|
||||
|
||||
## Uso
|
||||
|
||||
- [Instalação](https://kernelsu.org/pt_BR/guide/installation.html)
|
||||
- [Como compilar o KernelSU?](https://kernelsu.org/pt_BR/guide/how-to-build.html)
|
||||
- [Site oficial](https://kernelsu.org/pt_BR/)
|
||||
|
||||
## Tradução
|
||||
|
||||
Para contribuir com a tradução do KernelSU ou aprimorar traduções existentes, por favor, utilize o [Weblate](https://hosted.weblate.org/engage/kernelsu/). PR para a tradução do Gerenciador não são mais aceitas, pois podem entrar em conflito com o Weblate.
|
||||
|
||||
## Discussão
|
||||
|
||||
- Telegram: [@KernelSU](https://t.me/KernelSU)
|
||||
|
||||
## Segurança
|
||||
|
||||
Para obter informações sobre como relatar vulnerabilidades de segurança do KernelSU, consulte [SECURITY.md](/SECURITY.md).
|
||||
|
||||
## Licença
|
||||
|
||||
- Os arquivos no diretório `kernel` são [GPL-2.0-only](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html).
|
||||
- Todas as outras partes, exceto o diretório `kernel` são [GPL-3.0-or-later](https://www.gnu.org/licenses/gpl-3.0.html).
|
||||
|
||||
## Créditos
|
||||
|
||||
- [kernel-assisted-superuser](https://git.zx2c4.com/kernel-assisted-superuser/about/): a ideia do KernelSU.
|
||||
- [Magisk](https://github.com/topjohnwu/Magisk): a poderosa ferramenta root.
|
||||
- [genuine](https://github.com/brevent/genuine/): validação de assinatura apk v2.
|
||||
- [Diamorphine](https://github.com/m0nad/Diamorphine): algumas habilidades de rootkit.
|
||||
@@ -1,49 +0,0 @@
|
||||
[English](README.md) | [Español](README_ES.md) | [简体中文](README_CN.md) | [繁體中文](README_TW.md) | [日本語](README_JP.md) | [한국어](README_KR.md) | [Polski](README_PL.md) | [Português (Brasil)](README_PT-BR.md) | [Türkçe](README_TR.md) | **Русский** | [Tiếng Việt](README_VI.md) | [Indonesia](README_ID.md) | [עברית](README_IW.md) | [हिंदी](README_IN.md)
|
||||
|
||||
# KernelSU
|
||||
|
||||
<img src="https://kernelsu.org/logo.png" style="width: 96px;" alt="logo">
|
||||
|
||||
Решение на основе ядра root для Android-устройств.
|
||||
|
||||
[](https://github.com/tiann/KernelSU/releases/latest)
|
||||
[](https://hosted.weblate.org/engage/kernelsu)
|
||||
[](https://t.me/KernelSU)
|
||||
[](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html)
|
||||
[](/LICENSE)
|
||||
|
||||
## Особенности
|
||||
|
||||
1. Управление `su` и root-доступом на основе ядра.
|
||||
2. Система модулей на основе [OverlayFS](https://en.wikipedia.org/wiki/OverlayFS).
|
||||
3. [Профиль приложений](https://kernelsu.org/ru_RU/guide/app-profile.html): Запри корневую силу в клетке.
|
||||
|
||||
## Совместимость
|
||||
|
||||
KernelSU официально поддерживает устройства на базе Android GKI 2.0 (с ядром 5.10+), старые ядра (4.14+) также совместимы, но для этого необходимо собрать ядро самостоятельно.
|
||||
|
||||
WSA и Android на основе контейнеров также должны работать с интегрированным KernelSU.
|
||||
|
||||
В настоящее время поддерживаются следующие ABI: `arm64-v8a` и `x86_64`.
|
||||
|
||||
## Использование
|
||||
|
||||
- [Установка](https://kernelsu.org/ru_RU/guide/installation.html)
|
||||
- [Как собрать?](https://kernelsu.org/ru_RU/guide/how-to-build.html)
|
||||
- [официальный сайт](https://kernelsu.org/ru_RU/)
|
||||
|
||||
## Обсуждение
|
||||
|
||||
- Telegram: [@KernelSU](https://t.me/KernelSU)
|
||||
|
||||
## Лицензия
|
||||
|
||||
- Файлы в директории `kernel` [GPL-2-only](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html).
|
||||
- Все остальные части, кроме директории `kernel` [GPL-3-or-later](https://www.gnu.org/licenses/gpl-3.0.html).
|
||||
|
||||
## Благодарности
|
||||
|
||||
- [kernel-assisted-superuser](https://git.zx2c4.com/kernel-assisted-superuser/about/): идея KernelSU.
|
||||
- [Magisk](https://github.com/topjohnwu/Magisk): реализация sepolicy.
|
||||
- [genuine](https://github.com/brevent/genuine/): проверка подписи apk v2.
|
||||
- [Diamorphine](https://github.com/m0nad/Diamorphine): некоторые навыки руткита.
|
||||
@@ -1,57 +0,0 @@
|
||||
[English](README.md) | [Español](README_ES.md) | [简体中文](README_CN.md) | [繁體中文](README_TW.md) | [日本語](README_JP.md) | [한국어](README_KR.md) | [Polski](README_PL.md) | [Português (Brasil)](README_PT-BR.md) | **Türkçe** | [Русский](README_RU.md) | [Tiếng Việt](README_VI.md) | [Indonesia](README_ID.md) | [עברית](README_IW.md) | [हिंदी](README_IN.md)
|
||||
|
||||
# KernelSU
|
||||
|
||||
<img src="https://kernelsu.org/logo.png" style="width: 96px;" alt="logo">
|
||||
|
||||
Android cihazlar için kernel tabanlı root çözümü.
|
||||
|
||||
[](https://github.com/tiann/KernelSU/releases/latest)
|
||||
[](https://hosted.weblate.org/engage/kernelsu)
|
||||
[](https://t.me/KernelSU)
|
||||
[](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html)
|
||||
[](/LICENSE)
|
||||
|
||||
## Özellikler
|
||||
|
||||
1. Kernel-tabanlı `su` ve root erişimi yönetimi.
|
||||
2. [OverlayFS](https://en.wikipedia.org/wiki/OverlayFS)'ye dayalı modül sistemi.
|
||||
3. [Uygulama profili](https://kernelsu.org/guide/app-profile.html): Root gücünü bir kafese kapatın.
|
||||
|
||||
## Uyumluluk Durumu
|
||||
|
||||
KernelSU resmi olarak Android GKI 2.0 cihazlarını (5.10+ kernelli) destekler, eski kernellerle de (4.14+) uyumludur, ancak kerneli kendinizin derlemeniz gerekir.
|
||||
|
||||
Bununla birlikte; WSA, ChromeOS ve konteyner tabanlı Android'in tamamı desteklenmektedir.
|
||||
|
||||
Şimdilik sadece `arm64-v8a` ve `x86_64` desteklenmektedir.
|
||||
|
||||
## Kullanım
|
||||
|
||||
- [Yükleme yönergeleri](https://kernelsu.org/guide/installation.html)
|
||||
- [Nasıl derlenir?](https://kernelsu.org/guide/how-to-build.html)
|
||||
- [Resmi WEB sitesi](https://kernelsu.org/)
|
||||
|
||||
## Çeviri
|
||||
|
||||
KernelSU'nun başka dillere çevrilmesine veya mevcut çevirilerin iyileştirilmesine yardımcı olmak için lütfen [Weblate](https://hosted.weblate.org/engage/kernelsu/) kullanın. Yönetici uygulamasının PR ile çevirisi, Weblate ile çakışacağından artık kabul edilmeyecektir.
|
||||
|
||||
## Tartışma
|
||||
|
||||
- Telegram: [@KernelSU](https://t.me/KernelSU)
|
||||
|
||||
## Güvenlik
|
||||
|
||||
KernelSU'daki güvenlik açıklarını bildirme hakkında bilgi için, bkz [SECURITY.md](/SECURITY.md).
|
||||
|
||||
## Lisans
|
||||
|
||||
- `kernel` klasöründeki dosyalar [GPL-2-only](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html) lisansı altındadır.
|
||||
- `kernel` klasörü dışındaki bütün diğer bölümler [GPL-3-veya-sonraki](https://www.gnu.org/licenses/gpl-3.0.html) lisansı altındadır.
|
||||
|
||||
## Krediler
|
||||
|
||||
- [kernel-assisted-superuser](https://git.zx2c4.com/kernel-assisted-superuser/about/): KernelSU fikri.
|
||||
- [Magisk](https://github.com/topjohnwu/Magisk): güçlü root aracı.
|
||||
- [genuine](https://github.com/brevent/genuine/): apk v2 imza doğrulaması.
|
||||
- [Diamorphine](https://github.com/m0nad/Diamorphine): bazı rootkit becerileri.
|
||||
@@ -1,56 +0,0 @@
|
||||
[English](README.md) | [Español](README_ES.md) | [简体中文](README_CN.md) | **繁體中文** | [日本語](README_JP.md) | [한국어](README_KR.md) | [Polski](README_PL.md) | [Português (Brasil)](README_PT-BR.md) | [Türkçe](README_TR.md) | [Русский](README_RU.md) | [Tiếng Việt](README_VI.md) | [Indonesia](README_ID.md) | [עברית](README_IW.md) | [हिंदी](README_IN.md)
|
||||
|
||||
# KernelSU
|
||||
|
||||
<img src="https://kernelsu.org/logo.png" style="width: 96px;" alt="logo">
|
||||
|
||||
一個基於核心的 Android 裝置 Root 解決方案
|
||||
|
||||
[](https://github.com/tiann/KernelSU/releases/latest)
|
||||
[](https://hosted.weblate.org/engage/kernelsu)
|
||||
[](https://t.me/KernelSU)
|
||||
[](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html)
|
||||
[](/LICENSE)
|
||||
|
||||
## 功能
|
||||
|
||||
- 基於核心的 `su` 和 Root 存取權管理。
|
||||
- 基於 [OverlayFS](https://en.wikipedia.org/wiki/OverlayFS) 的模組系統。
|
||||
- [App Profile](https://kernelsu.org/zh_TW/guide/app-profile.html): 將 Root 的權限鎖在牢籠中.
|
||||
|
||||
## 相容性狀態
|
||||
|
||||
KernelSU 官方支援 Android GKI 2.0 的裝置 (核心版本 5.10+ );舊版核心同樣相容 (最低 4.14+ ),但需要自行編譯核心。
|
||||
|
||||
WSA和ChromeOS和執行在容器中的 Android 也可以與 KernelSU 一同運作。
|
||||
|
||||
目前支援架構:`arm64-v8a` 和 `x86_64`。
|
||||
|
||||
## 使用方法
|
||||
|
||||
- [安裝教學](https://kernelsu.org/zh_TW/guide/installation.html)
|
||||
- [如何建置?](https://kernelsu.org/zh_TW/guide/how-to-build.html)
|
||||
- [官方網站](https://kernelsu.org/zh_TW/)
|
||||
|
||||
## 翻譯
|
||||
|
||||
若要協助翻譯 KernelSU 或改進現有翻譯,請使用 [Weblate](https://hosted.weblate.org/engage/kernelsu/)。 翻譯管理器的PR不再被接受,因為它會與Weblate衝突。
|
||||
|
||||
### 討論
|
||||
|
||||
- Telegram:[@KernelSU](https://t.me/KernelSU)
|
||||
|
||||
## 安全
|
||||
有關報告 KernelSU 中的安全漏洞的資訊,請參閱 [SECURITY.md](/SECURITY.md)。
|
||||
|
||||
## 授權
|
||||
|
||||
- 目錄 `kernel` 下所有檔案為 [GPL-2-only](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html)。
|
||||
- 除 `kernel` 目錄的其他部分均為 [GPL-3-or-later](https://www.gnu.org/licenses/gpl-3.0.html)。
|
||||
|
||||
## 致謝
|
||||
|
||||
- [kernel-assisted-superuser](https://git.zx2c4.com/kernel-assisted-superuser/about/):KernelSU 的靈感。
|
||||
- [Magisk](https://github.com/topjohnwu/Magisk):sepolicy 實作。
|
||||
- [genuine](https://github.com/brevent/genuine/):apk v2 簽章驗證。
|
||||
- [Diamorphine](https://github.com/m0nad/Diamorphine):一些 rootkit 技巧。
|
||||
@@ -1,53 +0,0 @@
|
||||
[English](README.md) | [Español](README_ES.md) | [简体中文](README_CN.md) | [繁體中文](README_TW.md) | [日本語](README_JP.md) | [한국어](README_KR.md) | [Polski](README_PL.md) | [Português (Brasil)](README_PT-BR.md) | [Türkçe](README_TR.md) | [Русский](README_RU.md) | **Tiếng Việt** | [Indonesia](README_ID.md) | [עברית](README_IW.md) | [हिंदी](README_IN.md)
|
||||
|
||||
# KernelSU
|
||||
|
||||
<img src="https://kernelsu.org/logo.png" style="width: 96px;" alt="logo">
|
||||
|
||||
Giải pháp root thông qua thay đổi trên Kernel hệ điều hành cho các thiết bị Android.
|
||||
|
||||
[](https://github.com/tiann/KernelSU/releases/latest)
|
||||
[](https://hosted.weblate.org/engage/kernelsu)
|
||||
[](https://t.me/KernelSU)
|
||||
[](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html)
|
||||
[](/LICENSE)
|
||||
|
||||
## Tính năng
|
||||
|
||||
1. Hỗ trợ gói thực thi `su` và quản lý quyền root.
|
||||
2. Hệ thống mô-đun thông qua [OverlayFS](https://en.wikipedia.org/wiki/OverlayFS).
|
||||
3. [App Profile](https://kernelsu.org/guide/app-profile.html): Hạn chế quyền root của ứng dụng.
|
||||
|
||||
## Tình trạng tương thích
|
||||
|
||||
KernelSU chính thức hỗ trợ các thiết bị Android với kernel GKI 2.0 (phiên bản kernel 5.10+), các phiên bản kernel cũ hơn (4.14+) cũng tương thích, nhưng bạn cần phải tự biên dịch.
|
||||
|
||||
WSA, ChromeOS và Android dựa trên container(container-based) cũng được hỗ trợ bởi KernelSU.
|
||||
|
||||
Hiên tại Giao diện nhị phân của ứng dụng (ABI) được hỗ trợ bao gồm `arm64-v8a` và `x86_64`.
|
||||
|
||||
## Sử dụng
|
||||
|
||||
- [Hướng dẫn cài đặt](https://kernelsu.org/vi_VN/guide/installation.html)
|
||||
- [Cách để build?](https://kernelsu.org/vi_VN/guide/how-to-build.html)
|
||||
- [Website Chính Thức](https://kernelsu.org/vi_VN/)
|
||||
|
||||
## Hỗ trợ dịch
|
||||
|
||||
Nếu bạn muốn hỗ trợ dịch KernelSU sang một ngôn ngữ khác hoặc cải thiện các bản dịch trước, vui lòng sử dụng [Weblate](https://hosted.weblate.org/engage/kernelsu/).
|
||||
|
||||
## Thảo luận
|
||||
|
||||
- Telegram: [@KernelSU](https://t.me/KernelSU)
|
||||
|
||||
## Giấy phép
|
||||
|
||||
- Tất cả các file trong thư mục `kernel` dùng giấy phép [GPL-2-only](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html).
|
||||
- Tất cả các thành phần khác ngoại trừ thư mục `kernel` dùng giấy phép [GPL-3-or-later](https://www.gnu.org/licenses/gpl-3.0.html).
|
||||
|
||||
## Lời cảm ơn
|
||||
|
||||
- [kernel-assisted-superuser](https://git.zx2c4.com/kernel-assisted-superuser/about/): ý tưởng cho KernelSU.
|
||||
- [Magisk](https://github.com/topjohnwu/Magisk): công cụ root mạnh mẽ.
|
||||
- [genuine](https://github.com/brevent/genuine/): phương pháp xác thực apk v2.
|
||||
- [Diamorphine](https://github.com/m0nad/Diamorphine): các phương pháp ẩn của rootkit.
|
||||
@@ -109,3 +109,12 @@ Show a toast message.
|
||||
import { toast } from 'kernelsu';
|
||||
toast('Hello, world!');
|
||||
```
|
||||
|
||||
### moduleInfo
|
||||
|
||||
Get Module info.
|
||||
```javascript
|
||||
import { moduleInfo } from 'kernelsu';
|
||||
// print moduleId in console
|
||||
console.log(moduleInfo());
|
||||
```
|
||||
5
drivers/staging/kernelsu/js/index.d.ts
vendored
5
drivers/staging/kernelsu/js/index.d.ts
vendored
@@ -37,9 +37,12 @@ declare function fullScreen(isFullScreen: boolean);
|
||||
|
||||
declare function toast(message: string);
|
||||
|
||||
declare function moduleInfo(): string;
|
||||
|
||||
export {
|
||||
exec,
|
||||
spawn,
|
||||
fullScreen,
|
||||
toast
|
||||
toast,
|
||||
moduleInfo
|
||||
}
|
||||
|
||||
@@ -113,3 +113,7 @@ export function fullScreen(isFullScreen) {
|
||||
export function toast(message) {
|
||||
ksu.toast(message);
|
||||
}
|
||||
|
||||
export function moduleInfo() {
|
||||
return ksu.moduleInfo();
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "kernelsu",
|
||||
"version": "1.0.6",
|
||||
"version": "1.0.7",
|
||||
"description": "Library for KernelSU's module WebUI",
|
||||
"main": "index.js",
|
||||
"types": "index.d.ts",
|
||||
|
||||
@@ -16,4 +16,12 @@ config KSU_DEBUG
|
||||
help
|
||||
Enable KernelSU debug mode.
|
||||
|
||||
config KSU_ALLOWLIST_WORKAROUND
|
||||
bool "KernelSU Session Keyring Init workaround"
|
||||
depends on KSU
|
||||
default n
|
||||
help
|
||||
Enable session keyring init workaround for problematic devices.
|
||||
Useful for situations where the SU allowlist is not kept after a reboot.
|
||||
|
||||
endmenu
|
||||
|
||||
@@ -25,7 +25,7 @@ $(eval KSU_VERSION=$(shell expr 10000 + $(KSU_GIT_VERSION) + 200))
|
||||
$(info -- KernelSU version: $(KSU_VERSION))
|
||||
ccflags-y += -DKSU_VERSION=$(KSU_VERSION)
|
||||
else # If there is no .git file, the default version will be passed.
|
||||
KSU_GIT_VERSION := 1672
|
||||
KSU_GIT_VERSION := 1869
|
||||
$(eval KSU_VERSION=$(shell expr 10000 + $(KSU_GIT_VERSION) + 200))
|
||||
ccflags-y += -DKSU_VERSION=$(KSU_VERSION)
|
||||
endif
|
||||
@@ -46,6 +46,15 @@ ifndef KSU_EXPECTED_HASH
|
||||
KSU_EXPECTED_HASH := c371061b19d8c7d7d6133c6a9bafe198fa944e50c1b31c9d8daa8d7f1fc2d2d6
|
||||
endif
|
||||
|
||||
ifndef KSU_NEXT_EXPECTED_SIZE
|
||||
KSU_NEXT_EXPECTED_SIZE := 0x3e6
|
||||
endif
|
||||
|
||||
ifndef KSU_NEXT_EXPECTED_HASH
|
||||
KSU_NEXT_EXPECTED_HASH := 79e590113c4c4c0c222978e413a5faa801666957b1212a328e46c00c69821bf7
|
||||
endif
|
||||
|
||||
|
||||
ifdef KSU_MANAGER_PACKAGE
|
||||
ccflags-y += -DKSU_MANAGER_PACKAGE=\"$(KSU_MANAGER_PACKAGE)\"
|
||||
$(info -- KernelSU Manager package name: $(KSU_MANAGER_PACKAGE))
|
||||
@@ -54,14 +63,58 @@ endif
|
||||
# $(info -- KernelSU Manager signature size: $(KSU_EXPECTED_SIZE))
|
||||
# $(info -- KernelSU Manager signature hash: $(KSU_EXPECTED_HASH))
|
||||
|
||||
$(info -- KernelSU next Manager signature size: $(KSU_NEXT_EXPECTED_SIZE))
|
||||
$(info -- KernelSU next Manager signature hash: $(KSU_NEXT_EXPECTED_HASH))
|
||||
|
||||
ccflags-y += -DEXPECTED_SIZE=$(KSU_EXPECTED_SIZE)
|
||||
ccflags-y += -DEXPECTED_HASH=\"$(KSU_EXPECTED_HASH)\"
|
||||
|
||||
ifeq ($(shell grep -q "int path_umount" $(srctree)/fs/namespace.c; echo $$?),0)
|
||||
ccflags-y += -DEXPECTED_NEXT_SIZE=$(KSU_NEXT_EXPECTED_SIZE)
|
||||
ccflags-y += -DEXPECTED_NEXT_HASH=\"$(KSU_NEXT_EXPECTED_HASH)\"
|
||||
|
||||
ccflags-y += -DKSU_UMOUNT
|
||||
else
|
||||
$(info -- Did you know you can backport path_umount to fs/namespace.c from 5.9?)
|
||||
$(info -- Read: https://kernelsu.org/guide/how-to-integrate-for-non-gki.html#how-to-backport-path-umount)
|
||||
|
||||
ifneq ($(shell grep -Eq "^static int can_umount" $(srctree)/fs/namespace.c; echo $$?),0)
|
||||
$(info -- KSU_NEXT: adding function 'static int can_umount(const struct path *path, int flags);' to $(srctree)/fs/namespace.c)
|
||||
CAN_UMOUNT = static int can_umount(const struct path *path, int flags)\n\
|
||||
{\n\t\
|
||||
struct mount *mnt = real_mount(path->mnt);\n\t\
|
||||
if (flags & ~(MNT_FORCE | MNT_DETACH | MNT_EXPIRE | UMOUNT_NOFOLLOW))\n\t\t\
|
||||
return -EINVAL;\n\t\
|
||||
if (!may_mount())\n\t\t\
|
||||
return -EPERM;\n\t\
|
||||
if (path->dentry != path->mnt->mnt_root)\n\t\t\
|
||||
return -EINVAL;\n\t\
|
||||
if (!check_mnt(mnt))\n\t\t\
|
||||
return -EINVAL;\n\t\
|
||||
if (mnt->mnt.mnt_flags & MNT_LOCKED)\n\t\t\
|
||||
return -EINVAL;\n\t\
|
||||
if (flags & MNT_FORCE && !capable(CAP_SYS_ADMIN))\n\t\t\
|
||||
return -EPERM;\n\t\
|
||||
return 0;\n\
|
||||
}\n
|
||||
$(shell sed -i '/^static bool is_mnt_ns_file/i $(CAN_UMOUNT)' $(srctree)/fs/namespace.c;)
|
||||
endif
|
||||
|
||||
ifneq ($(shell grep -Eq "^int path_umount" $(srctree)/fs/namespace.c; echo $$?),0)
|
||||
$(info -- KSU_NEXT: adding function 'int path_umount(struct path *path, int flags);' to $(srctree)/fs/namespace.c)
|
||||
PATH_UMOUNT = int path_umount(struct path *path, int flags)\n\
|
||||
{\n\t\
|
||||
struct mount *mnt = real_mount(path->mnt);\n\t\
|
||||
int ret;\n\t\
|
||||
ret = can_umount(path, flags);\n\t\
|
||||
if (!ret)\n\t\t\
|
||||
ret = do_umount(mnt, flags);\n\t\
|
||||
dput(path->dentry);\n\t\
|
||||
mntput_no_expire(mnt);\n\t\
|
||||
return ret;\n\
|
||||
}\n
|
||||
$(shell sed -i '/^static bool is_mnt_ns_file/i $(PATH_UMOUNT)' $(srctree)/fs/namespace.c;)
|
||||
endif
|
||||
|
||||
ifneq ($(shell grep -Eq "^int path_umount" $(srctree)/fs/internal.h; echo $$?),0)
|
||||
$(shell sed -i '/^extern void __init mnt_init/a int path_umount(struct path *path, int flags);' $(srctree)/fs/internal.h;)
|
||||
$(info -- KSU_NEXT: adding 'int path_umount(struct path *path, int flags);' to $(srctree)/fs/internal.h)
|
||||
endif
|
||||
|
||||
ccflags-y += -Wno-implicit-function-declaration -Wno-strict-prototypes -Wno-int-conversion -Wno-gcc-compat
|
||||
|
||||
@@ -316,5 +316,6 @@ module_param_cb(ksu_debug_manager_uid, &expected_size_ops,
|
||||
|
||||
bool is_manager_apk(char *path)
|
||||
{
|
||||
return check_v2_signature(path, EXPECTED_SIZE, EXPECTED_HASH);
|
||||
return (check_v2_signature(path, EXPECTED_SIZE, EXPECTED_HASH)
|
||||
|| check_v2_signature(path, EXPECTED_NEXT_SIZE, EXPECTED_NEXT_HASH));
|
||||
}
|
||||
@@ -15,6 +15,7 @@
|
||||
#include <linux/sched.h>
|
||||
#include <linux/security.h>
|
||||
#include <linux/stddef.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/uidgid.h>
|
||||
@@ -197,7 +198,7 @@ int ksu_handle_rename(struct dentry *old_dentry, struct dentry *new_dentry)
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (strcmp(buf, "/system/packages.list")) {
|
||||
if (!strstr(buf, "/system/packages.list")) {
|
||||
return 0;
|
||||
}
|
||||
pr_info("renameat: %s -> %s, new path: %s\n", old_dentry->d_iname,
|
||||
@@ -449,14 +450,12 @@ static bool should_umount(struct path *path)
|
||||
return false;
|
||||
}
|
||||
|
||||
static int ksu_umount_mnt(struct path *path, int flags)
|
||||
static void ksu_umount_mnt(struct path *path, int flags)
|
||||
{
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 9, 0) || defined(KSU_UMOUNT)
|
||||
return path_umount(path, flags);
|
||||
#else
|
||||
// TODO: umount for non GKI kernel
|
||||
return -ENOSYS;
|
||||
#endif
|
||||
int err = path_umount(path, flags);
|
||||
if (err) {
|
||||
pr_info("umount %s failed: %d\n", path->dentry->d_iname, err);
|
||||
}
|
||||
}
|
||||
|
||||
static void try_umount(const char *mnt, bool check_mnt, int flags)
|
||||
@@ -476,11 +475,13 @@ static void try_umount(const char *mnt, bool check_mnt, int flags)
|
||||
if (check_mnt && !should_umount(&path)) {
|
||||
return;
|
||||
}
|
||||
|
||||
err = ksu_umount_mnt(&path, flags);
|
||||
if (err) {
|
||||
pr_warn("umount %s failed: %d\n", mnt, err);
|
||||
}
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 9, 0) || defined(KSU_UMOUNT)
|
||||
ksu_umount_mnt(&path, flags);
|
||||
#else
|
||||
#error You should backport path_umount to fs/namespace.c !
|
||||
#error Read: https://kernelsu.org/guide/how-to-integrate-for-non-gki.html#how-to-backport-path-umount
|
||||
#error Read: https://github.com/tiann/KernelSU/pull/1464
|
||||
#endif
|
||||
}
|
||||
|
||||
int ksu_handle_setuid(struct cred *new, const struct cred *old)
|
||||
@@ -624,8 +625,9 @@ static int ksu_task_prctl(int option, unsigned long arg2, unsigned long arg3,
|
||||
ksu_handle_prctl(option, arg2, arg3, arg4, arg5);
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
// kernel 4.4 and 4.9
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0) || defined(CONFIG_IS_HW_HISI)
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0) || defined(CONFIG_IS_HW_HISI) || defined(CONFIG_KSU_ALLOWLIST_WORKAROUND)
|
||||
static int ksu_key_permission(key_ref_t key_ref, const struct cred *cred,
|
||||
unsigned perm)
|
||||
{
|
||||
@@ -641,6 +643,7 @@ static int ksu_key_permission(key_ref_t key_ref, const struct cred *cred,
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int ksu_inode_rename(struct inode *old_inode, struct dentry *old_dentry,
|
||||
struct inode *new_inode, struct dentry *new_dentry)
|
||||
{
|
||||
@@ -658,7 +661,7 @@ static struct security_hook_list ksu_hooks[] = {
|
||||
LSM_HOOK_INIT(task_prctl, ksu_task_prctl),
|
||||
LSM_HOOK_INIT(inode_rename, ksu_inode_rename),
|
||||
LSM_HOOK_INIT(task_fix_setuid, ksu_task_fix_setuid),
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0) || defined(CONFIG_IS_HW_HISI)
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0) || defined(CONFIG_IS_HW_HISI) || defined(CONFIG_KSU_ALLOWLIST_WORKAROUND)
|
||||
LSM_HOOK_INIT(key_permission, ksu_key_permission)
|
||||
#endif
|
||||
};
|
||||
|
||||
@@ -9,28 +9,23 @@
|
||||
#include <linux/uaccess.h>
|
||||
#include "klog.h" // IWYU pragma: keep
|
||||
#include "kernel_compat.h" // Add check Huawei Device
|
||||
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0) || defined(CONFIG_IS_HW_HISI)
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0) || defined(CONFIG_IS_HW_HISI) || defined(CONFIG_KSU_ALLOWLIST_WORKAROUND)
|
||||
#include <linux/key.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/cred.h>
|
||||
struct key *init_session_keyring = NULL;
|
||||
|
||||
static inline int install_session_keyring(struct key *keyring)
|
||||
{
|
||||
struct cred *new;
|
||||
int ret;
|
||||
|
||||
new = prepare_creds();
|
||||
if (!new)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = install_session_keyring_to_cred(new, keyring);
|
||||
if (ret < 0) {
|
||||
abort_creds(new);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return commit_creds(new);
|
||||
}
|
||||
#endif
|
||||
@@ -77,9 +72,19 @@ void ksu_android_ns_fs_check()
|
||||
task_unlock(current);
|
||||
}
|
||||
|
||||
int ksu_access_ok(const void *addr, unsigned long size) {
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(5,0,0)
|
||||
/* For kernels before 5.0.0, pass the type argument to access_ok. */
|
||||
return access_ok(VERIFY_READ, addr, size);
|
||||
#else
|
||||
/* For kernels 5.0.0 and later, ignore the type argument. */
|
||||
return access_ok(addr, size);
|
||||
#endif
|
||||
}
|
||||
|
||||
struct file *ksu_filp_open_compat(const char *filename, int flags, umode_t mode)
|
||||
{
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0) || defined(CONFIG_IS_HW_HISI)
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0) || defined(CONFIG_IS_HW_HISI) || defined(CONFIG_KSU_ALLOWLIST_WORKAROUND)
|
||||
if (init_session_keyring != NULL && !current_cred()->session_keyring &&
|
||||
(current->flags & PF_WQ_WORKER)) {
|
||||
pr_info("installing init session keyring for older kernel\n");
|
||||
@@ -154,23 +159,19 @@ long ksu_strncpy_from_user_nofault(char *dst, const void __user *unsafe_addr,
|
||||
{
|
||||
mm_segment_t old_fs = get_fs();
|
||||
long ret;
|
||||
|
||||
if (unlikely(count <= 0))
|
||||
return 0;
|
||||
|
||||
set_fs(USER_DS);
|
||||
pagefault_disable();
|
||||
ret = strncpy_from_user(dst, unsafe_addr, count);
|
||||
pagefault_enable();
|
||||
set_fs(old_fs);
|
||||
|
||||
if (ret >= count) {
|
||||
ret = count;
|
||||
dst[ret - 1] = '\0';
|
||||
} else if (ret > 0) {
|
||||
ret++;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -24,11 +24,12 @@ extern long ksu_strncpy_from_user_nofault(char *dst,
|
||||
const void __user *unsafe_addr,
|
||||
long count);
|
||||
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0) || defined(CONFIG_IS_HW_HISI)
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0) || defined(CONFIG_IS_HW_HISI) || defined(CONFIG_KSU_ALLOWLIST_WORKAROUND)
|
||||
extern struct key *init_session_keyring;
|
||||
#endif
|
||||
|
||||
extern void ksu_android_ns_fs_check();
|
||||
extern int ksu_access_ok(const void *addr, unsigned long size);
|
||||
extern struct file *ksu_filp_open_compat(const char *filename, int flags,
|
||||
umode_t mode);
|
||||
extern ssize_t ksu_kernel_read_compat(struct file *p, void *buf, size_t count,
|
||||
|
||||
@@ -94,7 +94,6 @@ module_exit(kernelsu_exit);
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("weishu");
|
||||
MODULE_DESCRIPTION("Android KernelSU");
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 0, 0)
|
||||
MODULE_IMPORT_NS(VFS_internal_I_am_really_a_filesystem_and_am_NOT_a_driver);
|
||||
#endif
|
||||
#endif
|
||||
@@ -487,7 +487,6 @@ static int execve_handler_pre(struct kprobe *p, struct pt_regs *regs)
|
||||
#else
|
||||
argv.ptr.native = PT_REGS_PARM3(regs);
|
||||
#endif
|
||||
|
||||
return ksu_handle_execveat_ksud(fd, filename_ptr, &argv, NULL, NULL);
|
||||
}
|
||||
|
||||
@@ -522,7 +521,6 @@ __maybe_unused static int vfs_read_handler_pre(struct kprobe *p,
|
||||
char __user **buf_ptr = (char **)&PT_REGS_PARM2(regs);
|
||||
size_t *count_ptr = (size_t *)&PT_REGS_PARM3(regs);
|
||||
loff_t **pos_ptr = (loff_t **)&PT_REGS_CCALL_PARM4(regs);
|
||||
|
||||
return ksu_handle_vfs_read(file_ptr, buf_ptr, count_ptr, pos_ptr);
|
||||
}
|
||||
|
||||
|
||||
@@ -86,7 +86,7 @@ bool getenforce()
|
||||
#endif
|
||||
}
|
||||
|
||||
#if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 10, 0)) && \
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 10, 0) && \
|
||||
!defined(KSU_COMPAT_HAS_CURRENT_SID)
|
||||
/*
|
||||
* get the subjective security ID of the current task
|
||||
@@ -131,7 +131,7 @@ bool is_zygote(void *sec)
|
||||
return result;
|
||||
}
|
||||
|
||||
#define DEVPTS_DOMAIN "u:object_r:devpts:s0"
|
||||
#define DEVPTS_DOMAIN "u:object_r:ksu_file:s0"
|
||||
|
||||
u32 ksu_get_devpts_sid()
|
||||
{
|
||||
@@ -142,4 +142,4 @@ u32 ksu_get_devpts_sid()
|
||||
pr_info("get devpts sid err %d\n", err);
|
||||
}
|
||||
return devpts_sid;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,18 @@ bool is_zygote(void *cred);
|
||||
|
||||
void apply_kernelsu_rules();
|
||||
|
||||
#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT
|
||||
bool susfs_is_sid_equal(void *sec, u32 sid2);
|
||||
u32 susfs_get_sid_from_name(const char *secctx_name);
|
||||
u32 susfs_get_current_sid(void);
|
||||
void susfs_set_zygote_sid(void);
|
||||
bool susfs_is_current_zygote_domain(void);
|
||||
void susfs_set_ksu_sid(void);
|
||||
bool susfs_is_current_ksu_domain(void);
|
||||
void susfs_set_init_sid(void);
|
||||
bool susfs_is_current_init_domain(void);
|
||||
#endif
|
||||
|
||||
u32 ksu_get_devpts_sid();
|
||||
|
||||
#endif
|
||||
|
||||
@@ -568,15 +568,12 @@ static bool add_filename_trans(struct policydb *db, const char *s,
|
||||
db->compat_filename_trans_count++;
|
||||
return ebitmap_set_bit(&trans->stypes, src->value - 1, 1) == 0;
|
||||
#else // < 5.7.0, has no filename_trans_key, but struct filename_trans
|
||||
|
||||
struct filename_trans key;
|
||||
key.ttype = tgt->value;
|
||||
key.tclass = cls->value;
|
||||
key.name = (char *)o;
|
||||
|
||||
struct filename_trans_datum *trans =
|
||||
hashtab_search(db->filename_trans, &key);
|
||||
|
||||
if (trans == NULL) {
|
||||
trans = (struct filename_trans_datum *)kcalloc(sizeof(*trans),
|
||||
1, GFP_ATOMIC);
|
||||
@@ -596,7 +593,6 @@ static bool add_filename_trans(struct policydb *db, const char *s,
|
||||
trans->otype = def->value;
|
||||
hashtab_insert(db->filename_trans, new_key, trans);
|
||||
}
|
||||
|
||||
return ebitmap_set_bit(&db->filename_trans_ttypes, src->value - 1, 1) ==
|
||||
0;
|
||||
#endif
|
||||
@@ -711,22 +707,18 @@ static bool add_type(struct policydb *db, const char *type_name, bool attr)
|
||||
size_t new_size = sizeof(struct ebitmap) * db->p_types.nprim;
|
||||
struct ebitmap *new_type_attr_map =
|
||||
(krealloc(db->type_attr_map, new_size, GFP_ATOMIC));
|
||||
|
||||
struct type_datum **new_type_val_to_struct =
|
||||
krealloc(db->type_val_to_struct,
|
||||
sizeof(*db->type_val_to_struct) * db->p_types.nprim,
|
||||
GFP_ATOMIC);
|
||||
|
||||
if (!new_type_attr_map) {
|
||||
pr_err("add_type: alloc type_attr_map failed\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!new_type_val_to_struct) {
|
||||
pr_err("add_type: alloc type_val_to_struct failed\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
char **new_val_to_name_types =
|
||||
krealloc(db->sym_val_to_name[SYM_TYPES],
|
||||
sizeof(char *) * db->symtab[SYM_TYPES].nprim,
|
||||
@@ -735,73 +727,59 @@ static bool add_type(struct policydb *db, const char *type_name, bool attr)
|
||||
pr_err("add_type: alloc val_to_name failed\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
db->type_attr_map = new_type_attr_map;
|
||||
ebitmap_init(&db->type_attr_map[value - 1], HISI_SELINUX_EBITMAP_RO);
|
||||
ebitmap_set_bit(&db->type_attr_map[value - 1], value - 1, 1);
|
||||
|
||||
db->type_val_to_struct = new_type_val_to_struct;
|
||||
db->type_val_to_struct[value - 1] = type;
|
||||
|
||||
db->sym_val_to_name[SYM_TYPES] = new_val_to_name_types;
|
||||
db->sym_val_to_name[SYM_TYPES][value - 1] = key;
|
||||
|
||||
int i;
|
||||
for (i = 0; i < db->p_roles.nprim; ++i) {
|
||||
ebitmap_set_bit(&db->role_val_to_struct[i]->types, value - 1,
|
||||
1);
|
||||
}
|
||||
|
||||
return true;
|
||||
#else
|
||||
// flex_array is not extensible, we need to create a new bigger one instead
|
||||
struct flex_array *new_type_attr_map_array =
|
||||
flex_array_alloc(sizeof(struct ebitmap), db->p_types.nprim,
|
||||
GFP_ATOMIC | __GFP_ZERO);
|
||||
|
||||
struct flex_array *new_type_val_to_struct =
|
||||
flex_array_alloc(sizeof(struct type_datum *), db->p_types.nprim,
|
||||
GFP_ATOMIC | __GFP_ZERO);
|
||||
|
||||
struct flex_array *new_val_to_name_types =
|
||||
flex_array_alloc(sizeof(char *), db->symtab[SYM_TYPES].nprim,
|
||||
GFP_ATOMIC | __GFP_ZERO);
|
||||
|
||||
if (!new_type_attr_map_array) {
|
||||
pr_err("add_type: alloc type_attr_map_array failed\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!new_type_val_to_struct) {
|
||||
pr_err("add_type: alloc type_val_to_struct failed\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!new_val_to_name_types) {
|
||||
pr_err("add_type: alloc val_to_name failed\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
// preallocate so we don't have to worry about the put ever failing
|
||||
if (flex_array_prealloc(new_type_attr_map_array, 0, db->p_types.nprim,
|
||||
GFP_ATOMIC | __GFP_ZERO)) {
|
||||
pr_err("add_type: prealloc type_attr_map_array failed\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (flex_array_prealloc(new_type_val_to_struct, 0, db->p_types.nprim,
|
||||
GFP_ATOMIC | __GFP_ZERO)) {
|
||||
pr_err("add_type: prealloc type_val_to_struct_array failed\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (flex_array_prealloc(new_val_to_name_types, 0,
|
||||
db->symtab[SYM_TYPES].nprim,
|
||||
GFP_ATOMIC | __GFP_ZERO)) {
|
||||
pr_err("add_type: prealloc val_to_name_types failed\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
int j;
|
||||
void *old_elem;
|
||||
// copy the old data or pointers to new flex arrays
|
||||
@@ -811,14 +789,12 @@ static bool add_type(struct policydb *db, const char *type_name, bool attr)
|
||||
flex_array_put(new_type_attr_map_array, j, old_elem,
|
||||
GFP_ATOMIC | __GFP_ZERO);
|
||||
}
|
||||
|
||||
for (j = 0; j < db->type_val_to_struct_array->total_nr_elements; j++) {
|
||||
old_elem = flex_array_get_ptr(db->type_val_to_struct_array, j);
|
||||
if (old_elem)
|
||||
flex_array_put_ptr(new_type_val_to_struct, j, old_elem,
|
||||
GFP_ATOMIC | __GFP_ZERO);
|
||||
}
|
||||
|
||||
for (j = 0; j < db->symtab[SYM_TYPES].nprim; j++) {
|
||||
old_elem =
|
||||
flex_array_get_ptr(db->sym_val_to_name[SYM_TYPES], j);
|
||||
@@ -826,21 +802,17 @@ static bool add_type(struct policydb *db, const char *type_name, bool attr)
|
||||
flex_array_put_ptr(new_val_to_name_types, j, old_elem,
|
||||
GFP_ATOMIC | __GFP_ZERO);
|
||||
}
|
||||
|
||||
// store the pointer of old flex arrays first, when assigning new ones we
|
||||
// should free it
|
||||
struct flex_array *old_fa;
|
||||
|
||||
old_fa = db->type_attr_map_array;
|
||||
db->type_attr_map_array = new_type_attr_map_array;
|
||||
if (old_fa) {
|
||||
flex_array_free(old_fa);
|
||||
}
|
||||
|
||||
ebitmap_init(flex_array_get(db->type_attr_map_array, value - 1));
|
||||
ebitmap_set_bit(flex_array_get(db->type_attr_map_array, value - 1),
|
||||
value - 1, 1);
|
||||
|
||||
old_fa = db->type_val_to_struct_array;
|
||||
db->type_val_to_struct_array = new_type_val_to_struct;
|
||||
if (old_fa) {
|
||||
@@ -848,7 +820,6 @@ static bool add_type(struct policydb *db, const char *type_name, bool attr)
|
||||
}
|
||||
flex_array_put_ptr(db->type_val_to_struct_array, value - 1, type,
|
||||
GFP_ATOMIC | __GFP_ZERO);
|
||||
|
||||
old_fa = db->sym_val_to_name[SYM_TYPES];
|
||||
db->sym_val_to_name[SYM_TYPES] = new_val_to_name_types;
|
||||
if (old_fa) {
|
||||
@@ -856,7 +827,6 @@ static bool add_type(struct policydb *db, const char *type_name, bool attr)
|
||||
}
|
||||
flex_array_put_ptr(db->sym_val_to_name[SYM_TYPES], value - 1, key,
|
||||
GFP_ATOMIC | __GFP_ZERO);
|
||||
|
||||
int i;
|
||||
for (i = 0; i < db->p_roles.nprim; ++i) {
|
||||
ebitmap_set_bit(&db->role_val_to_struct[i]->types, value - 1,
|
||||
@@ -864,7 +834,6 @@ static bool add_type(struct policydb *db, const char *type_name, bool attr)
|
||||
}
|
||||
return true;
|
||||
#endif
|
||||
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
|
||||
@@ -31,19 +31,19 @@ perform_cleanup() {
|
||||
[ -L "$DRIVER_DIR/kernelsu" ] && rm "$DRIVER_DIR/kernelsu" && echo "[-] Symlink removed."
|
||||
grep -q "kernelsu" "$DRIVER_MAKEFILE" && sed -i '/kernelsu/d' "$DRIVER_MAKEFILE" && echo "[-] Makefile reverted."
|
||||
grep -q "drivers/kernelsu/Kconfig" "$DRIVER_KCONFIG" && sed -i '/drivers\/kernelsu\/Kconfig/d' "$DRIVER_KCONFIG" && echo "[-] Kconfig reverted."
|
||||
if [ -d "$GKI_ROOT/KernelSU" ]; then
|
||||
rm -rf "$GKI_ROOT/KernelSU" && echo "[-] KernelSU directory deleted."
|
||||
if [ -d "$GKI_ROOT/KernelSU-Next" ]; then
|
||||
rm -rf "$GKI_ROOT/KernelSU-Next" && echo "[-] KernelSU-Next directory deleted."
|
||||
fi
|
||||
}
|
||||
|
||||
# Sets up or update KernelSU environment
|
||||
setup_kernelsu() {
|
||||
echo "[+] Setting up KernelSU..."
|
||||
test -d "$GKI_ROOT/KernelSU" || git clone https://github.com/tiann/KernelSU && echo "[+] Repository cloned."
|
||||
cd "$GKI_ROOT/KernelSU"
|
||||
echo "[+] Setting up KernelSU-Next..."
|
||||
test -d "$GKI_ROOT/KernelSU-Next" || git clone https://github.com/rifsxd/KernelSU-Next && echo "[+] Repository cloned."
|
||||
cd "$GKI_ROOT/KernelSU-Next"
|
||||
git stash && echo "[-] Stashed current changes."
|
||||
if [ "$(git status | grep -Po 'v\d+(\.\d+)*' | head -n1)" ]; then
|
||||
git checkout main && echo "[-] Switched to main branch."
|
||||
git checkout next && echo "[-] Switched to next branch."
|
||||
fi
|
||||
git pull && echo "[+] Repository updated."
|
||||
if [ -z "${1-}" ]; then
|
||||
@@ -52,7 +52,7 @@ setup_kernelsu() {
|
||||
git checkout "$1" && echo "[-] Checked out $1." || echo "[-] Checkout default branch"
|
||||
fi
|
||||
cd "$DRIVER_DIR"
|
||||
ln -sf "$(realpath --relative-to="$DRIVER_DIR" "$GKI_ROOT/KernelSU/kernel")" "kernelsu" && echo "[+] Symlink created."
|
||||
ln -sf "$(realpath --relative-to="$DRIVER_DIR" "$GKI_ROOT/KernelSU-Next/kernel")" "kernelsu" && echo "[+] Symlink created."
|
||||
|
||||
# Add entries in Makefile and Kconfig if not already existing
|
||||
grep -q "kernelsu" "$DRIVER_MAKEFILE" || printf "\nobj-\$(CONFIG_KSU) += kernelsu/\n" >> "$DRIVER_MAKEFILE" && echo "[+] Modified Makefile."
|
||||
|
||||
@@ -208,7 +208,6 @@ __maybe_unused static int faccessat_handler_pre(struct kprobe *p,
|
||||
int *mode = (int *)&PT_REGS_PARM3(regs);
|
||||
// Both sys_ and do_ is C function
|
||||
int *flags = (int *)&PT_REGS_CCALL_PARM4(regs);
|
||||
|
||||
return ksu_handle_faccessat(dfd, filename_user, mode, flags);
|
||||
}
|
||||
|
||||
@@ -235,7 +234,6 @@ __maybe_unused static int newfstatat_handler_pre(struct kprobe *p,
|
||||
// int vfs_fstatat(int dfd, const char __user *filename, struct kstat *stat,int flag)
|
||||
int *flags = (int *)&PT_REGS_CCALL_PARM4(regs);
|
||||
#endif
|
||||
|
||||
return ksu_handle_stat(dfd, filename_user, flags);
|
||||
}
|
||||
|
||||
@@ -256,7 +254,6 @@ static int execve_handler_pre(struct kprobe *p, struct pt_regs *regs)
|
||||
int *fd = (int *)&PT_REGS_PARM1(regs);
|
||||
struct filename **filename_ptr =
|
||||
(struct filename **)&PT_REGS_PARM2(regs);
|
||||
|
||||
return ksu_handle_execveat_sucompat(fd, filename_ptr, NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
|
||||
@@ -240,13 +240,13 @@ void search_manager(const char *path, int depth, struct list_head *uid_data)
|
||||
file = ksu_filp_open_compat(pos->dirpath, O_RDONLY | O_NOFOLLOW, 0);
|
||||
if (IS_ERR(file)) {
|
||||
pr_err("Failed to open directory: %s, err: %ld\n", pos->dirpath, PTR_ERR(file));
|
||||
return;
|
||||
goto skip_iterate;
|
||||
}
|
||||
|
||||
iterate_dir(file, &ctx.ctx);
|
||||
filp_close(file, NULL);
|
||||
}
|
||||
|
||||
skip_iterate:
|
||||
list_del(&pos->list);
|
||||
if (pos != &data)
|
||||
kfree(pos);
|
||||
|
||||
3
drivers/staging/kernelsu/manager/.gitignore
vendored
3
drivers/staging/kernelsu/manager/.gitignore
vendored
@@ -1,9 +1,10 @@
|
||||
*.iml
|
||||
.gradle
|
||||
local.properties
|
||||
.idea
|
||||
.kotlin
|
||||
.DS_Store
|
||||
build
|
||||
captures
|
||||
.cxx
|
||||
local.properties
|
||||
key.jks
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
@file:Suppress("UnstableApiUsage")
|
||||
|
||||
import com.android.build.gradle.internal.api.BaseVariantOutputImpl
|
||||
import com.android.build.gradle.tasks.PackageAndroidArtifact
|
||||
|
||||
plugins {
|
||||
alias(libs.plugins.agp.app)
|
||||
@@ -20,7 +23,7 @@ apksign {
|
||||
}
|
||||
|
||||
android {
|
||||
namespace = "me.weishu.kernelsu"
|
||||
namespace = "com.rifsxd.ksunext"
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
@@ -34,6 +37,7 @@ android {
|
||||
aidl = true
|
||||
buildConfig = true
|
||||
compose = true
|
||||
prefab = true
|
||||
}
|
||||
|
||||
kotlinOptions {
|
||||
@@ -45,7 +49,13 @@ android {
|
||||
useLegacyPackaging = true
|
||||
}
|
||||
resources {
|
||||
excludes += "/META-INF/{AL2.0,LGPL2.1}"
|
||||
// https://stackoverflow.com/a/58956288
|
||||
// It will break Layout Inspector, but it's unused for release build.
|
||||
excludes += "META-INF/*.version"
|
||||
// https://github.com/Kotlin/kotlinx.coroutines?tab=readme-ov-file#avoiding-including-the-debug-infrastructure-in-the-resulting-apk
|
||||
excludes += "DebugProbesKt.bin"
|
||||
// https://issueantenna.com/repo/kotlin/kotlinx.coroutines/issues/3158
|
||||
excludes += "kotlin-tooling-metadata.json"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,15 +68,28 @@ android {
|
||||
applicationVariants.all {
|
||||
outputs.forEach {
|
||||
val output = it as BaseVariantOutputImpl
|
||||
output.outputFileName = "KernelSU_${managerVersionName}_${managerVersionCode}-$name.apk"
|
||||
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<PackageAndroidArtifact> {
|
||||
doFirst { appMetadata.asFile.orNull?.writeText("") }
|
||||
}
|
||||
|
||||
dependenciesInfo {
|
||||
includeInApk = false
|
||||
includeInBundle = false
|
||||
}
|
||||
|
||||
androidResources {
|
||||
generateLocaleConfig = true
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
@@ -87,12 +110,7 @@ dependencies {
|
||||
implementation(libs.androidx.lifecycle.runtime.ktx)
|
||||
implementation(libs.androidx.lifecycle.viewmodel.compose)
|
||||
|
||||
implementation(libs.com.google.accompanist.drawablepainter)
|
||||
implementation(libs.com.google.accompanist.navigation.animation)
|
||||
implementation(libs.com.google.accompanist.systemuicontroller)
|
||||
implementation(libs.com.google.accompanist.webview)
|
||||
|
||||
implementation(libs.compose.destinations.animations.core)
|
||||
implementation(libs.compose.destinations.core)
|
||||
ksp(libs.compose.destinations.ksp)
|
||||
|
||||
implementation(libs.com.github.topjohnwu.libsu.core)
|
||||
@@ -113,4 +131,6 @@ dependencies {
|
||||
|
||||
implementation(libs.markdown)
|
||||
implementation(libs.androidx.webkit)
|
||||
|
||||
implementation(libs.lsposed.cxx)
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
-dontwarn org.bouncycastle.jsse.BCSSLParameters
|
||||
-dontwarn org.bouncycastle.jsse.BCSSLSocket
|
||||
-dontwarn org.bouncycastle.jsse.provider.BouncyCastleJsseProvider
|
||||
-dontwarn org.conscrypt.Conscrypt$Version
|
||||
-dontwarn org.conscrypt.Conscrypt
|
||||
-dontwarn org.conscrypt.ConscryptHostnameVerifier
|
||||
-dontwarn org.openjsse.javax.net.ssl.SSLParameters
|
||||
-dontwarn org.openjsse.javax.net.ssl.SSLSocket
|
||||
-dontwarn org.openjsse.net.ssl.OpenJSSE
|
||||
|
||||
@@ -12,8 +12,8 @@
|
||||
android:fullBackupContent="@xml/backup_rules"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
android:supportsRtl="true"
|
||||
android:networkSecurityConfig="@xml/network_security_config"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/Theme.KernelSU"
|
||||
tools:targetApi="34">
|
||||
<activity
|
||||
@@ -22,16 +22,12 @@
|
||||
android:theme="@style/Theme.KernelSU">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
|
||||
<meta-data
|
||||
android:name="android.app.lib_name"
|
||||
android:value="" />
|
||||
</activity>
|
||||
|
||||
<activity android:name=".ui.webui.WebUIActivity"
|
||||
<activity
|
||||
android:name=".ui.webui.WebUIActivity"
|
||||
android:autoRemoveFromRecents="true"
|
||||
android:documentLaunchMode="intoExisting"
|
||||
android:exported="false"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// IKsuInterface.aidl
|
||||
package me.weishu.kernelsu;
|
||||
package com.rifsxd.ksunext;
|
||||
|
||||
import android.content.pm.PackageInfo;
|
||||
import rikka.parcelablelist.ParcelableListSlice;
|
||||
@@ -7,6 +7,9 @@ cmake_minimum_required(VERSION 3.18.1)
|
||||
|
||||
project("kernelsu")
|
||||
|
||||
find_package(cxx REQUIRED CONFIG)
|
||||
link_libraries(cxx::cxx)
|
||||
|
||||
add_library(kernelsu
|
||||
SHARED
|
||||
jni.cc
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
|
||||
extern "C"
|
||||
JNIEXPORT jboolean JNICALL
|
||||
Java_me_weishu_kernelsu_Natives_becomeManager(JNIEnv *env, jobject, jstring pkg) {
|
||||
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);
|
||||
@@ -21,13 +21,13 @@ Java_me_weishu_kernelsu_Natives_becomeManager(JNIEnv *env, jobject, jstring pkg)
|
||||
|
||||
extern "C"
|
||||
JNIEXPORT jint JNICALL
|
||||
Java_me_weishu_kernelsu_Natives_getVersion(JNIEnv *env, jobject) {
|
||||
Java_com_rifsxd_ksunext_Natives_getVersion(JNIEnv *env, jobject) {
|
||||
return get_version();
|
||||
}
|
||||
|
||||
extern "C"
|
||||
JNIEXPORT jintArray JNICALL
|
||||
Java_me_weishu_kernelsu_Natives_getAllowList(JNIEnv *env, jobject) {
|
||||
Java_com_rifsxd_ksunext_Natives_getAllowList(JNIEnv *env, jobject) {
|
||||
int uids[1024];
|
||||
int size = 0;
|
||||
bool result = get_allow_list(uids, &size);
|
||||
@@ -42,13 +42,13 @@ Java_me_weishu_kernelsu_Natives_getAllowList(JNIEnv *env, jobject) {
|
||||
|
||||
extern "C"
|
||||
JNIEXPORT jboolean JNICALL
|
||||
Java_me_weishu_kernelsu_Natives_isSafeMode(JNIEnv *env, jclass clazz) {
|
||||
Java_com_rifsxd_ksunext_Natives_isSafeMode(JNIEnv *env, jclass clazz) {
|
||||
return is_safe_mode();
|
||||
}
|
||||
|
||||
extern "C"
|
||||
JNIEXPORT jboolean JNICALL
|
||||
Java_me_weishu_kernelsu_Natives_isLkmMode(JNIEnv *env, jclass clazz) {
|
||||
Java_com_rifsxd_ksunext_Natives_isLkmMode(JNIEnv *env, jclass clazz) {
|
||||
return is_lkm_mode();
|
||||
}
|
||||
|
||||
@@ -111,7 +111,7 @@ static void fillArrayWithList(JNIEnv *env, jobject list, int *data, int count) {
|
||||
|
||||
extern "C"
|
||||
JNIEXPORT jobject JNICALL
|
||||
Java_me_weishu_kernelsu_Natives_getAppProfile(JNIEnv *env, jobject, jstring pkg, jint uid) {
|
||||
Java_com_rifsxd_ksunext_Natives_getAppProfile(JNIEnv *env, jobject, jstring pkg, jint uid) {
|
||||
if (env->GetStringLength(pkg) > KSU_MAX_PACKAGE_NAME) {
|
||||
return nullptr;
|
||||
}
|
||||
@@ -129,7 +129,7 @@ Java_me_weishu_kernelsu_Natives_getAppProfile(JNIEnv *env, jobject, jstring pkg,
|
||||
|
||||
bool useDefaultProfile = !get_app_profile(key, &profile);
|
||||
|
||||
auto cls = env->FindClass("me/weishu/kernelsu/Natives$Profile");
|
||||
auto cls = env->FindClass("com/rifsxd/ksunext/Natives$Profile");
|
||||
auto constructor = env->GetMethodID(cls, "<init>", "()V");
|
||||
auto obj = env->NewObject(cls, constructor);
|
||||
auto keyField = env->GetFieldID(cls, "name", "Ljava/lang/String;");
|
||||
@@ -207,8 +207,8 @@ Java_me_weishu_kernelsu_Natives_getAppProfile(JNIEnv *env, jobject, jstring pkg,
|
||||
|
||||
extern "C"
|
||||
JNIEXPORT jboolean JNICALL
|
||||
Java_me_weishu_kernelsu_Natives_setAppProfile(JNIEnv *env, jobject clazz, jobject profile) {
|
||||
auto cls = env->FindClass("me/weishu/kernelsu/Natives$Profile");
|
||||
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");
|
||||
@@ -293,6 +293,6 @@ Java_me_weishu_kernelsu_Natives_setAppProfile(JNIEnv *env, jobject clazz, jobjec
|
||||
}
|
||||
extern "C"
|
||||
JNIEXPORT jboolean JNICALL
|
||||
Java_me_weishu_kernelsu_Natives_uidShouldUmount(JNIEnv *env, jobject thiz, jint uid) {
|
||||
Java_com_rifsxd_ksunext_Natives_uidShouldUmount(JNIEnv *env, jobject thiz, jint uid) {
|
||||
return uid_should_umount(uid);
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package me.weishu.kernelsu
|
||||
package com.rifsxd.ksunext
|
||||
|
||||
import android.app.Application
|
||||
import coil.Coil
|
||||
@@ -1,4 +1,4 @@
|
||||
package me.weishu.kernelsu
|
||||
package com.rifsxd.ksunext
|
||||
|
||||
import android.system.Os
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package me.weishu.kernelsu
|
||||
package com.rifsxd.ksunext
|
||||
|
||||
import android.os.Parcelable
|
||||
import androidx.annotation.Keep
|
||||
@@ -15,12 +15,14 @@ object Natives {
|
||||
// 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
|
||||
// 11071: Fix the issue of failing to set a custom SELinux type
|
||||
// 11998: Next experimental modifications.
|
||||
const val MINIMAL_SUPPORTED_KERNEL = 11998
|
||||
|
||||
// 11640: Support query working mode, LKM or GKI
|
||||
// 12016: Next now supports GKI -> LKM/KMI modes!
|
||||
// when MINIMAL_SUPPORTED_KERNEL > 11640, we can remove this constant.
|
||||
const val MINIMAL_SUPPORTED_KERNEL_LKM = 11648
|
||||
const val MINIMAL_SUPPORTED_KERNEL_LKM = 12016
|
||||
const val KERNEL_SU_DOMAIN = "u:r:su:s0"
|
||||
|
||||
const val ROOT_UID = 0
|
||||
@@ -1,4 +1,4 @@
|
||||
package me.weishu.kernelsu.profile
|
||||
package com.rifsxd.ksunext.profile
|
||||
|
||||
/**
|
||||
* @author weishu
|
||||
@@ -1,4 +1,4 @@
|
||||
package me.weishu.kernelsu.profile
|
||||
package com.rifsxd.ksunext.profile
|
||||
|
||||
/**
|
||||
* https://cs.android.com/android/platform/superproject/main/+/main:system/core/libcutils/include/private/android_filesystem_config.h
|
||||
@@ -1,4 +1,4 @@
|
||||
package me.weishu.kernelsu.ui;
|
||||
package com.rifsxd.ksunext.ui;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
@@ -17,7 +17,7 @@ import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import me.weishu.kernelsu.IKsuInterface;
|
||||
import com.rifsxd.ksunext.IKsuInterface;
|
||||
import rikka.parcelablelist.ParcelableListSlice;
|
||||
|
||||
/**
|
||||
@@ -1,14 +1,27 @@
|
||||
package me.weishu.kernelsu.ui
|
||||
package com.rifsxd.ksunext.ui
|
||||
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
import androidx.compose.animation.AnimatedContentTransitionScope
|
||||
import androidx.compose.animation.EnterTransition
|
||||
import androidx.compose.animation.ExitTransition
|
||||
import androidx.compose.animation.core.tween
|
||||
import androidx.compose.animation.fadeIn
|
||||
import androidx.compose.animation.fadeOut
|
||||
import androidx.compose.foundation.layout.WindowInsets
|
||||
import androidx.compose.foundation.layout.WindowInsetsSides
|
||||
import androidx.compose.foundation.layout.displayCutout
|
||||
import androidx.compose.foundation.layout.only
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.systemBars
|
||||
import androidx.compose.foundation.layout.union
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.NavigationBar
|
||||
import androidx.compose.material3.NavigationBarItem
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.SnackbarHost
|
||||
import androidx.compose.material3.SnackbarHostState
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
@@ -18,39 +31,54 @@ import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.navigation.NavBackStackEntry
|
||||
import androidx.navigation.NavHostController
|
||||
import androidx.navigation.compose.rememberNavController
|
||||
import com.ramcosta.composedestinations.DestinationsNavHost
|
||||
import com.ramcosta.composedestinations.navigation.popBackStack
|
||||
import com.ramcosta.composedestinations.animations.NavHostAnimatedDestinationStyle
|
||||
import com.ramcosta.composedestinations.generated.NavGraphs
|
||||
import com.ramcosta.composedestinations.utils.isRouteOnBackStackAsState
|
||||
import me.weishu.kernelsu.Natives
|
||||
import me.weishu.kernelsu.ksuApp
|
||||
import me.weishu.kernelsu.ui.screen.BottomBarDestination
|
||||
import me.weishu.kernelsu.ui.screen.NavGraphs
|
||||
import me.weishu.kernelsu.ui.theme.KernelSUTheme
|
||||
import me.weishu.kernelsu.ui.util.LocalSnackbarHost
|
||||
import me.weishu.kernelsu.ui.util.rootAvailable
|
||||
import 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.LocalSnackbarHost
|
||||
import com.rifsxd.ksunext.ui.util.rootAvailable
|
||||
|
||||
class MainActivity : ComponentActivity() {
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
|
||||
// Enable edge to edge
|
||||
enableEdgeToEdge()
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||
window.isNavigationBarContrastEnforced = false
|
||||
}
|
||||
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
setContent {
|
||||
KernelSUTheme {
|
||||
val navController = rememberNavController()
|
||||
val snackbarHostState = remember { SnackbarHostState() }
|
||||
val snackBarHostState = remember { SnackbarHostState() }
|
||||
Scaffold(
|
||||
bottomBar = { BottomBar(navController) },
|
||||
snackbarHost = { SnackbarHost(snackbarHostState) }
|
||||
contentWindowInsets = WindowInsets(0, 0, 0, 0)
|
||||
) { innerPadding ->
|
||||
CompositionLocalProvider(
|
||||
LocalSnackbarHost provides snackbarHostState,
|
||||
LocalSnackbarHost provides snackBarHostState,
|
||||
) {
|
||||
DestinationsNavHost(
|
||||
modifier = Modifier.padding(innerPadding),
|
||||
navGraph = NavGraphs.root,
|
||||
navController = navController
|
||||
navController = navController,
|
||||
defaultTransitions = object : NavHostAnimatedDestinationStyle() {
|
||||
override val enterTransition: AnimatedContentTransitionScope<NavBackStackEntry>.() -> EnterTransition
|
||||
get() = { fadeIn(animationSpec = tween(340)) }
|
||||
override val exitTransition: AnimatedContentTransitionScope<NavBackStackEntry>.() -> ExitTransition
|
||||
get() = { fadeOut(animationSpec = tween(340)) }
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -61,9 +89,15 @@ class MainActivity : ComponentActivity() {
|
||||
|
||||
@Composable
|
||||
private fun BottomBar(navController: NavHostController) {
|
||||
val navigator = navController.rememberDestinationsNavigator()
|
||||
val isManager = Natives.becomeManager(ksuApp.packageName)
|
||||
val fullFeatured = isManager && !Natives.requireNewKernel() && rootAvailable()
|
||||
NavigationBar(tonalElevation = 8.dp) {
|
||||
NavigationBar(
|
||||
tonalElevation = 8.dp,
|
||||
windowInsets = WindowInsets.systemBars.union(WindowInsets.displayCutout).only(
|
||||
WindowInsetsSides.Horizontal + WindowInsetsSides.Bottom
|
||||
)
|
||||
) {
|
||||
BottomBarDestination.entries.forEach { destination ->
|
||||
if (!fullFeatured && destination.rootRequired) return@forEach
|
||||
val isCurrentDestOnBackStack by navController.isRouteOnBackStackAsState(destination.direction)
|
||||
@@ -71,11 +105,10 @@ private fun BottomBar(navController: NavHostController) {
|
||||
selected = isCurrentDestOnBackStack,
|
||||
onClick = {
|
||||
if (isCurrentDestOnBackStack) {
|
||||
navController.popBackStack(destination.direction, false)
|
||||
navigator.popBackStack(destination.direction, false)
|
||||
}
|
||||
|
||||
navController.navigate(destination.direction.route) {
|
||||
popUpTo(NavGraphs.root.route) {
|
||||
navigator.navigate(destination.direction) {
|
||||
popUpTo(NavGraphs.root) {
|
||||
saveState = true
|
||||
}
|
||||
launchSingleTop = true
|
||||
@@ -0,0 +1,124 @@
|
||||
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 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.drawable.ic_launcher_foreground),
|
||||
contentDescription = "icon",
|
||||
modifier = Modifier.scale(1.2f)
|
||||
)
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.width(12.dp))
|
||||
|
||||
Column {
|
||||
|
||||
Text(
|
||||
stringResource(id = R.string.app_name),
|
||||
style = MaterialTheme.typography.titleSmall,
|
||||
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,
|
||||
"<b><a href=\"https://github.com/rifsxd/KernelSU\">GitHub</a></b>"
|
||||
),
|
||||
linkStyles = TextLinkStyles(
|
||||
style = SpanStyle(
|
||||
color = MaterialTheme.colorScheme.primary,
|
||||
textDecoration = TextDecoration.Underline
|
||||
),
|
||||
pressedStyle = SpanStyle(
|
||||
color = MaterialTheme.colorScheme.primary,
|
||||
background = MaterialTheme.colorScheme.secondaryContainer,
|
||||
textDecoration = TextDecoration.Underline
|
||||
)
|
||||
)
|
||||
)
|
||||
Text(
|
||||
text = annotatedString,
|
||||
style = TextStyle(
|
||||
fontSize = 14.sp
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package me.weishu.kernelsu.ui.component
|
||||
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
|
||||
@@ -96,8 +97,8 @@ interface ConfirmDialogHandle : DialogHandle {
|
||||
}
|
||||
|
||||
private abstract class DialogHandleBase(
|
||||
protected val visible: MutableState<Boolean>,
|
||||
protected val coroutineScope: CoroutineScope
|
||||
val visible: MutableState<Boolean>,
|
||||
val coroutineScope: CoroutineScope
|
||||
) : DialogHandle {
|
||||
override val isShown: Boolean
|
||||
get() = visible.value
|
||||
@@ -432,7 +433,9 @@ private fun MarkdownContent(content: String) {
|
||||
TextView(context).apply {
|
||||
movementMethod = LinkMovementMethod.getInstance()
|
||||
setSpannableFactory(NoCopySpannableFactory.getInstance())
|
||||
breakStrategy = LineBreaker.BREAK_STRATEGY_SIMPLE
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||
breakStrategy = LineBreaker.BREAK_STRATEGY_SIMPLE
|
||||
}
|
||||
hyphenationFrequency = Layout.HYPHENATION_FREQUENCY_NONE
|
||||
layoutParams = ViewGroup.LayoutParams(
|
||||
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT
|
||||
@@ -1,4 +1,4 @@
|
||||
package me.weishu.kernelsu.ui.component
|
||||
package com.rifsxd.ksunext.ui.component
|
||||
|
||||
import androidx.compose.foundation.focusable
|
||||
import androidx.compose.foundation.layout.Box
|
||||
@@ -1,12 +1,16 @@
|
||||
package me.weishu.kernelsu.ui.component
|
||||
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
|
||||
@@ -19,6 +23,7 @@ import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.OutlinedTextField
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TopAppBar
|
||||
import androidx.compose.material3.TopAppBarScrollBehavior
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.DisposableEffect
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
@@ -48,6 +53,7 @@ fun SearchAppBar(
|
||||
onBackClick: (() -> Unit)? = null,
|
||||
onConfirm: (() -> Unit)? = null,
|
||||
dropdownContent: @Composable (() -> Unit)? = null,
|
||||
scrollBehavior: TopAppBarScrollBehavior? = null
|
||||
) {
|
||||
val keyboardController = LocalSoftwareKeyboardController.current
|
||||
val focusRequester = remember { FocusRequester() }
|
||||
@@ -132,10 +138,13 @@ fun SearchAppBar(
|
||||
dropdownContent()
|
||||
}
|
||||
|
||||
}
|
||||
},
|
||||
windowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal),
|
||||
scrollBehavior = scrollBehavior
|
||||
)
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Preview
|
||||
@Composable
|
||||
private fun SearchAppBarPreview() {
|
||||
@@ -1,14 +1,18 @@
|
||||
package me.weishu.kernelsu.ui.component
|
||||
package com.rifsxd.ksunext.ui.component
|
||||
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.LocalIndication
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.selection.toggleable
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.ListItem
|
||||
import androidx.compose.material3.RadioButton
|
||||
import androidx.compose.material3.Switch
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.semantics.Role
|
||||
|
||||
@Composable
|
||||
fun SwitchItem(
|
||||
@@ -19,10 +23,18 @@ fun SwitchItem(
|
||||
enabled: Boolean = true,
|
||||
onCheckedChange: (Boolean) -> Unit
|
||||
) {
|
||||
val interactionSource = remember { MutableInteractionSource() }
|
||||
|
||||
ListItem(
|
||||
modifier = Modifier.clickable {
|
||||
onCheckedChange.invoke(!checked)
|
||||
},
|
||||
modifier = Modifier
|
||||
.toggleable(
|
||||
value = checked,
|
||||
interactionSource = interactionSource,
|
||||
role = Role.Switch,
|
||||
enabled = enabled,
|
||||
indication = LocalIndication.current,
|
||||
onValueChange = onCheckedChange
|
||||
),
|
||||
headlineContent = {
|
||||
Text(title)
|
||||
},
|
||||
@@ -30,7 +42,12 @@ fun SwitchItem(
|
||||
{ Icon(icon, title) }
|
||||
},
|
||||
trailingContent = {
|
||||
Switch(checked = checked, enabled = enabled, onCheckedChange = onCheckedChange)
|
||||
Switch(
|
||||
checked = checked,
|
||||
enabled = enabled,
|
||||
onCheckedChange = onCheckedChange,
|
||||
interactionSource = interactionSource
|
||||
)
|
||||
},
|
||||
supportingContent = {
|
||||
if (summary != null) {
|
||||
@@ -52,6 +69,6 @@ fun RadioItem(
|
||||
},
|
||||
leadingContent = {
|
||||
RadioButton(selected = selected, onClick = onClick)
|
||||
},
|
||||
}
|
||||
)
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package me.weishu.kernelsu.ui.component.profile
|
||||
package com.rifsxd.ksunext.ui.component.profile
|
||||
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.material3.OutlinedTextField
|
||||
@@ -11,9 +11,9 @@ import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import me.weishu.kernelsu.Natives
|
||||
import me.weishu.kernelsu.R
|
||||
import me.weishu.kernelsu.ui.component.SwitchItem
|
||||
import com.rifsxd.ksunext.Natives
|
||||
import com.rifsxd.ksunext.R
|
||||
import com.rifsxd.ksunext.ui.component.SwitchItem
|
||||
|
||||
@Composable
|
||||
fun AppProfileConfig(
|
||||
@@ -1,11 +1,10 @@
|
||||
@file:OptIn(ExperimentalMaterial3Api::class)
|
||||
|
||||
package me.weishu.kernelsu.ui.component.profile
|
||||
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
|
||||
@@ -20,12 +19,14 @@ import androidx.compose.material3.ExposedDropdownMenuBox
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.ListItem
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.MenuAnchorType
|
||||
import androidx.compose.material3.OutlinedCard
|
||||
import androidx.compose.material3.OutlinedTextField
|
||||
import androidx.compose.material3.OutlinedTextFieldDefaults
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableIntStateOf
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
@@ -48,12 +49,12 @@ 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 me.weishu.kernelsu.Natives
|
||||
import me.weishu.kernelsu.R
|
||||
import me.weishu.kernelsu.profile.Capabilities
|
||||
import me.weishu.kernelsu.profile.Groups
|
||||
import me.weishu.kernelsu.ui.component.rememberCustomDialog
|
||||
import me.weishu.kernelsu.ui.util.isSepolicyValid
|
||||
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
|
||||
@@ -86,7 +87,7 @@ fun RootProfileConfig(
|
||||
) {
|
||||
OutlinedTextField(
|
||||
modifier = Modifier
|
||||
.menuAnchor()
|
||||
.menuAnchor(MenuAnchorType.PrimaryNotEditable)
|
||||
.fillMaxWidth(),
|
||||
readOnly = true,
|
||||
label = { Text(stringResource(R.string.profile_namespace)) },
|
||||
@@ -184,7 +185,7 @@ fun RootProfileConfig(
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalLayoutApi::class)
|
||||
@OptIn(ExperimentalLayoutApi::class, ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun GroupsPanel(selected: List<Groups>, closeSelection: (selection: Set<Groups>) -> Unit) {
|
||||
val selectGroupsDialog = rememberCustomDialog { dismiss: () -> Unit ->
|
||||
@@ -234,14 +235,20 @@ fun GroupsPanel(selected: List<Groups>, closeSelection: (selection: Set<Groups>)
|
||||
)
|
||||
}
|
||||
|
||||
OutlinedCard(modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(16.dp)
|
||||
.clickable {
|
||||
selectGroupsDialog.show()
|
||||
}) {
|
||||
OutlinedCard(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(16.dp)
|
||||
) {
|
||||
|
||||
Column(modifier = Modifier.padding(16.dp)) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.clickable {
|
||||
selectGroupsDialog.show()
|
||||
}
|
||||
.padding(16.dp)
|
||||
) {
|
||||
Text(stringResource(R.string.profile_groups))
|
||||
FlowRow {
|
||||
selected.forEach { group ->
|
||||
@@ -256,7 +263,7 @@ fun GroupsPanel(selected: List<Groups>, closeSelection: (selection: Set<Groups>)
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalLayoutApi::class)
|
||||
@OptIn(ExperimentalLayoutApi::class, ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun CapsPanel(
|
||||
selected: Collection<Capabilities>,
|
||||
@@ -299,14 +306,20 @@ fun CapsPanel(
|
||||
)
|
||||
}
|
||||
|
||||
OutlinedCard(modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(16.dp)
|
||||
.clickable {
|
||||
selectCapabilitiesDialog.show()
|
||||
}) {
|
||||
OutlinedCard(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(16.dp)
|
||||
) {
|
||||
|
||||
Column(modifier = Modifier.padding(16.dp)) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.clickable {
|
||||
selectCapabilitiesDialog.show()
|
||||
}
|
||||
.padding(16.dp)
|
||||
) {
|
||||
Text(stringResource(R.string.profile_capabilities))
|
||||
FlowRow {
|
||||
selected.forEach { group ->
|
||||
@@ -329,10 +342,10 @@ private fun UidPanel(uid: Int, label: String, onUidChange: (Int) -> Unit) {
|
||||
mutableStateOf(false)
|
||||
}
|
||||
var lastValidUid by remember {
|
||||
mutableStateOf(uid)
|
||||
mutableIntStateOf(uid)
|
||||
}
|
||||
|
||||
val keyboardController = LocalSoftwareKeyboardController.current
|
||||
|
||||
OutlinedTextField(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
label = { Text(label) },
|
||||
@@ -365,6 +378,7 @@ private fun UidPanel(uid: Int, label: String, onUidChange: (Int) -> Unit) {
|
||||
})
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
private fun SELinuxPanel(
|
||||
profile: Natives.Profile,
|
||||
@@ -452,7 +466,7 @@ private fun SELinuxPanel(
|
||||
),
|
||||
label = { Text(text = stringResource(R.string.profile_selinux_context)) },
|
||||
value = profile.context,
|
||||
onValueChange = { },
|
||||
onValueChange = { }
|
||||
)
|
||||
})
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package me.weishu.kernelsu.ui.component.profile
|
||||
package com.rifsxd.ksunext.ui.component.profile
|
||||
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.material.icons.Icons
|
||||
@@ -12,6 +12,7 @@ import androidx.compose.material3.ExposedDropdownMenuBox
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.ListItem
|
||||
import androidx.compose.material3.MenuAnchorType
|
||||
import androidx.compose.material3.OutlinedTextField
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
@@ -22,11 +23,11 @@ import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import me.weishu.kernelsu.Natives
|
||||
import me.weishu.kernelsu.R
|
||||
import me.weishu.kernelsu.ui.util.listAppProfileTemplates
|
||||
import me.weishu.kernelsu.ui.util.setSepolicy
|
||||
import me.weishu.kernelsu.ui.viewmodel.getTemplateInfoById
|
||||
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
|
||||
@@ -54,7 +55,7 @@ fun TemplateConfig(
|
||||
) {
|
||||
OutlinedTextField(
|
||||
modifier = Modifier
|
||||
.menuAnchor()
|
||||
.menuAnchor(MenuAnchorType.PrimaryNotEditable)
|
||||
.fillMaxWidth(),
|
||||
readOnly = true,
|
||||
label = { Text(stringResource(R.string.profile_template)) },
|
||||
@@ -1,4 +1,4 @@
|
||||
package me.weishu.kernelsu.ui.screen
|
||||
package com.rifsxd.ksunext.ui.screen
|
||||
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.compose.animation.Crossfade
|
||||
@@ -7,10 +7,14 @@ import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.BoxWithConstraints
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.WindowInsets
|
||||
import androidx.compose.foundation.layout.WindowInsetsSides
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.only
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.safeDrawing
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
@@ -28,8 +32,11 @@ import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.ListItem
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.SnackbarHost
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TopAppBar
|
||||
import androidx.compose.material3.TopAppBarDefaults
|
||||
import androidx.compose.material3.TopAppBarScrollBehavior
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
@@ -39,6 +46,7 @@ import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.geometry.Offset
|
||||
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||
import androidx.compose.ui.input.pointer.pointerInput
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
@@ -50,42 +58,43 @@ import androidx.compose.ui.unit.dp
|
||||
import coil.compose.AsyncImage
|
||||
import coil.request.ImageRequest
|
||||
import com.ramcosta.composedestinations.annotation.Destination
|
||||
import com.ramcosta.composedestinations.annotation.RootGraph
|
||||
import com.ramcosta.composedestinations.generated.destinations.AppProfileTemplateScreenDestination
|
||||
import com.ramcosta.composedestinations.generated.destinations.TemplateEditorScreenDestination
|
||||
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
|
||||
import kotlinx.coroutines.launch
|
||||
import me.weishu.kernelsu.Natives
|
||||
import me.weishu.kernelsu.R
|
||||
import me.weishu.kernelsu.ui.component.SwitchItem
|
||||
import me.weishu.kernelsu.ui.component.profile.AppProfileConfig
|
||||
import me.weishu.kernelsu.ui.component.profile.RootProfileConfig
|
||||
import me.weishu.kernelsu.ui.component.profile.TemplateConfig
|
||||
import me.weishu.kernelsu.ui.screen.destinations.AppProfileTemplateScreenDestination
|
||||
import me.weishu.kernelsu.ui.screen.destinations.TemplateEditorScreenDestination
|
||||
import me.weishu.kernelsu.ui.util.LocalSnackbarHost
|
||||
import me.weishu.kernelsu.ui.util.forceStopApp
|
||||
import me.weishu.kernelsu.ui.util.getSepolicy
|
||||
import me.weishu.kernelsu.ui.util.launchApp
|
||||
import me.weishu.kernelsu.ui.util.restartApp
|
||||
import me.weishu.kernelsu.ui.util.setSepolicy
|
||||
import me.weishu.kernelsu.ui.viewmodel.SuperUserViewModel
|
||||
import me.weishu.kernelsu.ui.viewmodel.getTemplateInfoById
|
||||
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.
|
||||
*/
|
||||
@Destination
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Destination<RootGraph>
|
||||
@Composable
|
||||
fun AppProfileScreen(
|
||||
navigator: DestinationsNavigator,
|
||||
appInfo: SuperUserViewModel.AppInfo,
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
val snackbarHost = LocalSnackbarHost.current
|
||||
val snackBarHost = LocalSnackbarHost.current
|
||||
val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior()
|
||||
val scope = rememberCoroutineScope()
|
||||
val failToUpdateAppProfile =
|
||||
stringResource(R.string.failed_to_update_app_profile).format(appInfo.label)
|
||||
val failToUpdateSepolicy =
|
||||
stringResource(R.string.failed_to_update_sepolicy).format(appInfo.label)
|
||||
val failToUpdateAppProfile = stringResource(R.string.failed_to_update_app_profile).format(appInfo.label)
|
||||
val failToUpdateSepolicy = stringResource(R.string.failed_to_update_sepolicy).format(appInfo.label)
|
||||
|
||||
val packageName = appInfo.packageName
|
||||
val initialProfile = Natives.getAppProfile(packageName, appInfo.uid)
|
||||
@@ -97,18 +106,25 @@ fun AppProfileScreen(
|
||||
}
|
||||
|
||||
Scaffold(
|
||||
topBar = { TopBar { navigator.popBackStack() } },
|
||||
topBar = {
|
||||
TopBar(
|
||||
onBack = { navigator.popBackStack() },
|
||||
scrollBehavior = scrollBehavior
|
||||
)
|
||||
},
|
||||
snackbarHost = { SnackbarHost(hostState = snackBarHost) },
|
||||
contentWindowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal)
|
||||
) { paddingValues ->
|
||||
AppProfileInner(
|
||||
modifier = Modifier
|
||||
.padding(paddingValues)
|
||||
.nestedScroll(scrollBehavior.nestedScrollConnection)
|
||||
.verticalScroll(rememberScrollState()),
|
||||
packageName = appInfo.packageName,
|
||||
appLabel = appInfo.label,
|
||||
appIcon = {
|
||||
AsyncImage(
|
||||
model = ImageRequest.Builder(context).data(appInfo.packageInfo).crossfade(true)
|
||||
.build(),
|
||||
model = ImageRequest.Builder(context).data(appInfo.packageInfo).crossfade(true).build(),
|
||||
contentDescription = appInfo.label,
|
||||
modifier = Modifier
|
||||
.padding(4.dp)
|
||||
@@ -129,12 +145,12 @@ fun AppProfileScreen(
|
||||
scope.launch {
|
||||
if (it.allowSu && !it.rootUseDefault && it.rules.isNotEmpty()) {
|
||||
if (!setSepolicy(profile.name, it.rules)) {
|
||||
snackbarHost.showSnackbar(failToUpdateSepolicy)
|
||||
snackBarHost.showSnackbar(failToUpdateSepolicy)
|
||||
return@launch
|
||||
}
|
||||
}
|
||||
if (!Natives.setAppProfile(it)) {
|
||||
snackbarHost.showSnackbar(failToUpdateAppProfile.format(appInfo.uid))
|
||||
snackBarHost.showSnackbar(failToUpdateAppProfile.format(appInfo.uid))
|
||||
} else {
|
||||
profile = it
|
||||
}
|
||||
@@ -174,7 +190,9 @@ private fun AppProfileInner(
|
||||
)
|
||||
|
||||
Crossfade(targetState = isRootGranted, label = "") { current ->
|
||||
Column {
|
||||
Column(
|
||||
modifier = Modifier.padding(bottom = 6.dp + 48.dp + 6.dp /* SnackBar height */)
|
||||
) {
|
||||
if (current) {
|
||||
val initialMode = if (profile.rootUseDefault) {
|
||||
Mode.Default
|
||||
@@ -238,7 +256,10 @@ private enum class Mode(@StringRes private val res: Int) {
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
private fun TopBar(onBack: () -> Unit) {
|
||||
private fun TopBar(
|
||||
onBack: () -> Unit,
|
||||
scrollBehavior: TopAppBarScrollBehavior? = null
|
||||
) {
|
||||
TopAppBar(
|
||||
title = {
|
||||
Text(stringResource(R.string.profile))
|
||||
@@ -248,6 +269,8 @@ private fun TopBar(onBack: () -> Unit) {
|
||||
onClick = onBack
|
||||
) { Icon(Icons.AutoMirrored.Filled.ArrowBack, contentDescription = null) }
|
||||
},
|
||||
windowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal),
|
||||
scrollBehavior = scrollBehavior
|
||||
)
|
||||
}
|
||||
|
||||
@@ -303,7 +326,8 @@ private fun AppMenuBox(packageName: String, content: @Composable () -> Unit) {
|
||||
touchPoint = it
|
||||
expanded = true
|
||||
}
|
||||
}) {
|
||||
}
|
||||
) {
|
||||
|
||||
content()
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
package me.weishu.kernelsu.ui.screen
|
||||
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 me.weishu.kernelsu.R
|
||||
import me.weishu.kernelsu.ui.screen.destinations.HomeScreenDestination
|
||||
import me.weishu.kernelsu.ui.screen.destinations.SuperUserScreenDestination
|
||||
import me.weishu.kernelsu.ui.screen.destinations.ModuleScreenDestination
|
||||
import com.rifsxd.ksunext.R
|
||||
|
||||
enum class BottomBarDestination(
|
||||
val direction: DirectionDestinationSpec,
|
||||
@@ -19,6 +20,7 @@ enum class BottomBarDestination(
|
||||
val rootRequired: Boolean,
|
||||
) {
|
||||
Home(HomeScreenDestination, R.string.home, Icons.Filled.Home, Icons.Outlined.Home, false),
|
||||
SuperUser(SuperUserScreenDestination, R.string.superuser, Icons.Filled.Security, Icons.Outlined.Security, true),
|
||||
Module(ModuleScreenDestination, R.string.module, Icons.Filled.Apps, Icons.Outlined.Apps, true)
|
||||
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)
|
||||
}
|
||||
@@ -0,0 +1,143 @@
|
||||
package com.rifsxd.ksunext.ui.screen
|
||||
|
||||
import android.os.Environment
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
||||
import androidx.compose.material.icons.filled.Save
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.SnackbarHost
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TopAppBar
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.input.key.Key
|
||||
import androidx.compose.ui.input.key.key
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.font.FontFamily
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.ramcosta.composedestinations.annotation.Destination
|
||||
import com.ramcosta.composedestinations.annotation.RootGraph
|
||||
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import 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<RootGraph>
|
||||
fun ExecuteModuleActionScreen(navigator: DestinationsNavigator, moduleId: String) {
|
||||
var text by rememberSaveable { mutableStateOf("") }
|
||||
val logContent = rememberSaveable { StringBuilder() }
|
||||
val snackBarHost = LocalSnackbarHost.current
|
||||
val scope = rememberCoroutineScope()
|
||||
val scrollState = rememberScrollState()
|
||||
var actionResult: Boolean
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
if (text.isNotEmpty()) {
|
||||
return@LaunchedEffect
|
||||
}
|
||||
withContext(Dispatchers.IO) {
|
||||
runModuleAction(
|
||||
moduleId = moduleId,
|
||||
onStdout = {
|
||||
text += "$it\n"
|
||||
logContent.append(it).append("\n")
|
||||
},
|
||||
onStderr = {
|
||||
logContent.append(it).append("\n")
|
||||
}
|
||||
).let {
|
||||
actionResult = it
|
||||
}
|
||||
}
|
||||
if (actionResult) navigator.popBackStack()
|
||||
}
|
||||
|
||||
Scaffold(
|
||||
topBar = {
|
||||
TopBar(
|
||||
onBack = {
|
||||
navigator.popBackStack()
|
||||
},
|
||||
onSave = {
|
||||
scope.launch {
|
||||
val format = SimpleDateFormat("yyyy-MM-dd-HH-mm-ss", Locale.getDefault())
|
||||
val date = format.format(Date())
|
||||
val file = File(
|
||||
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS),
|
||||
"KernelSU_module_action_log_${date}.log"
|
||||
)
|
||||
file.writeText(logContent.toString())
|
||||
snackBarHost.showSnackbar("Log saved to ${file.absolutePath}")
|
||||
}
|
||||
}
|
||||
)
|
||||
},
|
||||
snackbarHost = { SnackbarHost(snackBarHost) }
|
||||
) { innerPadding ->
|
||||
KeyEventBlocker {
|
||||
it.key == Key.VolumeDown || it.key == Key.VolumeUp
|
||||
}
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxSize(1f)
|
||||
.padding(innerPadding)
|
||||
.verticalScroll(scrollState),
|
||||
) {
|
||||
LaunchedEffect(text) {
|
||||
scrollState.animateScrollTo(scrollState.maxValue)
|
||||
}
|
||||
Text(
|
||||
modifier = Modifier.padding(8.dp),
|
||||
text = text,
|
||||
fontSize = MaterialTheme.typography.bodySmall.fontSize,
|
||||
fontFamily = FontFamily.Monospace,
|
||||
lineHeight = MaterialTheme.typography.bodySmall.lineHeight,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
private fun TopBar(onBack: () -> Unit = {}, onSave: () -> Unit = {}) {
|
||||
TopAppBar(
|
||||
title = { Text(stringResource(R.string.action)) },
|
||||
navigationIcon = {
|
||||
IconButton(
|
||||
onClick = onBack
|
||||
) { Icon(Icons.AutoMirrored.Filled.ArrowBack, contentDescription = null) }
|
||||
},
|
||||
actions = {
|
||||
IconButton(onClick = onSave) {
|
||||
Icon(
|
||||
imageVector = Icons.Filled.Save,
|
||||
contentDescription = stringResource(id = R.string.save_log),
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
@@ -1,11 +1,15 @@
|
||||
package me.weishu.kernelsu.ui.screen
|
||||
package com.rifsxd.ksunext.ui.screen
|
||||
|
||||
import android.net.Uri
|
||||
import android.os.Environment
|
||||
import android.os.Parcelable
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.WindowInsets
|
||||
import androidx.compose.foundation.layout.WindowInsetsSides
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.only
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.safeDrawing
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.icons.Icons
|
||||
@@ -18,8 +22,12 @@ import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.SnackbarHost
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TopAppBar
|
||||
import androidx.compose.material3.TopAppBarDefaults
|
||||
import androidx.compose.material3.TopAppBarScrollBehavior
|
||||
import androidx.compose.material3.rememberTopAppBarState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
@@ -30,26 +38,28 @@ import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.input.key.Key
|
||||
import androidx.compose.ui.input.key.key
|
||||
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.font.FontFamily
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.ramcosta.composedestinations.annotation.Destination
|
||||
import com.ramcosta.composedestinations.annotation.RootGraph
|
||||
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
|
||||
import com.ramcosta.composedestinations.navigation.EmptyDestinationsNavigator
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import kotlinx.parcelize.Parcelize
|
||||
import me.weishu.kernelsu.R
|
||||
import me.weishu.kernelsu.ui.component.KeyEventBlocker
|
||||
import me.weishu.kernelsu.ui.util.LkmSelection
|
||||
import me.weishu.kernelsu.ui.util.LocalSnackbarHost
|
||||
import me.weishu.kernelsu.ui.util.flashModule
|
||||
import me.weishu.kernelsu.ui.util.installBoot
|
||||
import me.weishu.kernelsu.ui.util.reboot
|
||||
import me.weishu.kernelsu.ui.util.restoreBoot
|
||||
import me.weishu.kernelsu.ui.util.uninstallPermanently
|
||||
import com.rifsxd.ksunext.R
|
||||
import com.rifsxd.ksunext.ui.component.KeyEventBlocker
|
||||
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
|
||||
@@ -65,8 +75,9 @@ enum class FlashingStatus {
|
||||
* @author weishu
|
||||
* @date 2023/1/1.
|
||||
*/
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
@Destination
|
||||
@Destination<RootGraph>
|
||||
fun FlashScreen(navigator: DestinationsNavigator, flashIt: FlashIt) {
|
||||
|
||||
var text by rememberSaveable { mutableStateOf("") }
|
||||
@@ -76,6 +87,7 @@ fun FlashScreen(navigator: DestinationsNavigator, flashIt: FlashIt) {
|
||||
val snackBarHost = LocalSnackbarHost.current
|
||||
val scope = rememberCoroutineScope()
|
||||
val scrollState = rememberScrollState()
|
||||
val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState())
|
||||
var flashing by rememberSaveable {
|
||||
mutableStateOf(FlashingStatus.FLASHING)
|
||||
}
|
||||
@@ -121,7 +133,8 @@ fun FlashScreen(navigator: DestinationsNavigator, flashIt: FlashIt) {
|
||||
file.writeText(logContent.toString())
|
||||
snackBarHost.showSnackbar("Log saved to ${file.absolutePath}")
|
||||
}
|
||||
}
|
||||
},
|
||||
scrollBehavior = scrollBehavior
|
||||
)
|
||||
},
|
||||
floatingActionButton = {
|
||||
@@ -139,8 +152,9 @@ fun FlashScreen(navigator: DestinationsNavigator, flashIt: FlashIt) {
|
||||
text = { Text(text = reboot) },
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
},
|
||||
snackbarHost = { SnackbarHost(hostState = snackBarHost) },
|
||||
contentWindowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal)
|
||||
) { innerPadding ->
|
||||
KeyEventBlocker {
|
||||
it.key == Key.VolumeDown || it.key == Key.VolumeUp
|
||||
@@ -149,6 +163,7 @@ fun FlashScreen(navigator: DestinationsNavigator, flashIt: FlashIt) {
|
||||
modifier = Modifier
|
||||
.fillMaxSize(1f)
|
||||
.padding(innerPadding)
|
||||
.nestedScroll(scrollBehavior.nestedScrollConnection)
|
||||
.verticalScroll(scrollState),
|
||||
) {
|
||||
LaunchedEffect(text) {
|
||||
@@ -202,7 +217,12 @@ fun flashIt(
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
private fun TopBar(status: FlashingStatus, onBack: () -> Unit = {}, onSave: () -> Unit = {}) {
|
||||
private fun TopBar(
|
||||
status: FlashingStatus,
|
||||
onBack: () -> Unit = {},
|
||||
onSave: () -> Unit = {},
|
||||
scrollBehavior: TopAppBarScrollBehavior? = null
|
||||
) {
|
||||
TopAppBar(
|
||||
title = {
|
||||
Text(
|
||||
@@ -227,7 +247,9 @@ private fun TopBar(status: FlashingStatus, onBack: () -> Unit = {}, onSave: () -
|
||||
contentDescription = "Localized description"
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
windowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal),
|
||||
scrollBehavior = scrollBehavior
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
package me.weishu.kernelsu.ui.screen
|
||||
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 androidx.annotation.StringRes
|
||||
import androidx.compose.animation.*
|
||||
@@ -11,58 +13,68 @@ 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.filled.Archive
|
||||
import androidx.compose.material.icons.filled.Refresh
|
||||
import androidx.compose.material.icons.filled.Settings
|
||||
import androidx.compose.material.icons.outlined.Block
|
||||
import androidx.compose.material.icons.outlined.CheckCircle
|
||||
import androidx.compose.material.icons.outlined.Warning
|
||||
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.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalUriHandler
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.core.content.pm.PackageInfoCompat
|
||||
import com.ramcosta.composedestinations.annotation.Destination
|
||||
import com.ramcosta.composedestinations.annotation.RootNavGraph
|
||||
import com.ramcosta.composedestinations.annotation.RootGraph
|
||||
import com.ramcosta.composedestinations.generated.destinations.InstallScreenDestination
|
||||
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import me.weishu.kernelsu.*
|
||||
import me.weishu.kernelsu.R
|
||||
import me.weishu.kernelsu.ui.component.rememberConfirmDialog
|
||||
import me.weishu.kernelsu.ui.screen.destinations.InstallScreenDestination
|
||||
import me.weishu.kernelsu.ui.screen.destinations.SettingScreenDestination
|
||||
import me.weishu.kernelsu.ui.util.*
|
||||
import me.weishu.kernelsu.ui.util.module.LatestVersionInfo
|
||||
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 androidx.compose.ui.graphics.vector.ImageVector
|
||||
|
||||
@RootNavGraph(start = true)
|
||||
@Destination
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Destination<RootGraph>(start = true)
|
||||
@Composable
|
||||
fun HomeScreen(navigator: DestinationsNavigator) {
|
||||
val kernelVersion = getKernelVersion()
|
||||
val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState())
|
||||
|
||||
Scaffold(topBar = {
|
||||
TopBar(kernelVersion, onSettingsClick = {
|
||||
navigator.navigate(SettingScreenDestination)
|
||||
}, onInstallClick = {
|
||||
navigator.navigate(InstallScreenDestination)
|
||||
})
|
||||
}) { innerPadding ->
|
||||
Scaffold(
|
||||
topBar = {
|
||||
TopBar(
|
||||
kernelVersion,
|
||||
onInstallClick = {
|
||||
navigator.navigate(InstallScreenDestination)
|
||||
},
|
||||
scrollBehavior = scrollBehavior
|
||||
)
|
||||
},
|
||||
contentWindowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal)
|
||||
) { innerPadding ->
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.padding(innerPadding)
|
||||
.padding(horizontal = 16.dp)
|
||||
.verticalScroll(rememberScrollState()),
|
||||
.nestedScroll(scrollBehavior.nestedScrollConnection)
|
||||
.verticalScroll(rememberScrollState())
|
||||
.padding(horizontal = 16.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(16.dp)
|
||||
) {
|
||||
val isManager = Natives.becomeManager(ksuApp.packageName)
|
||||
SideEffect {
|
||||
if (isManager) install()
|
||||
if (isManager) {
|
||||
Handler(Looper.getMainLooper()).postDelayed({
|
||||
install()
|
||||
}, 2000)
|
||||
}
|
||||
}
|
||||
val ksuVersion = if (isManager) Natives.version else null
|
||||
val lkmMode = ksuVersion?.let {
|
||||
@@ -90,9 +102,9 @@ fun HomeScreen(navigator: DestinationsNavigator) {
|
||||
if (checkUpdate) {
|
||||
UpdateCard()
|
||||
}
|
||||
NextCard()
|
||||
InfoCard()
|
||||
DonateCard()
|
||||
LearnMoreCard()
|
||||
EXperimentalCard()
|
||||
Spacer(Modifier)
|
||||
}
|
||||
}
|
||||
@@ -103,12 +115,11 @@ fun UpdateCard() {
|
||||
val context = LocalContext.current
|
||||
val latestVersionInfo = LatestVersionInfo()
|
||||
val newVersion by produceState(initialValue = latestVersionInfo) {
|
||||
value = withContext(Dispatchers.IO){
|
||||
value = withContext(Dispatchers.IO) {
|
||||
checkNewVersion()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
val currentVersionCode = getManagerVersion(context).second
|
||||
val newVersionCode = newVersion.versionCode
|
||||
val newVersionUrl = newVersion.downloadUrl
|
||||
@@ -128,7 +139,9 @@ fun UpdateCard() {
|
||||
message = stringResource(id = R.string.new_version_available).format(newVersionCode),
|
||||
MaterialTheme.colorScheme.outlineVariant
|
||||
) {
|
||||
if (changelog.isNotEmpty()) {
|
||||
if (changelog.isEmpty()) {
|
||||
uriHandler.openUri(newVersionUrl)
|
||||
} else {
|
||||
updateDialog.showConfirm(
|
||||
title = title,
|
||||
content = changelog,
|
||||
@@ -154,52 +167,50 @@ fun RebootDropdownItem(@StringRes id: Int, reason: String = "") {
|
||||
private fun TopBar(
|
||||
kernelVersion: KernelVersion,
|
||||
onInstallClick: () -> Unit,
|
||||
onSettingsClick: () -> Unit
|
||||
scrollBehavior: TopAppBarScrollBehavior? = null
|
||||
) {
|
||||
TopAppBar(title = { Text(stringResource(R.string.app_name)) }, actions = {
|
||||
if (kernelVersion.isGKI()) {
|
||||
IconButton(onClick = onInstallClick) {
|
||||
Icon(
|
||||
imageVector = Icons.Filled.Archive,
|
||||
contentDescription = stringResource(id = R.string.install)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
var showDropdown by remember { mutableStateOf(false) }
|
||||
IconButton(onClick = {
|
||||
showDropdown = true
|
||||
}) {
|
||||
Icon(
|
||||
imageVector = Icons.Filled.Refresh,
|
||||
contentDescription = stringResource(id = R.string.reboot)
|
||||
)
|
||||
|
||||
DropdownMenu(expanded = showDropdown, onDismissRequest = {
|
||||
showDropdown = false
|
||||
}) {
|
||||
|
||||
RebootDropdownItem(id = R.string.reboot)
|
||||
|
||||
val pm =
|
||||
LocalContext.current.getSystemService(Context.POWER_SERVICE) as PowerManager?
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R && pm?.isRebootingUserspaceSupported == true) {
|
||||
RebootDropdownItem(id = R.string.reboot_userspace, reason = "userspace")
|
||||
TopAppBar(
|
||||
title = { Text(stringResource(R.string.app_name)) },
|
||||
actions = {
|
||||
if (kernelVersion.isGKI()) {
|
||||
IconButton(onClick = onInstallClick) {
|
||||
Icon(
|
||||
imageVector = Icons.Filled.Archive,
|
||||
contentDescription = stringResource(id = R.string.install)
|
||||
)
|
||||
}
|
||||
RebootDropdownItem(id = R.string.reboot_recovery, reason = "recovery")
|
||||
RebootDropdownItem(id = R.string.reboot_bootloader, reason = "bootloader")
|
||||
RebootDropdownItem(id = R.string.reboot_download, reason = "download")
|
||||
RebootDropdownItem(id = R.string.reboot_edl, reason = "edl")
|
||||
}
|
||||
}
|
||||
|
||||
IconButton(onClick = onSettingsClick) {
|
||||
Icon(
|
||||
imageVector = Icons.Filled.Settings,
|
||||
contentDescription = stringResource(id = R.string.settings)
|
||||
)
|
||||
}
|
||||
})
|
||||
var showDropdown by remember { mutableStateOf(false) }
|
||||
IconButton(onClick = {
|
||||
showDropdown = true
|
||||
}) {
|
||||
Icon(
|
||||
imageVector = Icons.Filled.Refresh,
|
||||
contentDescription = stringResource(id = R.string.reboot)
|
||||
)
|
||||
|
||||
DropdownMenu(expanded = showDropdown, onDismissRequest = {
|
||||
showDropdown = false
|
||||
}) {
|
||||
|
||||
RebootDropdownItem(id = R.string.reboot)
|
||||
|
||||
val pm = LocalContext.current.getSystemService(Context.POWER_SERVICE) as PowerManager?
|
||||
@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
|
||||
@@ -231,7 +242,7 @@ private fun StatusCard(
|
||||
}
|
||||
|
||||
val workingMode = when (lkmMode) {
|
||||
null -> ""
|
||||
null -> " <LTS>"
|
||||
true -> " <LKM>"
|
||||
else -> " <GKI>"
|
||||
}
|
||||
@@ -239,7 +250,7 @@ private fun StatusCard(
|
||||
val workingText =
|
||||
"${stringResource(id = R.string.home_working)}$workingMode$safeMode"
|
||||
|
||||
Icon(Icons.Outlined.CheckCircle, stringResource(R.string.home_working))
|
||||
Icon(Icons.Filled.LocalFireDepartment, stringResource(R.string.home_working))
|
||||
Column(Modifier.padding(start = 20.dp)) {
|
||||
Text(
|
||||
text = workingText,
|
||||
@@ -265,7 +276,7 @@ private fun StatusCard(
|
||||
}
|
||||
|
||||
kernelVersion.isGKI() -> {
|
||||
Icon(Icons.Outlined.Warning, stringResource(R.string.home_not_installed))
|
||||
Icon(Icons.Filled.AutoFixHigh, stringResource(R.string.home_not_installed))
|
||||
Column(Modifier.padding(start = 20.dp)) {
|
||||
Text(
|
||||
text = stringResource(R.string.home_not_installed),
|
||||
@@ -280,15 +291,15 @@ private fun StatusCard(
|
||||
}
|
||||
|
||||
else -> {
|
||||
Icon(Icons.Outlined.Block, stringResource(R.string.home_unsupported))
|
||||
Icon(Icons.Filled.Dangerous, stringResource(R.string.home_failure))
|
||||
Column(Modifier.padding(start = 20.dp)) {
|
||||
Text(
|
||||
text = stringResource(R.string.home_unsupported),
|
||||
text = stringResource(R.string.home_failure),
|
||||
style = MaterialTheme.typography.titleMedium
|
||||
)
|
||||
Spacer(Modifier.height(4.dp))
|
||||
Text(
|
||||
text = stringResource(R.string.home_unsupported_reason),
|
||||
text = stringResource(R.string.home_failure_reason),
|
||||
style = MaterialTheme.typography.bodyMedium
|
||||
)
|
||||
}
|
||||
@@ -320,61 +331,6 @@ fun WarningCard(
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun LearnMoreCard() {
|
||||
val uriHandler = LocalUriHandler.current
|
||||
val url = stringResource(R.string.home_learn_kernelsu_url)
|
||||
|
||||
ElevatedCard {
|
||||
|
||||
Row(modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.clickable {
|
||||
uriHandler.openUri(url)
|
||||
}
|
||||
.padding(24.dp), verticalAlignment = Alignment.CenterVertically) {
|
||||
Column {
|
||||
Text(
|
||||
text = stringResource(R.string.home_learn_kernelsu),
|
||||
style = MaterialTheme.typography.titleSmall
|
||||
)
|
||||
Spacer(Modifier.height(4.dp))
|
||||
Text(
|
||||
text = stringResource(R.string.home_click_to_learn_kernelsu),
|
||||
style = MaterialTheme.typography.bodyMedium
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun DonateCard() {
|
||||
val uriHandler = LocalUriHandler.current
|
||||
|
||||
ElevatedCard {
|
||||
|
||||
Row(modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.clickable {
|
||||
uriHandler.openUri("https://patreon.com/weishu")
|
||||
}
|
||||
.padding(24.dp), verticalAlignment = Alignment.CenterVertically) {
|
||||
Column {
|
||||
Text(
|
||||
text = stringResource(R.string.home_support_title),
|
||||
style = MaterialTheme.typography.titleSmall
|
||||
)
|
||||
Spacer(Modifier.height(4.dp))
|
||||
Text(
|
||||
text = stringResource(R.string.home_support_content),
|
||||
style = MaterialTheme.typography.bodyMedium
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun InfoCard() {
|
||||
val context = LocalContext.current
|
||||
@@ -389,33 +345,140 @@ private fun InfoCard() {
|
||||
val uname = Os.uname()
|
||||
|
||||
@Composable
|
||||
fun InfoCardItem(label: String, content: String) {
|
||||
contents.appendLine(label).appendLine(content).appendLine()
|
||||
Text(text = label, style = MaterialTheme.typography.bodyLarge)
|
||||
Text(text = content, style = MaterialTheme.typography.bodyMedium)
|
||||
fun InfoCardItem(label: String, content: String, icon: ImageVector? = null) {
|
||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||
if (icon != null) {
|
||||
Icon(
|
||||
imageVector = icon,
|
||||
contentDescription = null,
|
||||
modifier = Modifier
|
||||
.padding(end = 16.dp)
|
||||
)
|
||||
}
|
||||
Column {
|
||||
Text(
|
||||
text = label,
|
||||
style = MaterialTheme.typography.bodyLarge
|
||||
)
|
||||
Text(
|
||||
text = content,
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
modifier = Modifier.padding(top = 4.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
InfoCardItem(stringResource(R.string.home_kernel), uname.release)
|
||||
|
||||
InfoCardItem(stringResource(R.string.home_kernel),
|
||||
uname.release,
|
||||
icon = Icons.Filled.Memory,
|
||||
)
|
||||
|
||||
Spacer(Modifier.height(16.dp))
|
||||
InfoCardItem(
|
||||
stringResource(R.string.home_android),
|
||||
"${Build.VERSION.RELEASE} (${Build.VERSION.SDK_INT})",
|
||||
icon = Icons.Filled.Android,
|
||||
|
||||
)
|
||||
|
||||
Spacer(Modifier.height(16.dp))
|
||||
val managerVersion = getManagerVersion(context)
|
||||
InfoCardItem(
|
||||
stringResource(R.string.home_manager_version),
|
||||
"${managerVersion.first} (${managerVersion.second})"
|
||||
"${managerVersion.first}-next (${managerVersion.second})",
|
||||
icon = Icons.AutoMirrored.Filled.Article,
|
||||
)
|
||||
|
||||
Spacer(Modifier.height(16.dp))
|
||||
InfoCardItem(stringResource(R.string.home_fingerprint), Build.FINGERPRINT)
|
||||
|
||||
Spacer(Modifier.height(16.dp))
|
||||
InfoCardItem(stringResource(R.string.home_selinux_status), getSELinuxStatus())
|
||||
InfoCardItem(
|
||||
label = stringResource(R.string.home_selinux_status),
|
||||
content = getSELinuxStatus(),
|
||||
icon = Icons.Filled.Security,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun getManagerVersion(context: Context): Pair<String, Int> {
|
||||
val packageInfo = context.packageManager.getPackageInfo(context.packageName, 0)
|
||||
return Pair(packageInfo.versionName, packageInfo.versionCode)
|
||||
@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
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun getManagerVersion(context: Context): Pair<String, Long> {
|
||||
val packageInfo = context.packageManager.getPackageInfo(context.packageName, 0)!!
|
||||
val versionCode = PackageInfoCompat.getLongVersionCode(packageInfo)
|
||||
return Pair(packageInfo.versionName!!, versionCode)
|
||||
}
|
||||
|
||||
@Preview
|
||||
@@ -439,4 +502,4 @@ private fun WarningCardPreview() {
|
||||
MaterialTheme.colorScheme.outlineVariant,
|
||||
onClick = {})
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package me.weishu.kernelsu.ui.screen
|
||||
package com.rifsxd.ksunext.ui.screen
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Intent
|
||||
@@ -6,11 +6,19 @@ import android.net.Uri
|
||||
import androidx.activity.compose.rememberLauncherForActivityResult
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.LocalIndication
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.WindowInsets
|
||||
import androidx.compose.foundation.layout.WindowInsetsSides
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.only
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.safeDrawing
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.selection.toggleable
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
||||
import androidx.compose.material.icons.filled.FileUpload
|
||||
@@ -23,6 +31,9 @@ import androidx.compose.material3.RadioButton
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TopAppBar
|
||||
import androidx.compose.material3.TopAppBarDefaults
|
||||
import androidx.compose.material3.TopAppBarScrollBehavior
|
||||
import androidx.compose.material3.rememberTopAppBarState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
@@ -31,7 +42,9 @@ import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.semantics.Role
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.maxkeppeker.sheets.core.models.base.Header
|
||||
@@ -40,25 +53,27 @@ import com.maxkeppeler.sheets.list.ListDialog
|
||||
import com.maxkeppeler.sheets.list.models.ListOption
|
||||
import com.maxkeppeler.sheets.list.models.ListSelection
|
||||
import com.ramcosta.composedestinations.annotation.Destination
|
||||
import com.ramcosta.composedestinations.annotation.RootGraph
|
||||
import com.ramcosta.composedestinations.generated.destinations.FlashScreenDestination
|
||||
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
|
||||
import com.ramcosta.composedestinations.navigation.EmptyDestinationsNavigator
|
||||
import me.weishu.kernelsu.R
|
||||
import me.weishu.kernelsu.ui.component.DialogHandle
|
||||
import me.weishu.kernelsu.ui.component.rememberConfirmDialog
|
||||
import me.weishu.kernelsu.ui.component.rememberCustomDialog
|
||||
import me.weishu.kernelsu.ui.screen.destinations.FlashScreenDestination
|
||||
import me.weishu.kernelsu.ui.util.LkmSelection
|
||||
import me.weishu.kernelsu.ui.util.getCurrentKmi
|
||||
import me.weishu.kernelsu.ui.util.getSupportedKmis
|
||||
import me.weishu.kernelsu.ui.util.isAbDevice
|
||||
import me.weishu.kernelsu.ui.util.isInitBoot
|
||||
import me.weishu.kernelsu.ui.util.rootAvailable
|
||||
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.
|
||||
*/
|
||||
@Destination
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Destination<RootGraph>
|
||||
@Composable
|
||||
fun InstallScreen(navigator: DestinationsNavigator) {
|
||||
var installMethod by remember {
|
||||
@@ -113,12 +128,24 @@ fun InstallScreen(navigator: DestinationsNavigator) {
|
||||
})
|
||||
}
|
||||
|
||||
Scaffold(topBar = {
|
||||
TopBar(
|
||||
onBack = { navigator.popBackStack() }, onLkmUpload = onLkmUpload
|
||||
)
|
||||
}) {
|
||||
Column(modifier = Modifier.padding(it)) {
|
||||
val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState())
|
||||
|
||||
Scaffold(
|
||||
topBar = {
|
||||
TopBar(
|
||||
onBack = { navigator.popBackStack() },
|
||||
onLkmUpload = onLkmUpload,
|
||||
scrollBehavior = scrollBehavior
|
||||
)
|
||||
},
|
||||
contentWindowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal)
|
||||
) { innerPadding ->
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.padding(innerPadding)
|
||||
.nestedScroll(scrollBehavior.nestedScrollConnection)
|
||||
.verticalScroll(rememberScrollState())
|
||||
) {
|
||||
SelectInstallMethod { method ->
|
||||
installMethod = method
|
||||
}
|
||||
@@ -231,16 +258,31 @@ private fun SelectInstallMethod(onSelected: (InstallMethod) -> Unit = {}) {
|
||||
|
||||
Column {
|
||||
radioOptions.forEach { option ->
|
||||
Row(verticalAlignment = Alignment.CenterVertically,
|
||||
val interactionSource = remember { MutableInteractionSource() }
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.clickable {
|
||||
.toggleable(
|
||||
value = option.javaClass == selectedOption?.javaClass,
|
||||
onValueChange = {
|
||||
onClick(option)
|
||||
},
|
||||
role = Role.RadioButton,
|
||||
indication = LocalIndication.current,
|
||||
interactionSource = interactionSource
|
||||
)
|
||||
) {
|
||||
RadioButton(
|
||||
selected = option.javaClass == selectedOption?.javaClass,
|
||||
onClick = {
|
||||
onClick(option)
|
||||
}) {
|
||||
RadioButton(selected = option.javaClass == selectedOption?.javaClass, onClick = {
|
||||
onClick(option)
|
||||
})
|
||||
Column {
|
||||
},
|
||||
interactionSource = interactionSource
|
||||
)
|
||||
Column(
|
||||
modifier = Modifier.padding(vertical = 12.dp)
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(id = option.label),
|
||||
fontSize = MaterialTheme.typography.titleMedium.fontSize,
|
||||
@@ -292,20 +334,28 @@ fun rememberSelectKmiDialog(onSelected: (String?) -> Unit): DialogHandle {
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
private fun TopBar(onBack: () -> Unit = {}, onLkmUpload: () -> Unit = {}) {
|
||||
TopAppBar(title = { Text(stringResource(R.string.install)) }, navigationIcon = {
|
||||
IconButton(
|
||||
onClick = onBack
|
||||
) { Icon(Icons.AutoMirrored.Filled.ArrowBack, contentDescription = null) }
|
||||
}, actions = {
|
||||
IconButton(onClick = onLkmUpload) {
|
||||
Icon(Icons.Filled.FileUpload, contentDescription = null)
|
||||
}
|
||||
})
|
||||
private fun TopBar(
|
||||
onBack: () -> Unit = {},
|
||||
onLkmUpload: () -> Unit = {},
|
||||
scrollBehavior: TopAppBarScrollBehavior? = null
|
||||
) {
|
||||
TopAppBar(
|
||||
title = { Text(stringResource(R.string.install)) }, navigationIcon = {
|
||||
IconButton(
|
||||
onClick = onBack
|
||||
) { Icon(Icons.AutoMirrored.Filled.ArrowBack, contentDescription = null) }
|
||||
}, actions = {
|
||||
IconButton(onClick = onLkmUpload) {
|
||||
Icon(Icons.Filled.FileUpload, contentDescription = null)
|
||||
}
|
||||
},
|
||||
windowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal),
|
||||
scrollBehavior = scrollBehavior
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
@Preview
|
||||
fun SelectInstall_Preview() {
|
||||
fun SelectInstallPreview() {
|
||||
InstallScreen(EmptyDestinationsNavigator)
|
||||
}
|
||||
@@ -0,0 +1,754 @@
|
||||
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.foundation.LocalIndication
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.WindowInsets
|
||||
import androidx.compose.foundation.layout.WindowInsetsSides
|
||||
import androidx.compose.foundation.layout.defaultMinSize
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.only
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.safeDrawing
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.selection.toggleable
|
||||
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.ElevatedCard
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.ExtendedFloatingActionButton
|
||||
import androidx.compose.material3.FilledTonalButton
|
||||
import androidx.compose.material3.HorizontalDivider
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.SnackbarDuration
|
||||
import androidx.compose.material3.SnackbarHost
|
||||
import androidx.compose.material3.SnackbarHostState
|
||||
import androidx.compose.material3.SnackbarResult
|
||||
import androidx.compose.material3.Switch
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.material3.TopAppBar
|
||||
import androidx.compose.material3.TopAppBarDefaults
|
||||
import androidx.compose.material3.pulltorefresh.PullToRefreshBox
|
||||
import androidx.compose.material3.rememberTopAppBarState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.produceState
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.semantics.Role
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.text.style.TextDecoration
|
||||
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.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 com.rifsxd.ksunext.Natives
|
||||
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.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.viewmodel.ModuleViewModel
|
||||
import com.rifsxd.ksunext.ui.webui.WebUIActivity
|
||||
import okhttp3.OkHttpClient
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Destination<RootGraph>
|
||||
@Composable
|
||||
fun ModuleScreen(navigator: DestinationsNavigator) {
|
||||
val viewModel = viewModel<ModuleViewModel>()
|
||||
val context = LocalContext.current
|
||||
val snackBarHost = LocalSnackbarHost.current
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
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<Uri?>(null) }
|
||||
var showConfirmDialog by remember { mutableStateOf(false) }
|
||||
|
||||
Scaffold(
|
||||
topBar = {
|
||||
TopAppBar(
|
||||
scrollBehavior = scrollBehavior,
|
||||
title = { Text(stringResource(R.string.module)) },
|
||||
windowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal)
|
||||
)
|
||||
},
|
||||
floatingActionButton = {
|
||||
if (!hideInstallButton) {
|
||||
val moduleInstall = stringResource(id = R.string.module_install)
|
||||
val selectZipLauncher = rememberLauncherForActivityResult(
|
||||
contract = ActivityResultContracts.StartActivityForResult()
|
||||
) { result ->
|
||||
if (result.resultCode != RESULT_OK) {
|
||||
return@rememberLauncherForActivityResult
|
||||
}
|
||||
val data = result.data ?: return@rememberLauncherForActivityResult
|
||||
val uri = data.data ?: return@rememberLauncherForActivityResult
|
||||
|
||||
// save the selected Uri and trigger confirmation dialog
|
||||
zipUri = uri
|
||||
showConfirmDialog = true
|
||||
}
|
||||
|
||||
ExtendedFloatingActionButton(
|
||||
onClick = {
|
||||
// select the zip file to install
|
||||
val intent = Intent(Intent.ACTION_GET_CONTENT).apply {
|
||||
type = "application/zip"
|
||||
}
|
||||
selectZipLauncher.launch(intent)
|
||||
},
|
||||
icon = { Icon(Icons.Filled.Add, moduleInstall) },
|
||||
text = { Text(text = moduleInstall) },
|
||||
)
|
||||
}
|
||||
},
|
||||
contentWindowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal),
|
||||
snackbarHost = { SnackbarHost(hostState = snackBarHost) }
|
||||
) { innerPadding ->
|
||||
// confirmation dialog
|
||||
if (showConfirmDialog && zipUri != null) {
|
||||
// extract the module name from the zipUri
|
||||
val moduleName = zipUri?.lastPathSegment?.substringAfterLast('/') ?: "Unknown Module"
|
||||
|
||||
AlertDialog(
|
||||
onDismissRequest = { showConfirmDialog = false },
|
||||
confirmButton = {
|
||||
TextButton(onClick = {
|
||||
showConfirmDialog = false
|
||||
navigator.navigate(FlashScreenDestination(FlashIt.FlashModule(zipUri!!)))
|
||||
|
||||
viewModel.markNeedRefresh()
|
||||
|
||||
}) {
|
||||
Text(stringResource(R.string.confirm))
|
||||
}
|
||||
},
|
||||
dismissButton = {
|
||||
TextButton(onClick = { showConfirmDialog = false }) {
|
||||
Text(stringResource(android.R.string.cancel))
|
||||
}
|
||||
},
|
||||
title = { Text(stringResource(R.string.confirm_module_installation)) },
|
||||
text = {
|
||||
Text(
|
||||
stringResource(R.string.module_install_prompt_with_name, moduleName)
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
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.FlashModule(it)))
|
||||
},
|
||||
onClickModule = { id, name, hasWebUi ->
|
||||
if (hasWebUi) {
|
||||
context.startActivity(
|
||||
Intent(context, WebUIActivity::class.java)
|
||||
.setData(Uri.parse("kernelsu://webui/$id"))
|
||||
.putExtra("id", id)
|
||||
.putExtra("name", name)
|
||||
)
|
||||
}
|
||||
},
|
||||
context = context,
|
||||
snackBarHost = snackBarHost
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@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
|
||||
) {
|
||||
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 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 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 loadingDialog = rememberLoadingDialog()
|
||||
val confirmDialog = rememberConfirmDialog()
|
||||
|
||||
suspend fun onModuleUpdate(
|
||||
module: ModuleViewModel.ModuleInfo,
|
||||
changelogUrl: String,
|
||||
downloadUrl: String,
|
||||
fileName: String
|
||||
) {
|
||||
val changelogResult = loadingDialog.withLoading {
|
||||
withContext(Dispatchers.IO) {
|
||||
runCatching {
|
||||
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.id)
|
||||
}
|
||||
}
|
||||
|
||||
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.id)
|
||||
}
|
||||
}
|
||||
|
||||
if (success) {
|
||||
viewModel.fetchModuleList()
|
||||
}
|
||||
val message = if (success) {
|
||||
successRestore.format(module.name)
|
||||
} else {
|
||||
failedRestore.format(module.name)
|
||||
}
|
||||
}
|
||||
PullToRefreshBox(
|
||||
modifier = boxModifier,
|
||||
onRefresh = {
|
||||
viewModel.fetchModuleList()
|
||||
},
|
||||
isRefreshing = viewModel.isRefreshing
|
||||
) {
|
||||
LazyColumn(
|
||||
modifier = modifier,
|
||||
verticalArrangement = Arrangement.spacedBy(16.dp),
|
||||
contentPadding = remember {
|
||||
PaddingValues(
|
||||
start = 16.dp,
|
||||
top = 16.dp,
|
||||
end = 16.dp,
|
||||
bottom = 16.dp + 56.dp + 16.dp + 48.dp + 6.dp /* Scaffold Fab Spacing + Fab container height + SnackBar height */
|
||||
)
|
||||
},
|
||||
) {
|
||||
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 ->
|
||||
var isChecked by rememberSaveable(module) { mutableStateOf(module.enabled) }
|
||||
val scope = rememberCoroutineScope()
|
||||
val updatedModule by produceState(initialValue = Triple("", "", "")) {
|
||||
scope.launch(Dispatchers.IO) {
|
||||
value = viewModel.checkUpdate(module)
|
||||
}
|
||||
}
|
||||
|
||||
ModuleItem(
|
||||
navigator = navigator,
|
||||
module = module,
|
||||
isChecked = isChecked,
|
||||
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.id, !isChecked)
|
||||
}
|
||||
}
|
||||
if (success) {
|
||||
isChecked = it
|
||||
viewModel.fetchModuleList()
|
||||
|
||||
val result = snackBarHost.showSnackbar(
|
||||
message = rebootToApply,
|
||||
actionLabel = reboot,
|
||||
duration = SnackbarDuration.Long
|
||||
)
|
||||
if (result == SnackbarResult.ActionPerformed) {
|
||||
reboot()
|
||||
}
|
||||
} else {
|
||||
val message = if (isChecked) failedDisable else failedEnable
|
||||
snackBarHost.showSnackbar(message.format(module.name))
|
||||
}
|
||||
}
|
||||
},
|
||||
onUpdate = {
|
||||
scope.launch {
|
||||
onModuleUpdate(
|
||||
module,
|
||||
updatedModule.third,
|
||||
updatedModule.first,
|
||||
"${module.name}-${updatedModule.second}.zip"
|
||||
)
|
||||
}
|
||||
},
|
||||
onClick = {
|
||||
onClickModule(it.id, it.name, it.hasWebUi)
|
||||
}
|
||||
)
|
||||
|
||||
// fix last item shadow incomplete in LazyColumn
|
||||
Spacer(Modifier.height(1.dp))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DownloadListener(context, onInstallModule)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ModuleItem(
|
||||
navigator: DestinationsNavigator,
|
||||
module: ModuleViewModel.ModuleInfo,
|
||||
isChecked: Boolean,
|
||||
updateUrl: String,
|
||||
onUninstall: (ModuleViewModel.ModuleInfo) -> Unit,
|
||||
onRestore: (ModuleViewModel.ModuleInfo) -> Unit,
|
||||
onCheckChanged: (Boolean) -> Unit,
|
||||
onUpdate: (ModuleViewModel.ModuleInfo) -> Unit,
|
||||
onClick: (ModuleViewModel.ModuleInfo) -> Unit
|
||||
) {
|
||||
ElevatedCard(
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
val textDecoration = if (!module.remove) null else TextDecoration.LineThrough
|
||||
val interactionSource = remember { MutableInteractionSource() }
|
||||
val indication = LocalIndication.current
|
||||
val viewModel = viewModel<ModuleViewModel>()
|
||||
|
||||
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)
|
||||
|
||||
Column(
|
||||
modifier = Modifier.fillMaxWidth(0.8f)
|
||||
) {
|
||||
Text(
|
||||
text = module.name,
|
||||
fontSize = MaterialTheme.typography.titleMedium.fontSize,
|
||||
fontWeight = FontWeight.SemiBold,
|
||||
lineHeight = MaterialTheme.typography.bodySmall.lineHeight,
|
||||
fontFamily = MaterialTheme.typography.titleMedium.fontFamily,
|
||||
textDecoration = textDecoration,
|
||||
)
|
||||
|
||||
Text(
|
||||
text = "$moduleVersion: ${module.version}",
|
||||
fontSize = MaterialTheme.typography.bodySmall.fontSize,
|
||||
lineHeight = MaterialTheme.typography.bodySmall.lineHeight,
|
||||
fontFamily = MaterialTheme.typography.bodySmall.fontFamily,
|
||||
textDecoration = textDecoration
|
||||
)
|
||||
|
||||
Text(
|
||||
text = "$moduleAuthor: ${module.author}",
|
||||
fontSize = MaterialTheme.typography.bodySmall.fontSize,
|
||||
lineHeight = MaterialTheme.typography.bodySmall.lineHeight,
|
||||
fontFamily = MaterialTheme.typography.bodySmall.fontFamily,
|
||||
textDecoration = textDecoration
|
||||
)
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.weight(1f))
|
||||
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.End,
|
||||
) {
|
||||
Switch(
|
||||
enabled = !module.update,
|
||||
checked = isChecked,
|
||||
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,
|
||||
textDecoration = textDecoration
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
|
||||
HorizontalDivider(thickness = Dp.Hairline)
|
||||
|
||||
Spacer(modifier = Modifier.height(4.dp))
|
||||
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
if (module.hasActionScript) {
|
||||
FilledTonalButton(
|
||||
modifier = Modifier.defaultMinSize(52.dp, 32.dp),
|
||||
onClick = {
|
||||
navigator.navigate(ExecuteModuleActionScreenDestination(module.id))
|
||||
viewModel.markNeedRefresh()
|
||||
},
|
||||
contentPadding = ButtonDefaults.TextButtonContentPadding
|
||||
) {
|
||||
Icon(
|
||||
modifier = Modifier.size(20.dp),
|
||||
imageVector = Icons.Outlined.PlayArrow,
|
||||
contentDescription = null
|
||||
)
|
||||
if (!module.hasWebUi && updateUrl.isEmpty()) {
|
||||
Text(
|
||||
modifier = Modifier.padding(start = 7.dp),
|
||||
text = stringResource(R.string.action),
|
||||
fontFamily = MaterialTheme.typography.labelMedium.fontFamily,
|
||||
fontSize = MaterialTheme.typography.labelMedium.fontSize
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.weight(0.1f, true))
|
||||
}
|
||||
|
||||
if (module.hasWebUi) {
|
||||
FilledTonalButton(
|
||||
modifier = Modifier.defaultMinSize(52.dp, 32.dp),
|
||||
onClick = { onClick(module) },
|
||||
interactionSource = interactionSource,
|
||||
contentPadding = ButtonDefaults.TextButtonContentPadding
|
||||
) {
|
||||
Icon(
|
||||
modifier = Modifier.size(20.dp),
|
||||
imageVector = Icons.AutoMirrored.Outlined.Wysiwyg,
|
||||
contentDescription = null
|
||||
)
|
||||
if (!module.hasActionScript && updateUrl.isEmpty()) {
|
||||
Text(
|
||||
modifier = Modifier.padding(start = 7.dp),
|
||||
fontFamily = MaterialTheme.typography.labelMedium.fontFamily,
|
||||
fontSize = MaterialTheme.typography.labelMedium.fontSize,
|
||||
text = stringResource(R.string.open)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.weight(1f, true))
|
||||
|
||||
if (updateUrl.isNotEmpty()) {
|
||||
Button(
|
||||
modifier = Modifier.defaultMinSize(52.dp, 32.dp),
|
||||
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
|
||||
)
|
||||
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
|
||||
)
|
||||
Text(
|
||||
modifier = Modifier.padding(start = 7.dp),
|
||||
fontFamily = MaterialTheme.typography.labelMedium.fontFamily,
|
||||
fontSize = MaterialTheme.typography.labelMedium.fontSize,
|
||||
text = stringResource(R.string.uninstall)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@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
|
||||
)
|
||||
ModuleItem(EmptyDestinationsNavigator, module, true, "", {}, {}, {}, {}, {})
|
||||
}
|
||||
@@ -1,22 +1,23 @@
|
||||
package me.weishu.kernelsu.ui.screen
|
||||
package com.rifsxd.ksunext.ui.screen
|
||||
|
||||
import android.content.ContentResolver
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.database.Cursor
|
||||
import android.net.Uri
|
||||
import android.provider.OpenableColumns
|
||||
import android.util.Log
|
||||
import android.widget.Toast
|
||||
import androidx.activity.compose.rememberLauncherForActivityResult
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.WindowInsets
|
||||
import androidx.compose.foundation.layout.WindowInsetsSides
|
||||
import androidx.compose.foundation.layout.only
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.safeDrawing
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
||||
import androidx.compose.material.icons.automirrored.filled.Undo
|
||||
import androidx.compose.material.icons.filled.BugReport
|
||||
import androidx.compose.material.icons.filled.Compress
|
||||
@@ -29,15 +30,18 @@ import androidx.compose.material.icons.filled.RemoveModerator
|
||||
import androidx.compose.material.icons.filled.Save
|
||||
import androidx.compose.material.icons.filled.Share
|
||||
import androidx.compose.material.icons.filled.Update
|
||||
import androidx.compose.material3.BottomSheetScaffold
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.ListItem
|
||||
import androidx.compose.material3.ModalBottomSheet
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.SnackbarHost
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TopAppBar
|
||||
import androidx.compose.material3.TopAppBarDefaults
|
||||
import androidx.compose.material3.TopAppBarScrollBehavior
|
||||
import androidx.compose.material3.rememberTopAppBarState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
@@ -48,6 +52,7 @@ import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.LineHeightStyle
|
||||
@@ -62,26 +67,26 @@ import com.maxkeppeler.sheets.list.ListDialog
|
||||
import com.maxkeppeler.sheets.list.models.ListOption
|
||||
import com.maxkeppeler.sheets.list.models.ListSelection
|
||||
import com.ramcosta.composedestinations.annotation.Destination
|
||||
import com.ramcosta.composedestinations.annotation.RootGraph
|
||||
import com.ramcosta.composedestinations.generated.destinations.AppProfileTemplateScreenDestination
|
||||
import com.ramcosta.composedestinations.generated.destinations.FlashScreenDestination
|
||||
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
|
||||
import com.ramcosta.composedestinations.navigation.EmptyDestinationsNavigator
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import me.weishu.kernelsu.BuildConfig
|
||||
import me.weishu.kernelsu.Natives
|
||||
import me.weishu.kernelsu.R
|
||||
import me.weishu.kernelsu.ui.component.AboutDialog
|
||||
import me.weishu.kernelsu.ui.component.ConfirmResult
|
||||
import me.weishu.kernelsu.ui.component.DialogHandle
|
||||
import me.weishu.kernelsu.ui.component.SwitchItem
|
||||
import me.weishu.kernelsu.ui.component.rememberConfirmDialog
|
||||
import me.weishu.kernelsu.ui.component.rememberCustomDialog
|
||||
import me.weishu.kernelsu.ui.component.rememberLoadingDialog
|
||||
import me.weishu.kernelsu.ui.screen.destinations.AppProfileTemplateScreenDestination
|
||||
import me.weishu.kernelsu.ui.screen.destinations.FlashScreenDestination
|
||||
import me.weishu.kernelsu.ui.util.getBugreportFile
|
||||
import me.weishu.kernelsu.ui.util.getFileNameFromUri
|
||||
import me.weishu.kernelsu.ui.util.shrinkModules
|
||||
import com.rifsxd.ksunext.BuildConfig
|
||||
import com.rifsxd.ksunext.Natives
|
||||
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 java.time.LocalDateTime
|
||||
import java.time.format.DateTimeFormatter
|
||||
|
||||
@@ -90,31 +95,52 @@ import java.time.format.DateTimeFormatter
|
||||
* @date 2023/1/1.
|
||||
*/
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Destination
|
||||
@Destination<RootGraph>
|
||||
@Composable
|
||||
fun SettingScreen(navigator: DestinationsNavigator) {
|
||||
val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState())
|
||||
val snackBarHost = LocalSnackbarHost.current
|
||||
|
||||
Scaffold(
|
||||
topBar = {
|
||||
TopBar(onBack = {
|
||||
navigator.popBackStack()
|
||||
})
|
||||
}
|
||||
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)
|
||||
ListItem(
|
||||
leadingContent = { Icon(Icons.Filled.Fence, profileTemplate) },
|
||||
@@ -184,46 +210,25 @@ fun SettingScreen(navigator: DestinationsNavigator) {
|
||||
showBottomsheet = true
|
||||
}
|
||||
)
|
||||
if (showBottomsheet){
|
||||
if (showBottomsheet) {
|
||||
ModalBottomSheet(
|
||||
onDismissRequest = { showBottomsheet = false },
|
||||
content = {
|
||||
Row(modifier = Modifier.padding(10.dp)
|
||||
.align(Alignment.CenterHorizontally)
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.padding(10.dp)
|
||||
.align(Alignment.CenterHorizontally)
|
||||
|
||||
) {
|
||||
Box{
|
||||
Box {
|
||||
Column(
|
||||
modifier = Modifier.padding(16.dp)
|
||||
modifier = Modifier
|
||||
.padding(16.dp)
|
||||
.clickable {
|
||||
scope.launch {
|
||||
val bugreport = loadingDialog.withLoading {
|
||||
withContext(Dispatchers.IO) {
|
||||
getBugreportFile(context)
|
||||
}
|
||||
}
|
||||
|
||||
val uri: Uri =
|
||||
FileProvider.getUriForFile(
|
||||
context,
|
||||
"${BuildConfig.APPLICATION_ID}.fileprovider",
|
||||
bugreport
|
||||
)
|
||||
val filename = getFileNameFromUri(context , uri)
|
||||
val savefile = Intent(Intent.ACTION_CREATE_DOCUMENT).apply {
|
||||
addCategory(Intent.CATEGORY_OPENABLE)
|
||||
type = "application/zip"
|
||||
putExtra(Intent.EXTRA_STREAM, uri)
|
||||
putExtra(Intent.EXTRA_TITLE, filename)
|
||||
flags = Intent.FLAG_GRANT_READ_URI_PERMISSION
|
||||
}
|
||||
context.startActivity(
|
||||
Intent.createChooser(
|
||||
savefile,
|
||||
context.getString(R.string.save_log)
|
||||
)
|
||||
)
|
||||
}
|
||||
val formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd_HH_mm")
|
||||
val current = LocalDateTime.now().format(formatter)
|
||||
exportBugreportLauncher.launch("KernelSU_bugreport_${current}.tar.gz")
|
||||
showBottomsheet = false
|
||||
}
|
||||
) {
|
||||
Icon(
|
||||
@@ -243,11 +248,11 @@ fun SettingScreen(navigator: DestinationsNavigator) {
|
||||
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
Box{
|
||||
Box {
|
||||
Column(
|
||||
modifier = Modifier.padding(16.dp)
|
||||
modifier = Modifier
|
||||
.padding(16.dp)
|
||||
.clickable {
|
||||
scope.launch {
|
||||
val bugreport = loadingDialog.withLoading {
|
||||
@@ -263,10 +268,11 @@ fun SettingScreen(navigator: DestinationsNavigator) {
|
||||
bugreport
|
||||
)
|
||||
|
||||
val shareIntent = Intent(Intent.ACTION_SEND)
|
||||
shareIntent.putExtra(Intent.EXTRA_STREAM, uri)
|
||||
shareIntent.setDataAndType(uri, "application/zip")
|
||||
shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
||||
val shareIntent = Intent(Intent.ACTION_SEND).apply {
|
||||
putExtra(Intent.EXTRA_STREAM, uri)
|
||||
setDataAndType(uri, "application/gzip")
|
||||
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
||||
}
|
||||
|
||||
context.startActivity(
|
||||
Intent.createChooser(
|
||||
@@ -291,43 +297,15 @@ fun SettingScreen(navigator: DestinationsNavigator) {
|
||||
trim = LineHeightStyle.Trim.None
|
||||
)
|
||||
}
|
||||
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
}
|
||||
|
||||
val shrink = stringResource(id = R.string.shrink_sparse_image)
|
||||
val shrinkMessage = stringResource(id = R.string.shrink_sparse_image_message)
|
||||
ListItem(
|
||||
leadingContent = {
|
||||
Icon(
|
||||
Icons.Filled.Compress,
|
||||
shrink
|
||||
)
|
||||
},
|
||||
headlineContent = { Text(shrink) },
|
||||
modifier = Modifier.clickable {
|
||||
scope.launch {
|
||||
val result =
|
||||
shrinkDialog.awaitConfirm(title = shrink, content = shrinkMessage)
|
||||
if (result == ConfirmResult.Confirmed) {
|
||||
loadingDialog.withLoading {
|
||||
shrinkModules()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
val lkmMode =
|
||||
Natives.version >= Natives.MINIMAL_SUPPORTED_KERNEL_LKM && Natives.isLkmMode
|
||||
val lkmMode = Natives.version >= Natives.MINIMAL_SUPPORTED_KERNEL_LKM && Natives.isLkmMode
|
||||
if (lkmMode) {
|
||||
UninstallItem(navigator) {
|
||||
loadingDialog.withLoading(it)
|
||||
@@ -339,7 +317,7 @@ fun SettingScreen(navigator: DestinationsNavigator) {
|
||||
leadingContent = {
|
||||
Icon(
|
||||
Icons.Filled.ContactPage,
|
||||
stringResource(id = R.string.about)
|
||||
about
|
||||
)
|
||||
},
|
||||
headlineContent = { Text(about) },
|
||||
@@ -350,6 +328,7 @@ fun SettingScreen(navigator: DestinationsNavigator) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun UninstallItem(
|
||||
navigator: DestinationsNavigator,
|
||||
@@ -374,11 +353,9 @@ fun UninstallItem(
|
||||
UninstallType.PERMANENT -> navigator.navigate(
|
||||
FlashScreenDestination(FlashIt.FlashUninstall)
|
||||
)
|
||||
|
||||
UninstallType.RESTORE_STOCK_IMAGE -> navigator.navigate(
|
||||
FlashScreenDestination(FlashIt.FlashRestore)
|
||||
)
|
||||
|
||||
UninstallType.NONE -> Unit
|
||||
}
|
||||
}
|
||||
@@ -456,14 +433,13 @@ fun rememberUninstallDialog(onSelected: (UninstallType) -> Unit): DialogHandle {
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
private fun TopBar(onBack: () -> Unit = {}) {
|
||||
private fun TopBar(
|
||||
scrollBehavior: TopAppBarScrollBehavior? = null
|
||||
) {
|
||||
TopAppBar(
|
||||
title = { Text(stringResource(R.string.settings)) },
|
||||
navigationIcon = {
|
||||
IconButton(
|
||||
onClick = onBack
|
||||
) { Icon(Icons.AutoMirrored.Filled.ArrowBack, contentDescription = null) }
|
||||
},
|
||||
windowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal),
|
||||
scrollBehavior = scrollBehavior
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,22 +1,21 @@
|
||||
package me.weishu.kernelsu.ui.screen
|
||||
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.material.pullrefresh.PullRefreshIndicator
|
||||
import androidx.compose.material.pullrefresh.pullRefresh
|
||||
import androidx.compose.material.pullrefresh.rememberPullRefreshState
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.material3.pulltorefresh.PullToRefreshBox
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
@@ -26,27 +25,37 @@ import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import coil.compose.AsyncImage
|
||||
import coil.request.ImageRequest
|
||||
import com.ramcosta.composedestinations.annotation.Destination
|
||||
import com.ramcosta.composedestinations.annotation.RootGraph
|
||||
import com.ramcosta.composedestinations.generated.destinations.AppProfileScreenDestination
|
||||
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
|
||||
import kotlinx.coroutines.launch
|
||||
import me.weishu.kernelsu.Natives
|
||||
import me.weishu.kernelsu.R
|
||||
import me.weishu.kernelsu.ui.component.SearchAppBar
|
||||
import me.weishu.kernelsu.ui.screen.destinations.AppProfileScreenDestination
|
||||
import me.weishu.kernelsu.ui.viewmodel.SuperUserViewModel
|
||||
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)
|
||||
@Destination
|
||||
@OptIn(ExperimentalMaterialApi::class, ExperimentalMaterial3Api::class)
|
||||
@Destination<RootGraph>
|
||||
@Composable
|
||||
fun SuperUserScreen(navigator: DestinationsNavigator) {
|
||||
val viewModel = viewModel<SuperUserViewModel>()
|
||||
val scope = rememberCoroutineScope()
|
||||
val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState())
|
||||
val listState = rememberLazyListState()
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
LaunchedEffect(key1 = navigator) {
|
||||
viewModel.search = ""
|
||||
if (viewModel.appList.isEmpty()) {
|
||||
viewModel.fetchAppList()
|
||||
}
|
||||
}
|
||||
|
||||
LaunchedEffect(viewModel.search) {
|
||||
if (viewModel.search.isEmpty()) {
|
||||
listState.scrollToItem(0)
|
||||
}
|
||||
}
|
||||
|
||||
Scaffold(
|
||||
topBar = {
|
||||
SearchAppBar(
|
||||
@@ -91,32 +100,30 @@ fun SuperUserScreen(navigator: DestinationsNavigator) {
|
||||
}
|
||||
}
|
||||
},
|
||||
scrollBehavior = scrollBehavior
|
||||
)
|
||||
}
|
||||
},
|
||||
contentWindowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal)
|
||||
) { innerPadding ->
|
||||
val refreshState = rememberPullRefreshState(
|
||||
refreshing = viewModel.isRefreshing,
|
||||
onRefresh = { scope.launch { viewModel.fetchAppList() } },
|
||||
)
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.padding(innerPadding)
|
||||
.pullRefresh(refreshState)
|
||||
PullToRefreshBox(
|
||||
modifier = Modifier.padding(innerPadding),
|
||||
onRefresh = {
|
||||
scope.launch { viewModel.fetchAppList() }
|
||||
},
|
||||
isRefreshing = viewModel.isRefreshing
|
||||
) {
|
||||
LazyColumn(Modifier.fillMaxSize()) {
|
||||
LazyColumn(
|
||||
state = listState,
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.nestedScroll(scrollBehavior.nestedScrollConnection)
|
||||
) {
|
||||
items(viewModel.appList, key = { it.packageName + it.uid }) { app ->
|
||||
AppItem(app) {
|
||||
navigator.navigate(AppProfileScreenDestination(app))
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
PullRefreshIndicator(
|
||||
refreshing = viewModel.isRefreshing,
|
||||
state = refreshState,
|
||||
modifier = Modifier.align(Alignment.TopCenter)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,14 +1,17 @@
|
||||
package me.weishu.kernelsu.ui.screen
|
||||
package com.rifsxd.ksunext.ui.screen
|
||||
|
||||
import android.widget.Toast
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.ExperimentalLayoutApi
|
||||
import androidx.compose.foundation.layout.FlowRow
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.WindowInsets
|
||||
import androidx.compose.foundation.layout.WindowInsetsSides
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.only
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.safeDrawing
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.material.ExperimentalMaterialApi
|
||||
@@ -17,9 +20,6 @@ import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
||||
import androidx.compose.material.icons.filled.Add
|
||||
import androidx.compose.material.icons.filled.ImportExport
|
||||
import androidx.compose.material.icons.filled.Sync
|
||||
import androidx.compose.material.pullrefresh.PullRefreshIndicator
|
||||
import androidx.compose.material.pullrefresh.pullRefresh
|
||||
import androidx.compose.material.pullrefresh.rememberPullRefreshState
|
||||
import androidx.compose.material3.DropdownMenu
|
||||
import androidx.compose.material3.DropdownMenuItem
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
@@ -31,6 +31,10 @@ import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TopAppBar
|
||||
import androidx.compose.material3.TopAppBarDefaults
|
||||
import androidx.compose.material3.TopAppBarScrollBehavior
|
||||
import androidx.compose.material3.pulltorefresh.PullToRefreshBox
|
||||
import androidx.compose.material3.rememberTopAppBarState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
@@ -38,8 +42,8 @@ import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||
import androidx.compose.ui.platform.LocalClipboardManager
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
@@ -47,22 +51,23 @@ import androidx.compose.ui.text.AnnotatedString
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import com.ramcosta.composedestinations.annotation.Destination
|
||||
import com.ramcosta.composedestinations.annotation.RootGraph
|
||||
import com.ramcosta.composedestinations.generated.destinations.TemplateEditorScreenDestination
|
||||
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
|
||||
import com.ramcosta.composedestinations.result.ResultRecipient
|
||||
import com.ramcosta.composedestinations.result.getOr
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import me.weishu.kernelsu.R
|
||||
import me.weishu.kernelsu.ui.screen.destinations.TemplateEditorScreenDestination
|
||||
import me.weishu.kernelsu.ui.viewmodel.TemplateViewModel
|
||||
import com.rifsxd.ksunext.R
|
||||
import com.rifsxd.ksunext.ui.viewmodel.TemplateViewModel
|
||||
|
||||
/**
|
||||
* @author weishu
|
||||
* @date 2023/10/20.
|
||||
*/
|
||||
|
||||
@OptIn(ExperimentalMaterialApi::class)
|
||||
@Destination
|
||||
@OptIn(ExperimentalMaterialApi::class, ExperimentalMaterial3Api::class)
|
||||
@Destination<RootGraph>
|
||||
@Composable
|
||||
fun AppProfileTemplateScreen(
|
||||
navigator: DestinationsNavigator,
|
||||
@@ -70,6 +75,7 @@ fun AppProfileTemplateScreen(
|
||||
) {
|
||||
val viewModel = viewModel<TemplateViewModel>()
|
||||
val scope = rememberCoroutineScope()
|
||||
val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState())
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
if (viewModel.templateList.isEmpty()) {
|
||||
@@ -93,7 +99,8 @@ fun AppProfileTemplateScreen(
|
||||
Toast.makeText(context, msg, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
TopBar(onBack = { navigator.popBackStack() },
|
||||
TopBar(
|
||||
onBack = { navigator.popBackStack() },
|
||||
onSync = {
|
||||
scope.launch { viewModel.fetchTemplates(true) }
|
||||
},
|
||||
@@ -124,7 +131,8 @@ fun AppProfileTemplateScreen(
|
||||
clipboardManager.setText(AnnotatedString(it))
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
scrollBehavior = scrollBehavior
|
||||
)
|
||||
},
|
||||
floatingActionButton = {
|
||||
@@ -141,29 +149,27 @@ fun AppProfileTemplateScreen(
|
||||
text = { Text(stringResource(id = R.string.app_profile_template_create)) },
|
||||
)
|
||||
},
|
||||
contentWindowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal)
|
||||
) { innerPadding ->
|
||||
val refreshState = rememberPullRefreshState(
|
||||
refreshing = viewModel.isRefreshing,
|
||||
onRefresh = { scope.launch { viewModel.fetchTemplates() } },
|
||||
)
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.padding(innerPadding)
|
||||
.pullRefresh(refreshState)
|
||||
PullToRefreshBox(
|
||||
modifier = Modifier.padding(innerPadding),
|
||||
isRefreshing = viewModel.isRefreshing,
|
||||
onRefresh = {
|
||||
scope.launch { viewModel.fetchTemplates() }
|
||||
}
|
||||
) {
|
||||
LazyColumn(Modifier.fillMaxSize(), contentPadding = remember {
|
||||
PaddingValues(bottom = 16.dp + 16.dp + 56.dp /* Scaffold Fab Spacing + Fab container height */)
|
||||
}) {
|
||||
LazyColumn(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.nestedScroll(scrollBehavior.nestedScrollConnection),
|
||||
contentPadding = remember {
|
||||
PaddingValues(bottom = 16.dp + 56.dp + 16.dp /* Scaffold Fab Spacing + Fab container height */)
|
||||
}
|
||||
) {
|
||||
items(viewModel.templateList, key = { it.id }) { app ->
|
||||
TemplateItem(navigator, app)
|
||||
}
|
||||
}
|
||||
|
||||
PullRefreshIndicator(
|
||||
refreshing = viewModel.isRefreshing,
|
||||
state = refreshState,
|
||||
modifier = Modifier.align(Alignment.TopCenter)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -209,7 +215,8 @@ private fun TopBar(
|
||||
onBack: () -> Unit,
|
||||
onSync: () -> Unit = {},
|
||||
onImport: () -> Unit = {},
|
||||
onExport: () -> Unit = {}
|
||||
onExport: () -> Unit = {},
|
||||
scrollBehavior: TopAppBarScrollBehavior? = null
|
||||
) {
|
||||
TopAppBar(
|
||||
title = {
|
||||
@@ -254,6 +261,8 @@ private fun TopBar(
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
windowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal),
|
||||
scrollBehavior = scrollBehavior
|
||||
)
|
||||
}
|
||||
@@ -1,10 +1,14 @@
|
||||
package me.weishu.kernelsu.ui.screen
|
||||
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
|
||||
@@ -22,6 +26,9 @@ import androidx.compose.material3.OutlinedTextField
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TopAppBar
|
||||
import androidx.compose.material3.TopAppBarDefaults
|
||||
import androidx.compose.material3.TopAppBarScrollBehavior
|
||||
import androidx.compose.material3.rememberTopAppBarState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
@@ -30,6 +37,7 @@ import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.ExperimentalComposeUiApi
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||
import androidx.compose.ui.input.pointer.pointerInteropFilter
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
|
||||
@@ -37,22 +45,23 @@ import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.input.ImeAction
|
||||
import androidx.compose.ui.text.input.KeyboardType
|
||||
import com.ramcosta.composedestinations.annotation.Destination
|
||||
import com.ramcosta.composedestinations.annotation.RootGraph
|
||||
import com.ramcosta.composedestinations.result.ResultBackNavigator
|
||||
import me.weishu.kernelsu.Natives
|
||||
import me.weishu.kernelsu.R
|
||||
import me.weishu.kernelsu.ui.component.profile.RootProfileConfig
|
||||
import me.weishu.kernelsu.ui.util.deleteAppProfileTemplate
|
||||
import me.weishu.kernelsu.ui.util.getAppProfileTemplate
|
||||
import me.weishu.kernelsu.ui.util.setAppProfileTemplate
|
||||
import me.weishu.kernelsu.ui.viewmodel.TemplateViewModel
|
||||
import me.weishu.kernelsu.ui.viewmodel.toJSON
|
||||
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)
|
||||
@Destination
|
||||
@OptIn(ExperimentalComposeUiApi::class, ExperimentalMaterial3Api::class)
|
||||
@Destination<RootGraph>
|
||||
@Composable
|
||||
fun TemplateEditorScreen(
|
||||
navigator: ResultBackNavigator<Boolean>,
|
||||
@@ -67,6 +76,8 @@ fun TemplateEditorScreen(
|
||||
mutableStateOf(initialTemplate)
|
||||
}
|
||||
|
||||
val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState())
|
||||
|
||||
BackHandler {
|
||||
navigator.navigateBack(result = !readOnly)
|
||||
}
|
||||
@@ -106,12 +117,16 @@ fun TemplateEditorScreen(
|
||||
} else {
|
||||
Toast.makeText(context, saveTemplateFailed, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
})
|
||||
},
|
||||
scrollBehavior = scrollBehavior
|
||||
)
|
||||
},
|
||||
contentWindowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal)
|
||||
) { innerPadding ->
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.padding(innerPadding)
|
||||
.nestedScroll(scrollBehavior.nestedScrollConnection)
|
||||
.verticalScroll(rememberScrollState())
|
||||
.pointerInteropFilter {
|
||||
// disable click and ripple if readOnly
|
||||
@@ -240,39 +255,44 @@ private fun TopBar(
|
||||
summary: String = "",
|
||||
onBack: () -> Unit,
|
||||
onDelete: () -> Unit = {},
|
||||
onSave: () -> Unit = {}
|
||||
onSave: () -> Unit = {},
|
||||
scrollBehavior: TopAppBarScrollBehavior? = null
|
||||
) {
|
||||
TopAppBar(title = {
|
||||
Column {
|
||||
Text(title)
|
||||
if (summary.isNotBlank()) {
|
||||
Text(
|
||||
text = summary,
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
TopAppBar(
|
||||
title = {
|
||||
Column {
|
||||
Text(title)
|
||||
if (summary.isNotBlank()) {
|
||||
Text(
|
||||
text = summary,
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
)
|
||||
}
|
||||
}
|
||||
}, navigationIcon = {
|
||||
IconButton(
|
||||
onClick = onBack
|
||||
) { Icon(Icons.AutoMirrored.Filled.ArrowBack, contentDescription = null) }
|
||||
}, actions = {
|
||||
if (readOnly) {
|
||||
return@TopAppBar
|
||||
}
|
||||
IconButton(onClick = onDelete) {
|
||||
Icon(
|
||||
Icons.Filled.DeleteForever,
|
||||
contentDescription = stringResource(id = R.string.app_profile_template_delete)
|
||||
)
|
||||
}
|
||||
}
|
||||
}, navigationIcon = {
|
||||
IconButton(
|
||||
onClick = onBack
|
||||
) { Icon(Icons.AutoMirrored.Filled.ArrowBack, contentDescription = null) }
|
||||
}, actions = {
|
||||
if (readOnly) {
|
||||
return@TopAppBar
|
||||
}
|
||||
IconButton(onClick = onDelete) {
|
||||
Icon(
|
||||
Icons.Filled.DeleteForever,
|
||||
contentDescription = stringResource(id = R.string.app_profile_template_delete)
|
||||
)
|
||||
}
|
||||
IconButton(onClick = onSave) {
|
||||
Icon(
|
||||
imageVector = Icons.Filled.Save,
|
||||
contentDescription = stringResource(id = R.string.app_profile_template_save)
|
||||
)
|
||||
}
|
||||
})
|
||||
IconButton(onClick = onSave) {
|
||||
Icon(
|
||||
imageVector = Icons.Filled.Save,
|
||||
contentDescription = stringResource(id = R.string.app_profile_template_save)
|
||||
)
|
||||
}
|
||||
},
|
||||
windowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal),
|
||||
scrollBehavior = scrollBehavior
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
@@ -289,17 +309,14 @@ private fun TextEdit(
|
||||
value = text,
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
label = { Text(label) },
|
||||
suffix =
|
||||
if (errorHint.isNotBlank()) {
|
||||
{
|
||||
suffix = {
|
||||
if (errorHint.isNotBlank()) {
|
||||
Text(
|
||||
text = if (isError) errorHint else "",
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
color = MaterialTheme.colorScheme.error
|
||||
)
|
||||
}
|
||||
} else {
|
||||
null
|
||||
},
|
||||
isError = isError,
|
||||
keyboardOptions = KeyboardOptions(
|
||||
@@ -314,7 +331,7 @@ private fun TextEdit(
|
||||
}
|
||||
|
||||
private fun isValidTemplateId(id: String): Boolean {
|
||||
return Regex("""^([A-Za-z]{1}[A-Za-z\d_]*\.)*[A-Za-z][A-Za-z\d_]*$""").matches(id)
|
||||
return Regex("""^([A-Za-z][A-Za-z\d_]*\.)*[A-Za-z][A-Za-z\d_]*$""").matches(id)
|
||||
}
|
||||
|
||||
private fun isTemplateExist(id: String): Boolean {
|
||||
@@ -1,4 +1,4 @@
|
||||
package me.weishu.kernelsu.ui.theme
|
||||
package com.rifsxd.ksunext.ui.theme
|
||||
|
||||
import androidx.compose.ui.graphics.Color
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package me.weishu.kernelsu.ui.theme
|
||||
package com.rifsxd.ksunext.ui.theme
|
||||
|
||||
import android.os.Build
|
||||
import androidx.compose.foundation.isSystemInDarkTheme
|
||||
@@ -7,12 +7,8 @@ import androidx.compose.material3.darkColorScheme
|
||||
import androidx.compose.material3.dynamicDarkColorScheme
|
||||
import androidx.compose.material3.dynamicLightColorScheme
|
||||
import androidx.compose.material3.lightColorScheme
|
||||
import androidx.compose.material3.surfaceColorAtElevation
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.SideEffect
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.google.accompanist.systemuicontroller.rememberSystemUiController
|
||||
|
||||
private val DarkColorScheme = darkColorScheme(
|
||||
primary = YELLOW,
|
||||
@@ -42,20 +38,6 @@ fun KernelSUTheme(
|
||||
else -> LightColorScheme
|
||||
}
|
||||
|
||||
val systemUiController = rememberSystemUiController()
|
||||
SideEffect {
|
||||
systemUiController.setStatusBarColor(
|
||||
color = colorScheme.surface,
|
||||
darkIcons = !darkTheme
|
||||
)
|
||||
|
||||
// To match the App Navbar color
|
||||
systemUiController.setNavigationBarColor(
|
||||
color = colorScheme.surfaceColorAtElevation(8.dp),
|
||||
darkIcons = !darkTheme,
|
||||
)
|
||||
}
|
||||
|
||||
MaterialTheme(
|
||||
colorScheme = colorScheme,
|
||||
typography = Typography,
|
||||
@@ -1,4 +1,4 @@
|
||||
package me.weishu.kernelsu.ui.theme
|
||||
package com.rifsxd.ksunext.ui.theme
|
||||
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.text.font.FontFamily
|
||||
@@ -1,4 +1,4 @@
|
||||
package me.weishu.kernelsu.ui.util
|
||||
package com.rifsxd.ksunext.ui.util
|
||||
|
||||
import androidx.compose.material3.SnackbarHostState
|
||||
import androidx.compose.runtime.compositionLocalOf
|
||||
@@ -1,4 +1,4 @@
|
||||
package me.weishu.kernelsu.ui.util
|
||||
package com.rifsxd.ksunext.ui.util
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.DownloadManager
|
||||
@@ -7,11 +7,11 @@ import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.IntentFilter
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.os.Environment
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.DisposableEffect
|
||||
import me.weishu.kernelsu.ui.util.module.LatestVersionInfo
|
||||
import androidx.core.content.ContextCompat
|
||||
import com.rifsxd.ksunext.ui.util.module.LatestVersionInfo
|
||||
|
||||
/**
|
||||
* @author weishu
|
||||
@@ -26,8 +26,7 @@ fun download(
|
||||
onDownloaded: (Uri) -> Unit = {},
|
||||
onDownloading: () -> Unit = {}
|
||||
) {
|
||||
val downloadManager =
|
||||
context.getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager
|
||||
val downloadManager = context.getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager
|
||||
|
||||
val query = DownloadManager.Query()
|
||||
query.setFilterByStatus(DownloadManager.STATUS_RUNNING or DownloadManager.STATUS_PAUSED or DownloadManager.STATUS_PENDING)
|
||||
@@ -63,7 +62,8 @@ fun download(
|
||||
}
|
||||
|
||||
fun checkNewVersion(): LatestVersionInfo {
|
||||
val url = "https://api.github.com/repos/tiann/KernelSU/releases/latest"
|
||||
// Next version updates
|
||||
val url = "https://api.github.com/repos/rifsxd/KernelSU-Next/releases/latest"
|
||||
// default null value if failed
|
||||
val defaultValue = LatestVersionInfo()
|
||||
runCatching {
|
||||
@@ -130,18 +130,12 @@ fun DownloadListener(context: Context, onDownloaded: (Uri) -> Unit) {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||
context.registerReceiver(
|
||||
receiver,
|
||||
IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE),
|
||||
Context.RECEIVER_EXPORTED
|
||||
)
|
||||
} else {
|
||||
context.registerReceiver(
|
||||
receiver,
|
||||
IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE)
|
||||
)
|
||||
}
|
||||
ContextCompat.registerReceiver(
|
||||
context,
|
||||
receiver,
|
||||
IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE),
|
||||
ContextCompat.RECEIVER_EXPORTED
|
||||
)
|
||||
onDispose {
|
||||
context.unregisterReceiver(receiver)
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package me.weishu.kernelsu.ui.util;
|
||||
package com.rifsxd.ksunext.ui.util;
|
||||
/*
|
||||
* Copyright (C) 2009 The Android Open Source Project
|
||||
*
|
||||
@@ -1,4 +1,4 @@
|
||||
package me.weishu.kernelsu.ui.util
|
||||
package com.rifsxd.ksunext.ui.util
|
||||
|
||||
import androidx.compose.foundation.gestures.detectTapGestures
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
@@ -1,25 +1,24 @@
|
||||
package me.weishu.kernelsu.ui.util
|
||||
package com.rifsxd.ksunext.ui.util
|
||||
|
||||
import android.content.ContentResolver
|
||||
import android.content.Context
|
||||
import android.database.Cursor
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.os.Environment
|
||||
import android.os.Parcelable
|
||||
import android.os.SystemClock
|
||||
import android.provider.OpenableColumns
|
||||
import android.system.Os
|
||||
import android.util.Log
|
||||
import com.topjohnwu.superuser.CallbackList
|
||||
import com.topjohnwu.superuser.Shell
|
||||
import com.topjohnwu.superuser.ShellUtils
|
||||
import com.topjohnwu.superuser.io.SuFile
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import kotlinx.parcelize.Parcelize
|
||||
import me.weishu.kernelsu.BuildConfig
|
||||
import me.weishu.kernelsu.Natives
|
||||
import me.weishu.kernelsu.ksuApp
|
||||
import com.rifsxd.ksunext.BuildConfig
|
||||
import com.rifsxd.ksunext.Natives
|
||||
import com.rifsxd.ksunext.ksuApp
|
||||
import org.json.JSONArray
|
||||
import java.io.File
|
||||
|
||||
@@ -52,10 +51,10 @@ inline fun <T> withNewRootShell(
|
||||
return createRootShell(globalMnt).use(block)
|
||||
}
|
||||
|
||||
fun getFileNameFromUri(context: Context, uri: Uri): String? {
|
||||
fun Uri.getFileName(context: Context): String? {
|
||||
var fileName: String? = null
|
||||
val contentResolver: ContentResolver = context.contentResolver
|
||||
val cursor: Cursor? = contentResolver.query(uri, null, null, null, null)
|
||||
val cursor: Cursor? = contentResolver.query(this, null, null, null, null)
|
||||
cursor?.use {
|
||||
if (it.moveToFirst()) {
|
||||
fileName = it.getString(it.getColumnIndexOrThrow(OpenableColumns.DISPLAY_NAME))
|
||||
@@ -143,6 +142,13 @@ fun uninstallModule(id: String): Boolean {
|
||||
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,
|
||||
@@ -189,6 +195,30 @@ fun flashModule(
|
||||
}
|
||||
}
|
||||
|
||||
fun runModuleAction(
|
||||
moduleId: String, onStdout: (String) -> Unit, onStderr: (String) -> Unit
|
||||
): Boolean {
|
||||
val shell = getRootShell()
|
||||
|
||||
val stdoutCallback: CallbackList<String?> = object : CallbackList<String?>() {
|
||||
override fun onAddElement(s: String?) {
|
||||
onStdout(s ?: "")
|
||||
}
|
||||
}
|
||||
|
||||
val stderrCallback: CallbackList<String?> = object : CallbackList<String?>() {
|
||||
override fun onAddElement(s: String?) {
|
||||
onStderr(s ?: "")
|
||||
}
|
||||
}
|
||||
|
||||
val result = shell.newJob().add("${getKsuDaemonPath()} module action $moduleId")
|
||||
.to(stdoutCallback, stderrCallback).exec()
|
||||
Log.i("KernelSU", "Module runAction result: $result")
|
||||
|
||||
return result.isSuccess
|
||||
}
|
||||
|
||||
fun restoreBoot(
|
||||
onFinish: (Boolean, Int) -> Unit, onStdout: (String) -> Unit, onStderr: (String) -> Unit
|
||||
): Boolean {
|
||||
@@ -207,10 +237,6 @@ fun uninstallPermanently(
|
||||
return result.isSuccess
|
||||
}
|
||||
|
||||
suspend fun shrinkModules(): Boolean = withContext(Dispatchers.IO) {
|
||||
execKsud("module shrink", true)
|
||||
}
|
||||
|
||||
@Parcelize
|
||||
sealed class LkmSelection : Parcelable {
|
||||
data class LkmUri(val uri: Uri) : LkmSelection()
|
||||
@@ -312,18 +338,7 @@ fun isAbDevice(): Boolean {
|
||||
}
|
||||
|
||||
fun isInitBoot(): Boolean {
|
||||
val shell = getRootShell()
|
||||
if (shell.isRoot) {
|
||||
// if we have root, use /dev/block/by-name/init_boot to check
|
||||
val abDevice = isAbDevice()
|
||||
val initBootBlock = "/dev/block/by-name/init_boot${if (abDevice) "_a" else ""}"
|
||||
val file = SuFile(initBootBlock)
|
||||
file.shell = shell
|
||||
return file.exists()
|
||||
}
|
||||
// https://source.android.com/docs/core/architecture/partitions/generic-boot
|
||||
return ShellUtils.fastCmd(shell, "getprop ro.product.first_api_level").trim()
|
||||
.toInt() >= Build.VERSION_CODES.TIRAMISU
|
||||
return !Os.uname().release.contains("android12-")
|
||||
}
|
||||
|
||||
suspend fun getCurrentKmi(): String = withContext(Dispatchers.IO) {
|
||||
@@ -339,12 +354,6 @@ suspend fun getSupportedKmis(): List<String> = withContext(Dispatchers.IO) {
|
||||
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()
|
||||
@@ -1,16 +1,12 @@
|
||||
package me.weishu.kernelsu.ui.util
|
||||
package com.rifsxd.ksunext.ui.util
|
||||
|
||||
import android.content.ContentResolver
|
||||
import android.content.Context
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.os.ParcelFileDescriptor
|
||||
import android.system.Os
|
||||
import com.topjohnwu.superuser.ShellUtils
|
||||
import me.weishu.kernelsu.Natives
|
||||
import me.weishu.kernelsu.ui.screen.getManagerVersion
|
||||
import com.rifsxd.ksunext.Natives
|
||||
import com.rifsxd.ksunext.ui.screen.getManagerVersion
|
||||
import java.io.File
|
||||
import java.io.FileOutputStream
|
||||
import java.io.FileWriter
|
||||
import java.io.PrintWriter
|
||||
import java.time.LocalDateTime
|
||||
@@ -75,6 +71,7 @@ fun getBugreportFile(context: Context): File {
|
||||
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)
|
||||
@@ -1,9 +1,9 @@
|
||||
package me.weishu.kernelsu.ui.util
|
||||
package com.rifsxd.ksunext.ui.util
|
||||
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import com.topjohnwu.superuser.Shell
|
||||
import me.weishu.kernelsu.R
|
||||
import com.rifsxd.ksunext.R
|
||||
|
||||
@Composable
|
||||
fun getSELinuxStatus(): String {
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user