From 74c704248b5ec831c5e5281f233e38e854d4909f Mon Sep 17 00:00:00 2001
From: YueMiyuki <76854136+YueMiyuki@users.noreply.github.com>
Date: Tue, 26 May 2026 13:14:41 +0800
Subject: [PATCH 1/7] Update actions
---
.github/workflows/bump-homebrew-tap.yml | 3 +++
.github/workflows/codeql-analysis.yml | 3 +++
.github/workflows/publish-npm.yml | 1 +
.github/workflows/release.yml | 3 +++
4 files changed, 10 insertions(+)
diff --git a/.github/workflows/bump-homebrew-tap.yml b/.github/workflows/bump-homebrew-tap.yml
index b1a2333d..60b69271 100644
--- a/.github/workflows/bump-homebrew-tap.yml
+++ b/.github/workflows/bump-homebrew-tap.yml
@@ -18,6 +18,9 @@ on:
permissions:
contents: read
+env:
+ FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
+
jobs:
bump:
name: Bump Formula/risuko-cli.rb and Casks/risuko-app.rb
diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml
index 37519db7..8265cde9 100644
--- a/.github/workflows/codeql-analysis.yml
+++ b/.github/workflows/codeql-analysis.yml
@@ -20,6 +20,9 @@ on:
schedule:
- cron: '23 8 * * 5'
+env:
+ FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
+
jobs:
analyze:
name: Analyze
diff --git a/.github/workflows/publish-npm.yml b/.github/workflows/publish-npm.yml
index 799b5a6e..2a9cae9f 100644
--- a/.github/workflows/publish-npm.yml
+++ b/.github/workflows/publish-npm.yml
@@ -18,6 +18,7 @@ permissions:
env:
CARGO_INCREMENTAL: 0
CARGO_NET_RETRY: 10
+ FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
jobs:
build-cli:
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 66263029..ac9ac377 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -9,6 +9,9 @@ on:
permissions:
contents: write
+env:
+ FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
+
jobs:
release:
name: Release (${{ matrix.platform }})
From b5cadef5f22d1ef0a9d0b00ba56ac7ad655903e7 Mon Sep 17 00:00:00 2001
From: YueMiyuki <76854136+YueMiyuki@users.noreply.github.com>
Date: Thu, 28 May 2026 18:32:34 +0800
Subject: [PATCH 2/7] Optimize android layout and fix file picker issues
---
.github/workflows/release.yml | 102 ++
.gitignore | 4 +-
package.json | 5 +
packages/risuko-app/package.json | 52 +-
.../risuko-cli/npm/darwin-arm64/package.json | 36 +-
.../risuko-cli/npm/darwin-x64/package.json | 36 +-
.../npm/linux-arm64-gnu/package.json | 36 +-
.../risuko-cli/npm/linux-x64-gnu/package.json | 36 +-
.../npm/win32-arm64-msvc/package.json | 36 +-
.../npm/win32-x64-msvc/package.json | 36 +-
packages/risuko-cli/package.json | 70 +-
.../risuko-js/npm/darwin-arm64/package.json | 38 +-
.../risuko-js/npm/darwin-x64/package.json | 38 +-
.../npm/linux-arm64-gnu/package.json | 44 +-
.../risuko-js/npm/linux-x64-gnu/package.json | 44 +-
.../npm/win32-arm64-msvc/package.json | 38 +-
.../risuko-js/npm/win32-x64-msvc/package.json | 38 +-
packages/risuko-js/package.json | 90 +-
pnpm-lock.yaml | 142 +-
pnpm-workspace.yaml | 13 +
scripts/android-env.mjs | 112 ++
scripts/sign-android-apks.mjs | 161 ++
src-tauri/Cargo.lock | 8 +
src-tauri/Cargo.toml | 18 +-
src-tauri/capabilities/default.json | 14 +-
src-tauri/capabilities/desktop.json | 11 +
src-tauri/gen/android/.editorconfig | 12 +
src-tauri/gen/android/.gitignore | 19 +
src-tauri/gen/android/app/.gitignore | 3 +
src-tauri/gen/android/app/build.gradle.kts | 71 +
src-tauri/gen/android/app/proguard-rules.pro | 24 +
src-tauri/gen/android/app/proguard-tauri.pro | 5 +
.../android/app/src/main/AndroidManifest.xml | 75 +
.../java/app/risuko/mobile/MainActivity.kt | 397 +++++
.../risuko/mobile/RisukoForegroundService.kt | 111 ++
.../drawable-v24/ic_launcher_foreground.xml | 30 +
.../main/res/drawable/empty_splash_icon.xml | 5 +
.../res/drawable/ic_launcher_background.xml | 170 +++
.../src/main/res/drawable/ic_notification.xml | 10 +
.../app/src/main/res/layout/activity_main.xml | 18 +
.../res/mipmap-anydpi-v26/ic_launcher.xml | 5 +
.../src/main/res/mipmap-hdpi/ic_launcher.png | Bin 0 -> 2826 bytes
.../mipmap-hdpi/ic_launcher_foreground.png | Bin 0 -> 2826 bytes
.../res/mipmap-hdpi/ic_launcher_round.png | Bin 0 -> 2826 bytes
.../src/main/res/mipmap-mdpi/ic_launcher.png | Bin 0 -> 1774 bytes
.../mipmap-mdpi/ic_launcher_foreground.png | Bin 0 -> 1774 bytes
.../res/mipmap-mdpi/ic_launcher_round.png | Bin 0 -> 1774 bytes
.../src/main/res/mipmap-xhdpi/ic_launcher.png | Bin 0 -> 4009 bytes
.../mipmap-xhdpi/ic_launcher_foreground.png | Bin 0 -> 4009 bytes
.../res/mipmap-xhdpi/ic_launcher_round.png | Bin 0 -> 4009 bytes
.../main/res/mipmap-xxhdpi/ic_launcher.png | Bin 0 -> 6175 bytes
.../mipmap-xxhdpi/ic_launcher_foreground.png | Bin 0 -> 6175 bytes
.../res/mipmap-xxhdpi/ic_launcher_round.png | Bin 0 -> 6175 bytes
.../main/res/mipmap-xxxhdpi/ic_launcher.png | Bin 0 -> 8465 bytes
.../mipmap-xxxhdpi/ic_launcher_foreground.png | Bin 0 -> 8465 bytes
.../res/mipmap-xxxhdpi/ic_launcher_round.png | Bin 0 -> 8465 bytes
.../app/src/main/res/values-night/themes.xml | 6 +
.../app/src/main/res/values-v31/themes.xml | 7 +
.../app/src/main/res/values/colors.xml | 10 +
.../res/values/ic_launcher_background.xml | 4 +
.../app/src/main/res/values/strings.xml | 4 +
.../app/src/main/res/values/themes.xml | 6 +
.../app/src/main/res/xml/file_paths.xml | 5 +
.../gen/android/app/tauri.build.gradle.kts | 12 +
src-tauri/gen/android/app/tauri.properties | 3 +
src-tauri/gen/android/build.gradle.kts | 22 +
.../gen/android/buildSrc/build.gradle.kts | 23 +
.../app/risuko/mobile/kotlin/BuildTask.kt | 68 +
.../app/risuko/mobile/kotlin/RustPlugin.kt | 85 ++
src-tauri/gen/android/gradle.properties | 24 +
.../android/gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 59203 bytes
.../gradle/wrapper/gradle-wrapper.properties | 6 +
src-tauri/gen/android/gradlew | 185 +++
src-tauri/gen/android/gradlew.bat | 89 ++
src-tauri/gen/android/settings.gradle | 3 +
src-tauri/gen/android/tauri.settings.gradle | 15 +
src-tauri/icons/128x128.png | Bin 6688 -> 3752 bytes
src-tauri/icons/128x128@2x.png | Bin 14683 -> 7803 bytes
src-tauri/icons/32x32.png | Bin 1439 -> 808 bytes
src-tauri/icons/64x64.png | Bin 3069 -> 1821 bytes
src-tauri/icons/Square107x107Logo.png | Bin 5774 -> 3084 bytes
src-tauri/icons/Square142x142Logo.png | Bin 7814 -> 4133 bytes
src-tauri/icons/Square150x150Logo.png | Bin 8371 -> 4459 bytes
src-tauri/icons/Square284x284Logo.png | Bin 16928 -> 8659 bytes
src-tauri/icons/Square30x30Logo.png | Bin 1403 -> 758 bytes
src-tauri/icons/Square310x310Logo.png | Bin 18561 -> 9453 bytes
src-tauri/icons/Square44x44Logo.png | Bin 2196 -> 1217 bytes
src-tauri/icons/Square71x71Logo.png | Bin 3722 -> 2022 bytes
src-tauri/icons/Square89x89Logo.png | Bin 4745 -> 2545 bytes
src-tauri/icons/StoreLogo.png | Bin 2443 -> 1361 bytes
.../icons/android/mipmap-hdpi/ic_launcher.png | Bin 3699 -> 2826 bytes
.../mipmap-hdpi/ic_launcher_foreground.png | Bin 3699 -> 2826 bytes
.../android/mipmap-hdpi/ic_launcher_round.png | Bin 3699 -> 2826 bytes
.../icons/android/mipmap-mdpi/ic_launcher.png | Bin 2284 -> 1774 bytes
.../mipmap-mdpi/ic_launcher_foreground.png | Bin 2284 -> 1774 bytes
.../android/mipmap-mdpi/ic_launcher_round.png | Bin 2284 -> 1774 bytes
.../android/mipmap-xhdpi/ic_launcher.png | Bin 4848 -> 4009 bytes
.../mipmap-xhdpi/ic_launcher_foreground.png | Bin 4848 -> 4009 bytes
.../mipmap-xhdpi/ic_launcher_round.png | Bin 4848 -> 4009 bytes
.../android/mipmap-xxhdpi/ic_launcher.png | Bin 7909 -> 6175 bytes
.../mipmap-xxhdpi/ic_launcher_foreground.png | Bin 7909 -> 6175 bytes
.../mipmap-xxhdpi/ic_launcher_round.png | Bin 7909 -> 6175 bytes
.../android/mipmap-xxxhdpi/ic_launcher.png | Bin 10580 -> 8465 bytes
.../mipmap-xxxhdpi/ic_launcher_foreground.png | Bin 10580 -> 8465 bytes
.../mipmap-xxxhdpi/ic_launcher_round.png | Bin 10580 -> 8465 bytes
.../android/values/ic_launcher_background.xml | 2 +-
src-tauri/icons/icon.icns | Bin 253675 -> 90841 bytes
src-tauri/icons/icon.ico | Bin 372526 -> 14141 bytes
src-tauri/icons/icon.png | Bin 32638 -> 15944 bytes
src-tauri/icons/icon.svg | 27 +-
src-tauri/icons/ios/AppIcon-20x20@1x.png | Bin 833 -> 467 bytes
src-tauri/icons/ios/AppIcon-20x20@2x-1.png | Bin 1954 -> 1080 bytes
src-tauri/icons/ios/AppIcon-20x20@2x.png | Bin 1954 -> 1080 bytes
src-tauri/icons/ios/AppIcon-20x20@3x.png | Bin 3033 -> 1718 bytes
src-tauri/icons/ios/AppIcon-29x29@1x.png | Bin 1298 -> 725 bytes
src-tauri/icons/ios/AppIcon-29x29@2x-1.png | Bin 2940 -> 1608 bytes
src-tauri/icons/ios/AppIcon-29x29@2x.png | Bin 2940 -> 1608 bytes
src-tauri/icons/ios/AppIcon-29x29@3x.png | Bin 4622 -> 2471 bytes
src-tauri/icons/ios/AppIcon-40x40@1x.png | Bin 1954 -> 1080 bytes
src-tauri/icons/ios/AppIcon-40x40@2x-1.png | Bin 4226 -> 2262 bytes
src-tauri/icons/ios/AppIcon-40x40@2x.png | Bin 4226 -> 2262 bytes
src-tauri/icons/ios/AppIcon-40x40@3x.png | Bin 6518 -> 3447 bytes
src-tauri/icons/ios/AppIcon-512@2x.png | Bin 73390 -> 34604 bytes
src-tauri/icons/ios/AppIcon-60x60@2x.png | Bin 6518 -> 3447 bytes
src-tauri/icons/ios/AppIcon-60x60@3x.png | Bin 10283 -> 5329 bytes
src-tauri/icons/ios/AppIcon-76x76@1x.png | Bin 3896 -> 2168 bytes
src-tauri/icons/ios/AppIcon-76x76@2x.png | Bin 8305 -> 4476 bytes
src-tauri/icons/ios/AppIcon-83.5x83.5@2x.png | Bin 9363 -> 4949 bytes
src-tauri/risuko-cookies/Cargo.toml | 4 +-
src-tauri/risuko-cookies/src/lib.rs | 38 +-
.../risuko-engine/src/config/defaults.rs | 56 +-
src-tauri/risuko-engine/src/config/mod.rs | 2 +-
src-tauri/risuko-engine/src/engine/http.rs | 16 +-
src-tauri/src/commands/android_intent.rs | 540 +++++++
src-tauri/src/commands/app_cmds.rs | 54 +
src-tauri/src/commands/config_cmds.rs | 41 +-
src-tauri/src/commands/event_cmds.rs | 137 +-
src-tauri/src/commands/file_cmds.rs | 229 ++-
src-tauri/src/commands/health_cmds.rs | 18 +-
src-tauri/src/commands/mod.rs | 2 +
src-tauri/src/lib.rs | 761 +++++-----
src-tauri/src/managers/menu.rs | 36 +
src-tauri/src/managers/mod.rs | 3 +
src-tauri/src/managers/tray.rs | 23 +
src-tauri/tauri.android.conf.json | 3 +
src/renderer/api/Api.ts | 12 +
src/renderer/assets/logo.svg | 2 +-
src/renderer/components/DragSelect/Index.vue | 36 +-
src/renderer/components/Health/Index.vue | 53 +-
src/renderer/components/Main.vue | 150 +-
.../components/Native/SelectDirectory.vue | 73 +-
.../components/Native/ShowInFolder.vue | 14 +-
.../components/Preference/Advanced.vue | 37 +-
src/renderer/components/Rss/Index.vue | 26 +-
.../components/Subnav/SubnavSwitcher.vue | 39 +-
src/renderer/components/Task/AddTask.vue | 12 +-
src/renderer/components/Task/Index.vue | 112 +-
src/renderer/components/Task/TaskActions.vue | 40 +-
src/renderer/components/Task/TaskItem.vue | 8 +-
.../components/Task/TaskItemActions.vue | 66 +-
src/renderer/components/Task/TaskList.vue | 75 +-
src/renderer/components/TaskDetail/Index.vue | 6 +-
.../components/TaskDetail/TaskGeneral.vue | 7 +-
.../ui/confirm-dialog/ConfirmDialog.vue | 6 +-
src/renderer/pages/index/App.vue | 40 +-
src/renderer/pages/index/main.ts | 3 +-
src/renderer/router/index.ts | 1 +
src/renderer/shims/platform.ts | 4 +
src/renderer/store/app.ts | 73 +-
src/renderer/store/preference.ts | 7 +-
src/renderer/store/task.ts | 112 +-
src/renderer/styles/android.css | 1316 +++++++++++++++++
src/renderer/styles/app.css | 3 +-
src/renderer/styles/components/input.css | 14 +
.../styles/components/preferences.css | 49 +
.../styles/components/task-detail.css | 20 +
src/renderer/styles/components/task.css | 4 +
src/renderer/utils/native.ts | 91 +-
src/shared/configKeys.ts | 1 +
src/shared/locales/en-US/app.ts | 8 +
src/shared/locales/en-US/preferences.ts | 4 +
src/shared/locales/en-US/task.ts | 3 +
src/shared/locales/index.ts | 21 +
src/shared/locales/zh-CN/app.ts | 8 +
src/shared/locales/zh-CN/preferences.ts | 3 +
src/shared/locales/zh-CN/task.ts | 3 +
src/shared/locales/zh-TW/app.ts | 8 +
src/shared/locales/zh-TW/preferences.ts | 3 +
src/shared/locales/zh-TW/task.ts | 4 +
src/shared/types/config.ts | 1 +
static/logo.svg | 2 +-
191 files changed, 6251 insertions(+), 1111 deletions(-)
create mode 100644 scripts/android-env.mjs
create mode 100644 scripts/sign-android-apks.mjs
create mode 100644 src-tauri/capabilities/desktop.json
create mode 100644 src-tauri/gen/android/.editorconfig
create mode 100644 src-tauri/gen/android/.gitignore
create mode 100644 src-tauri/gen/android/app/.gitignore
create mode 100644 src-tauri/gen/android/app/build.gradle.kts
create mode 100644 src-tauri/gen/android/app/proguard-rules.pro
create mode 100644 src-tauri/gen/android/app/proguard-tauri.pro
create mode 100644 src-tauri/gen/android/app/src/main/AndroidManifest.xml
create mode 100644 src-tauri/gen/android/app/src/main/java/app/risuko/mobile/MainActivity.kt
create mode 100644 src-tauri/gen/android/app/src/main/java/app/risuko/mobile/RisukoForegroundService.kt
create mode 100644 src-tauri/gen/android/app/src/main/res/drawable-v24/ic_launcher_foreground.xml
create mode 100644 src-tauri/gen/android/app/src/main/res/drawable/empty_splash_icon.xml
create mode 100644 src-tauri/gen/android/app/src/main/res/drawable/ic_launcher_background.xml
create mode 100644 src-tauri/gen/android/app/src/main/res/drawable/ic_notification.xml
create mode 100644 src-tauri/gen/android/app/src/main/res/layout/activity_main.xml
create mode 100644 src-tauri/gen/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
create mode 100644 src-tauri/gen/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
create mode 100644 src-tauri/gen/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png
create mode 100644 src-tauri/gen/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
create mode 100644 src-tauri/gen/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
create mode 100644 src-tauri/gen/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png
create mode 100644 src-tauri/gen/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
create mode 100644 src-tauri/gen/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
create mode 100644 src-tauri/gen/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png
create mode 100644 src-tauri/gen/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
create mode 100644 src-tauri/gen/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
create mode 100644 src-tauri/gen/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png
create mode 100644 src-tauri/gen/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
create mode 100644 src-tauri/gen/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
create mode 100644 src-tauri/gen/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png
create mode 100644 src-tauri/gen/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
create mode 100644 src-tauri/gen/android/app/src/main/res/values-night/themes.xml
create mode 100644 src-tauri/gen/android/app/src/main/res/values-v31/themes.xml
create mode 100644 src-tauri/gen/android/app/src/main/res/values/colors.xml
create mode 100644 src-tauri/gen/android/app/src/main/res/values/ic_launcher_background.xml
create mode 100644 src-tauri/gen/android/app/src/main/res/values/strings.xml
create mode 100644 src-tauri/gen/android/app/src/main/res/values/themes.xml
create mode 100644 src-tauri/gen/android/app/src/main/res/xml/file_paths.xml
create mode 100644 src-tauri/gen/android/app/tauri.build.gradle.kts
create mode 100644 src-tauri/gen/android/app/tauri.properties
create mode 100644 src-tauri/gen/android/build.gradle.kts
create mode 100644 src-tauri/gen/android/buildSrc/build.gradle.kts
create mode 100644 src-tauri/gen/android/buildSrc/src/main/java/app/risuko/mobile/kotlin/BuildTask.kt
create mode 100644 src-tauri/gen/android/buildSrc/src/main/java/app/risuko/mobile/kotlin/RustPlugin.kt
create mode 100644 src-tauri/gen/android/gradle.properties
create mode 100644 src-tauri/gen/android/gradle/wrapper/gradle-wrapper.jar
create mode 100644 src-tauri/gen/android/gradle/wrapper/gradle-wrapper.properties
create mode 100755 src-tauri/gen/android/gradlew
create mode 100644 src-tauri/gen/android/gradlew.bat
create mode 100644 src-tauri/gen/android/settings.gradle
create mode 100644 src-tauri/gen/android/tauri.settings.gradle
create mode 100644 src-tauri/src/commands/android_intent.rs
create mode 100644 src-tauri/tauri.android.conf.json
create mode 100644 src/renderer/styles/android.css
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index ac9ac377..775588c4 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -13,6 +13,108 @@ env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
jobs:
+ android-release:
+ name: Release (android)
+ runs-on: ubuntu-22.04
+ steps:
+ - name: Check out Git repository
+ uses: actions/checkout@v4
+
+ - name: Setup pnpm
+ uses: pnpm/action-setup@v4
+ with:
+ run_install: false
+
+ - name: Setup Node.js
+ uses: actions/setup-node@v4
+ with:
+ node-version: 24
+ cache: pnpm
+
+ - name: Setup Java
+ uses: actions/setup-java@v4
+ with:
+ distribution: temurin
+ java-version: 17
+
+ - name: Setup Android SDK
+ uses: android-actions/setup-android@v3
+
+ - name: Install Android SDK packages
+ shell: bash
+ run: |
+ set -euo pipefail
+ sdkmanager \
+ "platforms;android-36" \
+ "build-tools;35.0.0" \
+ "ndk;27.2.12479018"
+
+ - name: Setup Rust Android targets
+ uses: dtolnay/rust-toolchain@stable
+ with:
+ targets: aarch64-linux-android,armv7-linux-androideabi,i686-linux-android,x86_64-linux-android
+
+ - name: Install dependencies
+ run: pnpm install --no-frozen-lockfile
+
+ - name: Build Android release APKs
+ run: pnpm android:build
+ env:
+ ANDROID_NDK_VERSION: 27.2.12479018
+ ANDROID_API_LEVEL: 35
+
+ - name: Sign Android release APKs
+ run: pnpm android:sign
+ env:
+ ANDROID_SIGNING_KEYSTORE_BASE64: ${{ secrets.ANDROID_SIGNING_KEYSTORE_BASE64 }}
+ ANDROID_SIGNING_KEYSTORE_PASSWORD: ${{ secrets.ANDROID_SIGNING_KEYSTORE_PASSWORD }}
+ ANDROID_SIGNING_KEY_ALIAS: ${{ secrets.ANDROID_SIGNING_KEY_ALIAS }}
+ ANDROID_SIGNING_KEY_PASSWORD: ${{ secrets.ANDROID_SIGNING_KEY_PASSWORD }}
+
+ - name: Package Android APKs with standardized names
+ shell: bash
+ run: |
+ set -euo pipefail
+ if [[ "${{ github.ref }}" == refs/tags/* ]]; then
+ version="${{ github.ref_name }}"; version="${version#v}"
+ else
+ version=$(grep -m1 '^version' src-tauri/Cargo.toml | sed 's/.*"\(.*\)"/\1/')
+ fi
+ mkdir -p dist/android
+ declare -A labels=(
+ [arm]=arm
+ [arm64]=arm64
+ [x86]=x86
+ [x86_64]=x64
+ )
+ for abi in arm arm64 x86 x86_64; do
+ src="src-tauri/gen/android/app/build/outputs/apk/${abi}/release/app-${abi}-release.apk"
+ if [[ ! -f "$src" ]]; then
+ echo "::error::Signed Android APK not found: $src"
+ find src-tauri/gen/android/app/build/outputs/apk -type f -name '*.apk' -print || true
+ exit 1
+ fi
+ dest="dist/android/Risuko_${version}_android_${labels[$abi]}.apk"
+ cp "$src" "$dest"
+ echo "packaged: $dest"
+ done
+
+ - name: Upload Android APKs to GitHub Release
+ if: startsWith(github.ref, 'refs/tags/')
+ uses: softprops/action-gh-release@v2
+ with:
+ tag_name: ${{ github.ref_name }}
+ files: dist/android/*.apk
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+
+ - name: Upload Android APK artifacts (manual builds)
+ if: ${{ !startsWith(github.ref, 'refs/tags/') }}
+ uses: actions/upload-artifact@v4
+ with:
+ name: app-android
+ path: dist/android/*.apk
+
release:
name: Release (${{ matrix.platform }})
runs-on: ${{ matrix.os }}
diff --git a/.gitignore b/.gitignore
index bff0ba71..ffdd02a0 100644
--- a/.gitignore
+++ b/.gitignore
@@ -27,6 +27,8 @@ release
# tauri
src-tauri/target
-src-tauri/gen
+src-tauri/gen/*
+!src-tauri/gen/android/
+!src-tauri/gen/android/**
*.node
\ No newline at end of file
diff --git a/package.json b/package.json
index e25c9ec2..174af41f 100644
--- a/package.json
+++ b/package.json
@@ -19,6 +19,11 @@
"build": "node scripts/build.mjs",
"dev:renderer": "vite --config vite.renderer.config.ts --mode development --port 9080",
"pack:renderer": "vite build --config vite.renderer.config.ts --mode production",
+ "android:dev": "node scripts/android-env.mjs pnpm exec tauri android dev",
+ "android:build": "node scripts/android-env.mjs pnpm exec tauri android build --apk --split-per-abi",
+ "android:build:debug": "node scripts/android-env.mjs pnpm exec tauri android build --debug --target aarch64 --apk --split-per-abi",
+ "android:build:signed": "pnpm android:build && pnpm android:sign",
+ "android:sign": "node scripts/sign-android-apks.mjs",
"fmt": "pnpx @biomejs/biome check --write --max-diagnostics none",
"typecheck": "vue-tsc --noEmit -p tsconfig.json",
"knip": "pnpx knip --fix",
diff --git a/packages/risuko-app/package.json b/packages/risuko-app/package.json
index 04dfbe46..ff1864e2 100644
--- a/packages/risuko-app/package.json
+++ b/packages/risuko-app/package.json
@@ -1,28 +1,28 @@
{
- "name": "@risuko/app",
- "version": "0.3.3",
- "description": "Risuko download manager — launches the desktop app, downloading it from GitHub Releases on first run",
- "license": "MIT",
- "repository": {
- "type": "git",
- "url": "git+https://github.com/YueMiyuki/Risuko.git"
- },
- "keywords": [
- "download",
- "torrent",
- "bittorrent",
- "ed2k",
- "m3u8",
- "ftp",
- "download-manager"
- ],
- "bin": {
- "risuko-app": "bin.js"
- },
- "files": [
- "bin.js"
- ],
- "engines": {
- "node": ">=22.0.0"
- }
+ "name": "@risuko/app",
+ "version": "0.3.3",
+ "description": "Risuko download manager — launches the desktop app, downloading it from GitHub Releases on first run",
+ "license": "MIT",
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/YueMiyuki/Risuko.git"
+ },
+ "keywords": [
+ "download",
+ "torrent",
+ "bittorrent",
+ "ed2k",
+ "m3u8",
+ "ftp",
+ "download-manager"
+ ],
+ "bin": {
+ "risuko-app": "bin.js"
+ },
+ "files": [
+ "bin.js"
+ ],
+ "engines": {
+ "node": ">=22.0.0"
+ }
}
diff --git a/packages/risuko-cli/npm/darwin-arm64/package.json b/packages/risuko-cli/npm/darwin-arm64/package.json
index d18d5d19..70252588 100644
--- a/packages/risuko-cli/npm/darwin-arm64/package.json
+++ b/packages/risuko-cli/npm/darwin-arm64/package.json
@@ -1,20 +1,20 @@
{
- "name": "@risuko/cli-darwin-arm64",
- "version": "0.3.3",
- "description": "Risuko CLI binary for macOS ARM64",
- "repository": {
- "type": "git",
- "url": "git+https://github.com/YueMiyuki/Risuko.git",
- "directory": "packages/risuko-cli/npm/darwin-arm64"
- },
- "license": "MIT",
- "os": [
- "darwin"
- ],
- "cpu": [
- "arm64"
- ],
- "files": [
- "risuko"
- ]
+ "name": "@risuko/cli-darwin-arm64",
+ "version": "0.3.3",
+ "description": "Risuko CLI binary for macOS ARM64",
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/YueMiyuki/Risuko.git",
+ "directory": "packages/risuko-cli/npm/darwin-arm64"
+ },
+ "license": "MIT",
+ "os": [
+ "darwin"
+ ],
+ "cpu": [
+ "arm64"
+ ],
+ "files": [
+ "risuko"
+ ]
}
diff --git a/packages/risuko-cli/npm/darwin-x64/package.json b/packages/risuko-cli/npm/darwin-x64/package.json
index 32438324..8e482358 100644
--- a/packages/risuko-cli/npm/darwin-x64/package.json
+++ b/packages/risuko-cli/npm/darwin-x64/package.json
@@ -1,20 +1,20 @@
{
- "name": "@risuko/cli-darwin-x64",
- "version": "0.3.3",
- "description": "Risuko CLI binary for macOS x64",
- "repository": {
- "type": "git",
- "url": "git+https://github.com/YueMiyuki/Risuko.git",
- "directory": "packages/risuko-cli/npm/darwin-x64"
- },
- "license": "MIT",
- "os": [
- "darwin"
- ],
- "cpu": [
- "x64"
- ],
- "files": [
- "risuko"
- ]
+ "name": "@risuko/cli-darwin-x64",
+ "version": "0.3.3",
+ "description": "Risuko CLI binary for macOS x64",
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/YueMiyuki/Risuko.git",
+ "directory": "packages/risuko-cli/npm/darwin-x64"
+ },
+ "license": "MIT",
+ "os": [
+ "darwin"
+ ],
+ "cpu": [
+ "x64"
+ ],
+ "files": [
+ "risuko"
+ ]
}
diff --git a/packages/risuko-cli/npm/linux-arm64-gnu/package.json b/packages/risuko-cli/npm/linux-arm64-gnu/package.json
index 8fa156ed..9a50fd09 100644
--- a/packages/risuko-cli/npm/linux-arm64-gnu/package.json
+++ b/packages/risuko-cli/npm/linux-arm64-gnu/package.json
@@ -1,20 +1,20 @@
{
- "name": "@risuko/cli-linux-arm64-gnu",
- "version": "0.3.3",
- "description": "Risuko CLI binary for Linux ARM64",
- "repository": {
- "type": "git",
- "url": "git+https://github.com/YueMiyuki/Risuko.git",
- "directory": "packages/risuko-cli/npm/linux-arm64-gnu"
- },
- "license": "MIT",
- "os": [
- "linux"
- ],
- "cpu": [
- "arm64"
- ],
- "files": [
- "risuko"
- ]
+ "name": "@risuko/cli-linux-arm64-gnu",
+ "version": "0.3.3",
+ "description": "Risuko CLI binary for Linux ARM64",
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/YueMiyuki/Risuko.git",
+ "directory": "packages/risuko-cli/npm/linux-arm64-gnu"
+ },
+ "license": "MIT",
+ "os": [
+ "linux"
+ ],
+ "cpu": [
+ "arm64"
+ ],
+ "files": [
+ "risuko"
+ ]
}
diff --git a/packages/risuko-cli/npm/linux-x64-gnu/package.json b/packages/risuko-cli/npm/linux-x64-gnu/package.json
index 48931c4c..3e899e0b 100644
--- a/packages/risuko-cli/npm/linux-x64-gnu/package.json
+++ b/packages/risuko-cli/npm/linux-x64-gnu/package.json
@@ -1,20 +1,20 @@
{
- "name": "@risuko/cli-linux-x64-gnu",
- "version": "0.3.3",
- "description": "Risuko CLI binary for Linux x64",
- "repository": {
- "type": "git",
- "url": "git+https://github.com/YueMiyuki/Risuko.git",
- "directory": "packages/risuko-cli/npm/linux-x64-gnu"
- },
- "license": "MIT",
- "os": [
- "linux"
- ],
- "cpu": [
- "x64"
- ],
- "files": [
- "risuko"
- ]
+ "name": "@risuko/cli-linux-x64-gnu",
+ "version": "0.3.3",
+ "description": "Risuko CLI binary for Linux x64",
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/YueMiyuki/Risuko.git",
+ "directory": "packages/risuko-cli/npm/linux-x64-gnu"
+ },
+ "license": "MIT",
+ "os": [
+ "linux"
+ ],
+ "cpu": [
+ "x64"
+ ],
+ "files": [
+ "risuko"
+ ]
}
diff --git a/packages/risuko-cli/npm/win32-arm64-msvc/package.json b/packages/risuko-cli/npm/win32-arm64-msvc/package.json
index 722661e2..0f6645d7 100644
--- a/packages/risuko-cli/npm/win32-arm64-msvc/package.json
+++ b/packages/risuko-cli/npm/win32-arm64-msvc/package.json
@@ -1,20 +1,20 @@
{
- "name": "@risuko/cli-win32-arm64-msvc",
- "version": "0.3.3",
- "description": "Risuko CLI binary for Windows ARM64",
- "repository": {
- "type": "git",
- "url": "git+https://github.com/YueMiyuki/Risuko.git",
- "directory": "packages/risuko-cli/npm/win32-arm64-msvc"
- },
- "license": "MIT",
- "os": [
- "win32"
- ],
- "cpu": [
- "arm64"
- ],
- "files": [
- "risuko.exe"
- ]
+ "name": "@risuko/cli-win32-arm64-msvc",
+ "version": "0.3.3",
+ "description": "Risuko CLI binary for Windows ARM64",
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/YueMiyuki/Risuko.git",
+ "directory": "packages/risuko-cli/npm/win32-arm64-msvc"
+ },
+ "license": "MIT",
+ "os": [
+ "win32"
+ ],
+ "cpu": [
+ "arm64"
+ ],
+ "files": [
+ "risuko.exe"
+ ]
}
diff --git a/packages/risuko-cli/npm/win32-x64-msvc/package.json b/packages/risuko-cli/npm/win32-x64-msvc/package.json
index 3419cb77..9a3666be 100644
--- a/packages/risuko-cli/npm/win32-x64-msvc/package.json
+++ b/packages/risuko-cli/npm/win32-x64-msvc/package.json
@@ -1,20 +1,20 @@
{
- "name": "@risuko/cli-win32-x64-msvc",
- "version": "0.3.3",
- "description": "Risuko CLI binary for Windows x64",
- "repository": {
- "type": "git",
- "url": "git+https://github.com/YueMiyuki/Risuko.git",
- "directory": "packages/risuko-cli/npm/win32-x64-msvc"
- },
- "license": "MIT",
- "os": [
- "win32"
- ],
- "cpu": [
- "x64"
- ],
- "files": [
- "risuko.exe"
- ]
+ "name": "@risuko/cli-win32-x64-msvc",
+ "version": "0.3.3",
+ "description": "Risuko CLI binary for Windows x64",
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/YueMiyuki/Risuko.git",
+ "directory": "packages/risuko-cli/npm/win32-x64-msvc"
+ },
+ "license": "MIT",
+ "os": [
+ "win32"
+ ],
+ "cpu": [
+ "x64"
+ ],
+ "files": [
+ "risuko.exe"
+ ]
}
diff --git a/packages/risuko-cli/package.json b/packages/risuko-cli/package.json
index 6e2dbaec..6a288076 100644
--- a/packages/risuko-cli/package.json
+++ b/packages/risuko-cli/package.json
@@ -1,37 +1,37 @@
{
- "name": "@risuko/cli",
- "version": "0.3.3",
- "description": "Risuko download engine CLI — multi-protocol downloads (HTTP, BitTorrent, ED2K, M3U8, FTP/SFTP)",
- "license": "MIT",
- "repository": {
- "type": "git",
- "url": "git+https://github.com/YueMiyuki/Risuko.git"
- },
- "keywords": [
- "download",
- "torrent",
- "bittorrent",
- "ed2k",
- "m3u8",
- "ftp",
- "cli",
- "download-manager"
- ],
- "bin": {
- "risuko": "bin.js"
- },
- "files": [
- "bin.js"
- ],
- "optionalDependencies": {
- "@risuko/cli-darwin-arm64": "0.3.3",
- "@risuko/cli-darwin-x64": "0.3.3",
- "@risuko/cli-linux-arm64-gnu": "0.3.3",
- "@risuko/cli-linux-x64-gnu": "0.3.3",
- "@risuko/cli-win32-arm64-msvc": "0.3.3",
- "@risuko/cli-win32-x64-msvc": "0.3.3"
- },
- "engines": {
- "node": ">= 18"
- }
+ "name": "@risuko/cli",
+ "version": "0.3.3",
+ "description": "Risuko download engine CLI — multi-protocol downloads (HTTP, BitTorrent, ED2K, M3U8, FTP/SFTP)",
+ "license": "MIT",
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/YueMiyuki/Risuko.git"
+ },
+ "keywords": [
+ "download",
+ "torrent",
+ "bittorrent",
+ "ed2k",
+ "m3u8",
+ "ftp",
+ "cli",
+ "download-manager"
+ ],
+ "bin": {
+ "risuko": "bin.js"
+ },
+ "files": [
+ "bin.js"
+ ],
+ "optionalDependencies": {
+ "@risuko/cli-darwin-arm64": "0.3.3",
+ "@risuko/cli-darwin-x64": "0.3.3",
+ "@risuko/cli-linux-arm64-gnu": "0.3.3",
+ "@risuko/cli-linux-x64-gnu": "0.3.3",
+ "@risuko/cli-win32-arm64-msvc": "0.3.3",
+ "@risuko/cli-win32-x64-msvc": "0.3.3"
+ },
+ "engines": {
+ "node": ">= 18"
+ }
}
diff --git a/packages/risuko-js/npm/darwin-arm64/package.json b/packages/risuko-js/npm/darwin-arm64/package.json
index 68eac77b..eed1ab35 100644
--- a/packages/risuko-js/npm/darwin-arm64/package.json
+++ b/packages/risuko-js/npm/darwin-arm64/package.json
@@ -1,21 +1,21 @@
{
- "name": "@risuko/js-darwin-arm64",
- "version": "0.3.3",
- "description": "Risuko JS native module for macOS ARM64",
- "repository": {
- "type": "git",
- "url": "git+https://github.com/YueMiyuki/Risuko.git",
- "directory": "packages/risuko-js/npm/darwin-arm64"
- },
- "license": "MIT",
- "os": [
- "darwin"
- ],
- "cpu": [
- "arm64"
- ],
- "main": "risuko.darwin-arm64.node",
- "files": [
- "risuko.darwin-arm64.node"
- ]
+ "name": "@risuko/js-darwin-arm64",
+ "version": "0.3.3",
+ "description": "Risuko JS native module for macOS ARM64",
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/YueMiyuki/Risuko.git",
+ "directory": "packages/risuko-js/npm/darwin-arm64"
+ },
+ "license": "MIT",
+ "os": [
+ "darwin"
+ ],
+ "cpu": [
+ "arm64"
+ ],
+ "main": "risuko.darwin-arm64.node",
+ "files": [
+ "risuko.darwin-arm64.node"
+ ]
}
diff --git a/packages/risuko-js/npm/darwin-x64/package.json b/packages/risuko-js/npm/darwin-x64/package.json
index 330fdcad..115b5f37 100644
--- a/packages/risuko-js/npm/darwin-x64/package.json
+++ b/packages/risuko-js/npm/darwin-x64/package.json
@@ -1,21 +1,21 @@
{
- "name": "@risuko/js-darwin-x64",
- "version": "0.3.3",
- "description": "Risuko JS native module for macOS x64",
- "repository": {
- "type": "git",
- "url": "git+https://github.com/YueMiyuki/Risuko.git",
- "directory": "packages/risuko-js/npm/darwin-x64"
- },
- "license": "MIT",
- "os": [
- "darwin"
- ],
- "cpu": [
- "x64"
- ],
- "main": "risuko.darwin-x64.node",
- "files": [
- "risuko.darwin-x64.node"
- ]
+ "name": "@risuko/js-darwin-x64",
+ "version": "0.3.3",
+ "description": "Risuko JS native module for macOS x64",
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/YueMiyuki/Risuko.git",
+ "directory": "packages/risuko-js/npm/darwin-x64"
+ },
+ "license": "MIT",
+ "os": [
+ "darwin"
+ ],
+ "cpu": [
+ "x64"
+ ],
+ "main": "risuko.darwin-x64.node",
+ "files": [
+ "risuko.darwin-x64.node"
+ ]
}
diff --git a/packages/risuko-js/npm/linux-arm64-gnu/package.json b/packages/risuko-js/npm/linux-arm64-gnu/package.json
index 1d5f0935..e1a555d9 100644
--- a/packages/risuko-js/npm/linux-arm64-gnu/package.json
+++ b/packages/risuko-js/npm/linux-arm64-gnu/package.json
@@ -1,24 +1,24 @@
{
- "name": "@risuko/js-linux-arm64-gnu",
- "version": "0.3.3",
- "description": "Risuko JS native module for Linux ARM64",
- "repository": {
- "type": "git",
- "url": "git+https://github.com/YueMiyuki/Risuko.git",
- "directory": "packages/risuko-js/npm/linux-arm64-gnu"
- },
- "license": "MIT",
- "os": [
- "linux"
- ],
- "cpu": [
- "arm64"
- ],
- "main": "risuko.linux-arm64-gnu.node",
- "files": [
- "risuko.linux-arm64-gnu.node"
- ],
- "libc": [
- "glibc"
- ]
+ "name": "@risuko/js-linux-arm64-gnu",
+ "version": "0.3.3",
+ "description": "Risuko JS native module for Linux ARM64",
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/YueMiyuki/Risuko.git",
+ "directory": "packages/risuko-js/npm/linux-arm64-gnu"
+ },
+ "license": "MIT",
+ "os": [
+ "linux"
+ ],
+ "cpu": [
+ "arm64"
+ ],
+ "main": "risuko.linux-arm64-gnu.node",
+ "files": [
+ "risuko.linux-arm64-gnu.node"
+ ],
+ "libc": [
+ "glibc"
+ ]
}
diff --git a/packages/risuko-js/npm/linux-x64-gnu/package.json b/packages/risuko-js/npm/linux-x64-gnu/package.json
index 8a516fa4..5228b1a0 100644
--- a/packages/risuko-js/npm/linux-x64-gnu/package.json
+++ b/packages/risuko-js/npm/linux-x64-gnu/package.json
@@ -1,24 +1,24 @@
{
- "name": "@risuko/js-linux-x64-gnu",
- "version": "0.3.3",
- "description": "Risuko JS native module for Linux x64",
- "repository": {
- "type": "git",
- "url": "git+https://github.com/YueMiyuki/Risuko.git",
- "directory": "packages/risuko-js/npm/linux-x64-gnu"
- },
- "license": "MIT",
- "os": [
- "linux"
- ],
- "cpu": [
- "x64"
- ],
- "main": "risuko.linux-x64-gnu.node",
- "files": [
- "risuko.linux-x64-gnu.node"
- ],
- "libc": [
- "glibc"
- ]
+ "name": "@risuko/js-linux-x64-gnu",
+ "version": "0.3.3",
+ "description": "Risuko JS native module for Linux x64",
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/YueMiyuki/Risuko.git",
+ "directory": "packages/risuko-js/npm/linux-x64-gnu"
+ },
+ "license": "MIT",
+ "os": [
+ "linux"
+ ],
+ "cpu": [
+ "x64"
+ ],
+ "main": "risuko.linux-x64-gnu.node",
+ "files": [
+ "risuko.linux-x64-gnu.node"
+ ],
+ "libc": [
+ "glibc"
+ ]
}
diff --git a/packages/risuko-js/npm/win32-arm64-msvc/package.json b/packages/risuko-js/npm/win32-arm64-msvc/package.json
index 17c36271..10e450c2 100644
--- a/packages/risuko-js/npm/win32-arm64-msvc/package.json
+++ b/packages/risuko-js/npm/win32-arm64-msvc/package.json
@@ -1,21 +1,21 @@
{
- "name": "@risuko/js-win32-arm64-msvc",
- "version": "0.3.3",
- "description": "Risuko JS native module for Windows ARM64",
- "repository": {
- "type": "git",
- "url": "git+https://github.com/YueMiyuki/Risuko.git",
- "directory": "packages/risuko-js/npm/win32-arm64-msvc"
- },
- "license": "MIT",
- "os": [
- "win32"
- ],
- "cpu": [
- "arm64"
- ],
- "main": "risuko.win32-arm64-msvc.node",
- "files": [
- "risuko.win32-arm64-msvc.node"
- ]
+ "name": "@risuko/js-win32-arm64-msvc",
+ "version": "0.3.3",
+ "description": "Risuko JS native module for Windows ARM64",
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/YueMiyuki/Risuko.git",
+ "directory": "packages/risuko-js/npm/win32-arm64-msvc"
+ },
+ "license": "MIT",
+ "os": [
+ "win32"
+ ],
+ "cpu": [
+ "arm64"
+ ],
+ "main": "risuko.win32-arm64-msvc.node",
+ "files": [
+ "risuko.win32-arm64-msvc.node"
+ ]
}
diff --git a/packages/risuko-js/npm/win32-x64-msvc/package.json b/packages/risuko-js/npm/win32-x64-msvc/package.json
index c0eac006..d15117a3 100644
--- a/packages/risuko-js/npm/win32-x64-msvc/package.json
+++ b/packages/risuko-js/npm/win32-x64-msvc/package.json
@@ -1,21 +1,21 @@
{
- "name": "@risuko/js-win32-x64-msvc",
- "version": "0.3.3",
- "description": "Risuko JS native module for Windows x64",
- "repository": {
- "type": "git",
- "url": "git+https://github.com/YueMiyuki/Risuko.git",
- "directory": "packages/risuko-js/npm/win32-x64-msvc"
- },
- "license": "MIT",
- "os": [
- "win32"
- ],
- "cpu": [
- "x64"
- ],
- "main": "risuko.win32-x64-msvc.node",
- "files": [
- "risuko.win32-x64-msvc.node"
- ]
+ "name": "@risuko/js-win32-x64-msvc",
+ "version": "0.3.3",
+ "description": "Risuko JS native module for Windows x64",
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/YueMiyuki/Risuko.git",
+ "directory": "packages/risuko-js/npm/win32-x64-msvc"
+ },
+ "license": "MIT",
+ "os": [
+ "win32"
+ ],
+ "cpu": [
+ "x64"
+ ],
+ "main": "risuko.win32-x64-msvc.node",
+ "files": [
+ "risuko.win32-x64-msvc.node"
+ ]
}
diff --git a/packages/risuko-js/package.json b/packages/risuko-js/package.json
index 0e2f72a5..686f6e29 100644
--- a/packages/risuko-js/package.json
+++ b/packages/risuko-js/package.json
@@ -1,47 +1,47 @@
{
- "name": "@risuko/risuko-js",
- "version": "0.3.3",
- "description": "Risuko download engine — Node.js native bindings for multi-protocol downloads (HTTP, BitTorrent, ED2K, M3U8, FTP/SFTP)",
- "main": "index.js",
- "types": "index.d.ts",
- "license": "MIT",
- "repository": {
- "type": "git",
- "url": "git+https://github.com/YueMiyuki/Risuko.git"
- },
- "keywords": [
- "download",
- "torrent",
- "bittorrent",
- "ed2k",
- "m3u8",
- "ftp",
- "download-manager"
- ],
- "files": [
- "index.js",
- "index.d.ts"
- ],
- "napi": {
- "name": "risuko",
- "triples": {
- "defaults": true,
- "additional": [
- "aarch64-apple-darwin",
- "aarch64-unknown-linux-gnu",
- "aarch64-pc-windows-msvc"
- ]
- }
- },
- "optionalDependencies": {
- "@risuko/js-darwin-arm64": "0.3.3",
- "@risuko/js-darwin-x64": "0.3.3",
- "@risuko/js-linux-x64-gnu": "0.3.3",
- "@risuko/js-linux-arm64-gnu": "0.3.3",
- "@risuko/js-win32-x64-msvc": "0.3.3",
- "@risuko/js-win32-arm64-msvc": "0.3.3"
- },
- "engines": {
- "node": ">= 18"
- }
+ "name": "@risuko/risuko-js",
+ "version": "0.3.3",
+ "description": "Risuko download engine — Node.js native bindings for multi-protocol downloads (HTTP, BitTorrent, ED2K, M3U8, FTP/SFTP)",
+ "main": "index.js",
+ "types": "index.d.ts",
+ "license": "MIT",
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/YueMiyuki/Risuko.git"
+ },
+ "keywords": [
+ "download",
+ "torrent",
+ "bittorrent",
+ "ed2k",
+ "m3u8",
+ "ftp",
+ "download-manager"
+ ],
+ "files": [
+ "index.js",
+ "index.d.ts"
+ ],
+ "napi": {
+ "name": "risuko",
+ "triples": {
+ "defaults": true,
+ "additional": [
+ "aarch64-apple-darwin",
+ "aarch64-unknown-linux-gnu",
+ "aarch64-pc-windows-msvc"
+ ]
+ }
+ },
+ "optionalDependencies": {
+ "@risuko/js-darwin-arm64": "0.3.3",
+ "@risuko/js-darwin-x64": "0.3.3",
+ "@risuko/js-linux-x64-gnu": "0.3.3",
+ "@risuko/js-linux-arm64-gnu": "0.3.3",
+ "@risuko/js-win32-x64-msvc": "0.3.3",
+ "@risuko/js-win32-arm64-msvc": "0.3.3"
+ },
+ "engines": {
+ "node": ">= 18"
+ }
}
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 67924e9d..cf8dd90c 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -132,9 +132,47 @@ importers:
packages/risuko-app: {}
- packages/risuko-cli: {}
-
- packages/risuko-js: {}
+ packages/risuko-cli:
+ optionalDependencies:
+ '@risuko/cli-darwin-arm64':
+ specifier: 0.3.3
+ version: 0.3.3
+ '@risuko/cli-darwin-x64':
+ specifier: 0.3.3
+ version: 0.3.3
+ '@risuko/cli-linux-arm64-gnu':
+ specifier: 0.3.3
+ version: 0.3.3
+ '@risuko/cli-linux-x64-gnu':
+ specifier: 0.3.3
+ version: 0.3.3
+ '@risuko/cli-win32-arm64-msvc':
+ specifier: 0.3.3
+ version: 0.3.3
+ '@risuko/cli-win32-x64-msvc':
+ specifier: 0.3.3
+ version: 0.3.3
+
+ packages/risuko-js:
+ optionalDependencies:
+ '@risuko/js-darwin-arm64':
+ specifier: 0.3.3
+ version: 0.3.3
+ '@risuko/js-darwin-x64':
+ specifier: 0.3.3
+ version: 0.3.3
+ '@risuko/js-linux-arm64-gnu':
+ specifier: 0.3.3
+ version: 0.3.3
+ '@risuko/js-linux-x64-gnu':
+ specifier: 0.3.3
+ version: 0.3.3
+ '@risuko/js-win32-arm64-msvc':
+ specifier: 0.3.3
+ version: 0.3.3
+ '@risuko/js-win32-x64-msvc':
+ specifier: 0.3.3
+ version: 0.3.3
packages:
@@ -327,6 +365,68 @@ packages:
resolution: {integrity: sha512-tmmZ3lQxAe/k/+rNnXQRawJ4NjxO2hqiOLTHvWchtGZULp4RyFeh6aU4XdOYBFe2KE1oShQTv4AblOs2iOrNnQ==}
engines: {node: '>= 10.0.0'}
+ '@risuko/cli-darwin-arm64@0.3.3':
+ resolution: {integrity: sha512-KRiBVMQF8fXZod9MfHtMl0l45i9aEw55jMwoccKwxrZNRGO00dt4qKVWys4h0M8AKqKfDeA/ZB5ewmPm/fdPqg==}
+ cpu: [arm64]
+ os: [darwin]
+
+ '@risuko/cli-darwin-x64@0.3.3':
+ resolution: {integrity: sha512-FsO+k13FLcYtpJEB9TD96EYUoQo9M7g0SaE08pPUAtAQnCiN8TIXzw71A1P+IwvHSBtY2UgVkXG6x1d8nq3OEQ==}
+ cpu: [x64]
+ os: [darwin]
+
+ '@risuko/cli-linux-arm64-gnu@0.3.3':
+ resolution: {integrity: sha512-1gjMDUq/iCyT3tu3AIIX0+6XvnD8IW61NL7J4mWxO6qz9j4u3oVHw8kFwgcIDiA3VHGoEh9mC+VpqaddbKOVxA==}
+ cpu: [arm64]
+ os: [linux]
+
+ '@risuko/cli-linux-x64-gnu@0.3.3':
+ resolution: {integrity: sha512-fEMKsr7Grv8Swsz0HkNlZ9fIJBd5lA5/lWMa7XidEUb4ORwND0MAw3lMy+HTa41Cvnvs0tdFwdIosyxHJtwZnA==}
+ cpu: [x64]
+ os: [linux]
+
+ '@risuko/cli-win32-arm64-msvc@0.3.3':
+ resolution: {integrity: sha512-b2kA828hnj4dZimR7ra7sOOdHZV2zGDxgYV1JeQ9WEbDg16FY+Wx8JnMYQogE1CTgUtJzvT2vOl5q2Q5Xl98rA==}
+ cpu: [arm64]
+ os: [win32]
+
+ '@risuko/cli-win32-x64-msvc@0.3.3':
+ resolution: {integrity: sha512-svu0yH1g1Iv+CfgOKXtY42L8mtUB1Z4f7s6phDM5ip6DMSuhqj89u9IIyGs1xDpezHtXf63hUF+g7MDfVYYwvg==}
+ cpu: [x64]
+ os: [win32]
+
+ '@risuko/js-darwin-arm64@0.3.3':
+ resolution: {integrity: sha512-8VRdXP3jI1mDy7LOdHKgE+6Hims+xBkFPEtl3yXIkJd+0phEI4CUpz2CmhLmtciCPrp67NVauZARZYLiqvmY9A==}
+ cpu: [arm64]
+ os: [darwin]
+
+ '@risuko/js-darwin-x64@0.3.3':
+ resolution: {integrity: sha512-UtlkpKQmezVuOvGHa4raU3Z80lH3Bo7BSN8BiGdaPrI0tHyzbn2BsoD1h4OHXdyWcOTxIallCKoBYWTzARwSVQ==}
+ cpu: [x64]
+ os: [darwin]
+
+ '@risuko/js-linux-arm64-gnu@0.3.3':
+ resolution: {integrity: sha512-wDVGIDJdRYy52osKbXZfK9dFi6yFgggkFAQOJIuR23rEG9t+l1IsqcrMXKZ6hekqlBPg4BPDaqwQgbV8GYZBfA==}
+ cpu: [arm64]
+ os: [linux]
+ libc: [glibc]
+
+ '@risuko/js-linux-x64-gnu@0.3.3':
+ resolution: {integrity: sha512-0ezO72GJxnc9+wbVmRbTbCkBhSDXwoOf1dYCssjBNlhdMT5YGEt+HvPiwIkshO7g4UJuNw5E74VzUcV0cDxNLw==}
+ cpu: [x64]
+ os: [linux]
+ libc: [glibc]
+
+ '@risuko/js-win32-arm64-msvc@0.3.3':
+ resolution: {integrity: sha512-cED2sDph96qjw4z12tMzhu5RdSmMjwIf63o+LfrthVqLp2sWdRowxiCyClyis4IJDLNqglCGCUpzCxzD1pqqPA==}
+ cpu: [arm64]
+ os: [win32]
+
+ '@risuko/js-win32-x64-msvc@0.3.3':
+ resolution: {integrity: sha512-IVMJDaw8Lqcq22QxUJtwJ5JHqYEe6Oyfl3uA1lTNQjoPQ0IaZjN7Z4b4RJJYy9QR59HR/ydAjPu42kTM9RZ6gQ==}
+ cpu: [x64]
+ os: [win32]
+
'@rolldown/binding-android-arm64@1.0.2':
resolution: {integrity: sha512-ZS4D1JPGn/MYQN/SYDWftIE/nVsM8j/AFOYEzAoOE2O3NktQOZru+/vYXGbR/qtdLdIfGCP0lcoJiYVzsEz+iQ==}
engines: {node: ^20.19.0 || >=22.12.0}
@@ -1691,6 +1791,42 @@ snapshots:
'@parcel/watcher-win32-x64': 2.5.6
optional: true
+ '@risuko/cli-darwin-arm64@0.3.3':
+ optional: true
+
+ '@risuko/cli-darwin-x64@0.3.3':
+ optional: true
+
+ '@risuko/cli-linux-arm64-gnu@0.3.3':
+ optional: true
+
+ '@risuko/cli-linux-x64-gnu@0.3.3':
+ optional: true
+
+ '@risuko/cli-win32-arm64-msvc@0.3.3':
+ optional: true
+
+ '@risuko/cli-win32-x64-msvc@0.3.3':
+ optional: true
+
+ '@risuko/js-darwin-arm64@0.3.3':
+ optional: true
+
+ '@risuko/js-darwin-x64@0.3.3':
+ optional: true
+
+ '@risuko/js-linux-arm64-gnu@0.3.3':
+ optional: true
+
+ '@risuko/js-linux-x64-gnu@0.3.3':
+ optional: true
+
+ '@risuko/js-win32-arm64-msvc@0.3.3':
+ optional: true
+
+ '@risuko/js-win32-x64-msvc@0.3.3':
+ optional: true
+
'@rolldown/binding-android-arm64@1.0.2':
optional: true
diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml
index 73e86376..868d375a 100644
--- a/pnpm-workspace.yaml
+++ b/pnpm-workspace.yaml
@@ -7,3 +7,16 @@ allowBuilds:
esbuild: true
unrs-resolver: true
vue-demi: true
+minimumReleaseAgeExclude:
+ - '@risuko/cli-darwin-arm64@0.3.3'
+ - '@risuko/cli-darwin-x64@0.3.3'
+ - '@risuko/cli-linux-arm64-gnu@0.3.3'
+ - '@risuko/cli-linux-x64-gnu@0.3.3'
+ - '@risuko/cli-win32-arm64-msvc@0.3.3'
+ - '@risuko/cli-win32-x64-msvc@0.3.3'
+ - '@risuko/js-darwin-arm64@0.3.3'
+ - '@risuko/js-darwin-x64@0.3.3'
+ - '@risuko/js-linux-arm64-gnu@0.3.3'
+ - '@risuko/js-linux-x64-gnu@0.3.3'
+ - '@risuko/js-win32-arm64-msvc@0.3.3'
+ - '@risuko/js-win32-x64-msvc@0.3.3'
diff --git a/scripts/android-env.mjs b/scripts/android-env.mjs
new file mode 100644
index 00000000..c066af59
--- /dev/null
+++ b/scripts/android-env.mjs
@@ -0,0 +1,112 @@
+#!/usr/bin/env node
+import { spawn } from "node:child_process";
+import { existsSync } from "node:fs";
+import { join } from "node:path";
+
+const sdkRoot =
+ process.env.ANDROID_HOME ||
+ process.env.ANDROID_SDK_ROOT ||
+ "/opt/homebrew/share/android-commandlinetools";
+const ndkVersion = process.env.ANDROID_NDK_VERSION || "27.2.12479018";
+const ndkHome = process.env.ANDROID_NDK_HOME || join(sdkRoot, "ndk", ndkVersion);
+const prebuiltRoot = join(ndkHome, "toolchains", "llvm", "prebuilt");
+const prebuiltHostCandidates = [
+ process.platform === "darwin" && process.arch === "arm64" ? "darwin-arm64" : "",
+ process.platform === "darwin" ? "darwin-x86_64" : "",
+ process.platform === "linux" ? "linux-x86_64" : "",
+ process.platform === "win32" ? "windows-x86_64" : "",
+ "darwin-arm64",
+ "darwin-x86_64",
+ "linux-x86_64",
+ "windows-x86_64",
+].filter(Boolean);
+const prebuiltHost = prebuiltHostCandidates.find((host) =>
+ existsSync(join(prebuiltRoot, host)),
+);
+if (!prebuiltHost) {
+ console.error(`Android NDK prebuilt toolchain not found under: ${prebuiltRoot}`);
+ process.exit(1);
+}
+const llvmBin = join(prebuiltRoot, prebuiltHost, "bin");
+const api = process.env.ANDROID_API_LEVEL || "35";
+const javaHomeCandidates = [
+ process.env.ANDROID_JAVA_HOME,
+ "/opt/homebrew/opt/openjdk@17/libexec/openjdk.jdk/Contents/Home",
+ "/opt/homebrew/opt/openjdk@21/libexec/openjdk.jdk/Contents/Home",
+ process.env.JAVA_HOME,
+].filter(Boolean);
+const javaHome = javaHomeCandidates.find((path) =>
+ existsSync(join(path, "bin", "java")),
+);
+const javaBin = javaHome ? join(javaHome, "bin") : "";
+
+if (!existsSync(llvmBin)) {
+ console.error(`Android NDK LLVM toolchain not found: ${llvmBin}`);
+ process.exit(1);
+}
+
+const env = {
+ ...process.env,
+ ANDROID_HOME: sdkRoot,
+ ANDROID_SDK_ROOT: sdkRoot,
+ ANDROID_NDK_HOME: ndkHome,
+ NDK_HOME: ndkHome,
+ ...(javaHome ? { JAVA_HOME: javaHome } : {}),
+ PATH: `${javaBin ? `${javaBin}:` : ""}${llvmBin}:${process.env.PATH || ""}`,
+ CC_aarch64_linux_android: join(llvmBin, `aarch64-linux-android${api}-clang`),
+ CXX_aarch64_linux_android: join(llvmBin, `aarch64-linux-android${api}-clang++`),
+ AR_aarch64_linux_android: join(llvmBin, "llvm-ar"),
+ RANLIB_aarch64_linux_android: join(llvmBin, "llvm-ranlib"),
+ CARGO_TARGET_AARCH64_LINUX_ANDROID_LINKER: join(
+ llvmBin,
+ `aarch64-linux-android${api}-clang`,
+ ),
+ CC_armv7_linux_androideabi: join(llvmBin, `armv7a-linux-androideabi${api}-clang`),
+ CXX_armv7_linux_androideabi: join(llvmBin, `armv7a-linux-androideabi${api}-clang++`),
+ AR_armv7_linux_androideabi: join(llvmBin, "llvm-ar"),
+ RANLIB_armv7_linux_androideabi: join(llvmBin, "llvm-ranlib"),
+ CARGO_TARGET_ARMV7_LINUX_ANDROIDEABI_LINKER: join(
+ llvmBin,
+ `armv7a-linux-androideabi${api}-clang`,
+ ),
+ CC_i686_linux_android: join(llvmBin, `i686-linux-android${api}-clang`),
+ CXX_i686_linux_android: join(llvmBin, `i686-linux-android${api}-clang++`),
+ AR_i686_linux_android: join(llvmBin, "llvm-ar"),
+ RANLIB_i686_linux_android: join(llvmBin, "llvm-ranlib"),
+ CARGO_TARGET_I686_LINUX_ANDROID_LINKER: join(
+ llvmBin,
+ `i686-linux-android${api}-clang`,
+ ),
+ CC_x86_64_linux_android: join(llvmBin, `x86_64-linux-android${api}-clang`),
+ CXX_x86_64_linux_android: join(llvmBin, `x86_64-linux-android${api}-clang++`),
+ AR_x86_64_linux_android: join(llvmBin, "llvm-ar"),
+ RANLIB_x86_64_linux_android: join(llvmBin, "llvm-ranlib"),
+ CARGO_TARGET_X86_64_LINUX_ANDROID_LINKER: join(
+ llvmBin,
+ `x86_64-linux-android${api}-clang`,
+ ),
+};
+
+const args = process.argv.slice(2);
+if (args.length === 0) {
+ console.log(`ANDROID_HOME=${env.ANDROID_HOME}`);
+ console.log(`ANDROID_NDK_HOME=${env.ANDROID_NDK_HOME}`);
+ if (env.JAVA_HOME) {
+ console.log(`JAVA_HOME=${env.JAVA_HOME}`);
+ }
+ console.log(`PATH prefix=${llvmBin}`);
+ process.exit(0);
+}
+
+const child = spawn(args[0], args.slice(1), {
+ stdio: "inherit",
+ env,
+});
+
+child.on("exit", (code, signal) => {
+ if (signal) {
+ process.kill(process.pid, signal);
+ return;
+ }
+ process.exit(code ?? 1);
+});
diff --git a/scripts/sign-android-apks.mjs b/scripts/sign-android-apks.mjs
new file mode 100644
index 00000000..db6e3e25
--- /dev/null
+++ b/scripts/sign-android-apks.mjs
@@ -0,0 +1,161 @@
+#!/usr/bin/env node
+import { spawnSync } from "node:child_process";
+import { existsSync, mkdtempSync, readdirSync, rmSync, writeFileSync } from "node:fs";
+import { tmpdir } from "node:os";
+import { basename, dirname, join, resolve } from "node:path";
+
+const projectRoot = resolve(new URL("..", import.meta.url).pathname);
+
+const envFile = resolve(projectRoot, ".env");
+if (existsSync(envFile)) {
+ process.loadEnvFile(envFile);
+}
+
+const apkRoot = resolve(
+ projectRoot,
+ "src-tauri/gen/android/app/build/outputs/apk",
+);
+
+function fail(message) {
+ console.error(message);
+ process.exit(1);
+}
+
+function run(command, args, options = {}) {
+ const result = spawnSync(command, args, {
+ stdio: "inherit",
+ env: process.env,
+ ...options,
+ });
+ if (result.status !== 0) {
+ fail(`${command} failed with exit code ${result.status}`);
+ }
+}
+
+function versionParts(version) {
+ return version.split(".").map((part) => Number(part));
+}
+
+function compareVersions(a, b) {
+ const left = versionParts(a);
+ const right = versionParts(b);
+ const length = Math.max(left.length, right.length);
+ for (let index = 0; index < length; index += 1) {
+ const diff = (left[index] || 0) - (right[index] || 0);
+ if (diff !== 0) {
+ return diff;
+ }
+ }
+ return 0;
+}
+
+function findBuildTool(name) {
+ const explicit = process.env[`${name.toUpperCase()}_PATH`];
+ if (explicit) {
+ return explicit;
+ }
+ const sdkRoot = process.env.ANDROID_HOME || process.env.ANDROID_SDK_ROOT;
+ if (!sdkRoot) {
+ fail("ANDROID_HOME or ANDROID_SDK_ROOT is required to locate Android build tools");
+ }
+ const buildToolsRoot = join(sdkRoot, "build-tools");
+ if (!existsSync(buildToolsRoot)) {
+ fail(`Android build-tools directory not found: ${buildToolsRoot}`);
+ }
+ const versions = readdirSync(buildToolsRoot)
+ .filter((entry) => existsSync(join(buildToolsRoot, entry, name)))
+ .sort(compareVersions)
+ .reverse();
+ if (versions.length === 0) {
+ fail(`${name} not found under ${buildToolsRoot}`);
+ }
+ return join(buildToolsRoot, versions[0], name);
+}
+
+function findUnsignedApks(root) {
+ if (!existsSync(root)) {
+ fail(`APK output directory not found: ${root}`);
+ }
+ const results = [];
+ const stack = [root];
+ while (stack.length > 0) {
+ const current = stack.pop();
+ for (const entry of readdirSync(current, { withFileTypes: true })) {
+ const fullPath = join(current, entry.name);
+ if (entry.isDirectory()) {
+ stack.push(fullPath);
+ } else if (entry.isFile() && entry.name.endsWith("-unsigned.apk")) {
+ results.push(fullPath);
+ }
+ }
+ }
+ return results.sort();
+}
+
+function resolveKeystore() {
+ const keystorePath = process.env.ANDROID_SIGNING_KEYSTORE_PATH;
+ if (keystorePath) {
+ return { path: keystorePath };
+ }
+ const encoded = process.env.ANDROID_SIGNING_KEYSTORE_BASE64;
+ if (!encoded) {
+ fail(
+ "ANDROID_SIGNING_KEYSTORE_PATH or ANDROID_SIGNING_KEYSTORE_BASE64 is required",
+ );
+ }
+ const dir = mkdtempSync(join(tmpdir(), "risuko-android-signing-"));
+ const path = join(dir, "release.keystore");
+ writeFileSync(path, Buffer.from(encoded, "base64"));
+ return { path, cleanup: () => rmSync(dir, { recursive: true, force: true }) };
+}
+
+const storePassword = process.env.ANDROID_SIGNING_KEYSTORE_PASSWORD;
+const alias = process.env.ANDROID_SIGNING_KEY_ALIAS;
+if (!storePassword) {
+ fail("ANDROID_SIGNING_KEYSTORE_PASSWORD is required");
+}
+if (!alias) {
+ fail("ANDROID_SIGNING_KEY_ALIAS is required");
+}
+if (!process.env.ANDROID_SIGNING_KEY_PASSWORD) {
+ process.env.ANDROID_SIGNING_KEY_PASSWORD = storePassword;
+}
+
+const apksigner = findBuildTool("apksigner");
+const zipalign = findBuildTool("zipalign");
+const keystore = resolveKeystore();
+const unsignedApks = findUnsignedApks(apkRoot);
+
+if (unsignedApks.length === 0) {
+ fail(`No unsigned release APKs found under ${apkRoot}`);
+}
+
+try {
+ for (const unsignedApk of unsignedApks) {
+ const outputApk = unsignedApk.replace(/-unsigned\.apk$/, ".apk");
+ const alignedApk = join(
+ dirname(unsignedApk),
+ `${basename(unsignedApk, ".apk")}-aligned.apk`,
+ );
+ run(zipalign, ["-f", "-p", "4", unsignedApk, alignedApk]);
+ run(apksigner, [
+ "sign",
+ "--ks",
+ keystore.path,
+ "--ks-key-alias",
+ alias,
+ "--ks-pass",
+ "env:ANDROID_SIGNING_KEYSTORE_PASSWORD",
+ "--key-pass",
+ "env:ANDROID_SIGNING_KEY_PASSWORD",
+ "--out",
+ outputApk,
+ alignedApk,
+ ]);
+ run(apksigner, ["verify", "--verbose", "--print-certs", outputApk]);
+ rmSync(alignedApk, { force: true });
+ console.log(`signed: ${outputApk}`);
+ }
+} finally {
+ keystore.cleanup?.();
+}
\ No newline at end of file
diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock
index 577a31c1..80ef45bf 100644
--- a/src-tauri/Cargo.lock
+++ b/src-tauri/Cargo.lock
@@ -3945,6 +3945,12 @@ dependencies = [
"thiserror 1.0.69",
]
+[[package]]
+name = "ndk-context"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "27b02d87554356db9e9a873add8782d4ea6e3e58ea071a9adb9a2e8ddb884a8b"
+
[[package]]
name = "ndk-sys"
version = "0.6.0+11769913"
@@ -5491,8 +5497,10 @@ dependencies = [
"dbus-secret-service-keyring-store",
"dirs 6.0.0",
"fs4",
+ "jni",
"keyring-core",
"log",
+ "ndk-context",
"open",
"regex",
"risuko-cookies",
diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml
index 34752024..982acf65 100644
--- a/src-tauri/Cargo.toml
+++ b/src-tauri/Cargo.toml
@@ -47,7 +47,6 @@ tauri-plugin-store = "2"
tauri-plugin-deep-link = "2"
tauri-plugin-dialog = "2"
tauri-plugin-shell = "2"
-tauri-plugin-autostart = "2"
tauri-plugin-process = "2"
tauri-plugin-fs = "2"
tauri-plugin-notification = "2"
@@ -59,10 +58,7 @@ tokio = { workspace = true }
log = { workspace = true }
dirs = { workspace = true }
open = "5"
-trash = "5"
tauri-plugin-clipboard-manager = "2.3.2"
-tauri-plugin-nosleep = { git = "https://github.com/pevers/tauri-plugin-nosleep", rev = "6f6ab76ec171d075476b585282290fcf2e1c40b5" }
-tauri-plugin-single-instance = "2"
tracing = "0.1"
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
tracing-appender = "0.2"
@@ -86,4 +82,18 @@ windows-native-keyring-store = "1"
[target.'cfg(all(unix, not(any(target_os = "macos", target_os = "ios", target_os = "android"))))'.dependencies]
dbus-secret-service-keyring-store = { version = "1", features = ["crypto-rust"] }
+[target.'cfg(not(target_os = "android"))'.dependencies]
+trash = "5"
+tauri-plugin-nosleep = { git = "https://github.com/pevers/tauri-plugin-nosleep", rev = "6f6ab76ec171d075476b585282290fcf2e1c40b5" }
+tauri-plugin-autostart = "2"
+tauri-plugin-single-instance = "2"
+
+[target.'cfg(target_os = "android")'.dependencies]
+# JNI bridge so we can dispatch Android `Intent`s with explicit MIME types
+# and `Intent.createChooser`. `tauri-plugin-shell::open` only calls
+# `Intent(ACTION_VIEW, uri)` with no extras, which lets generic
+# wildcard-MIME apps like Messages intercept any content URI we send.
+jni = "0.21"
+ndk-context = "0.1"
+
[dev-dependencies]
diff --git a/src-tauri/capabilities/default.json b/src-tauri/capabilities/default.json
index 0a044f7d..b72c1047 100644
--- a/src-tauri/capabilities/default.json
+++ b/src-tauri/capabilities/default.json
@@ -25,7 +25,16 @@
"dialog:allow-open",
"dialog:allow-message",
"shell:default",
- "shell:allow-open",
+ {
+ "identifier": "shell:allow-open",
+ "allow": [
+ { "url": "file://**" },
+ { "url": "https://**" },
+ { "url": "http://**" },
+ { "url": "mailto:**" },
+ { "url": "tel:**" }
+ ]
+ },
"store:default",
"fs:default",
{
@@ -56,10 +65,7 @@
"os:default",
"notification:default",
"notification:allow-notify",
- "autostart:default",
"deep-link:default",
- "nosleep:allow-block",
- "nosleep:allow-unblock",
"clipboard-manager:default",
"clipboard-manager:allow-read-text",
"clipboard-manager:allow-write-text"
diff --git a/src-tauri/capabilities/desktop.json b/src-tauri/capabilities/desktop.json
new file mode 100644
index 00000000..5824ff77
--- /dev/null
+++ b/src-tauri/capabilities/desktop.json
@@ -0,0 +1,11 @@
+{
+ "identifier": "desktop",
+ "description": "Desktop-only capabilities for Risuko",
+ "windows": ["main"],
+ "platforms": ["macOS", "windows", "linux"],
+ "permissions": [
+ "autostart:default",
+ "nosleep:allow-block",
+ "nosleep:allow-unblock"
+ ]
+}
diff --git a/src-tauri/gen/android/.editorconfig b/src-tauri/gen/android/.editorconfig
new file mode 100644
index 00000000..ebe51d3b
--- /dev/null
+++ b/src-tauri/gen/android/.editorconfig
@@ -0,0 +1,12 @@
+# EditorConfig is awesome: https://EditorConfig.org
+
+# top-most EditorConfig file
+root = true
+
+[*]
+indent_style = space
+indent_size = 2
+end_of_line = lf
+charset = utf-8
+trim_trailing_whitespace = false
+insert_final_newline = false
\ No newline at end of file
diff --git a/src-tauri/gen/android/.gitignore b/src-tauri/gen/android/.gitignore
new file mode 100644
index 00000000..2ebfbcca
--- /dev/null
+++ b/src-tauri/gen/android/.gitignore
@@ -0,0 +1,19 @@
+*.iml
+.gradle
+/local.properties
+/.idea/caches
+/.idea/libraries
+/.idea/modules.xml
+/.idea/workspace.xml
+/.idea/navEditor.xml
+/.idea/assetWizardSettings.xml
+.DS_Store
+build
+/captures
+.externalNativeBuild
+.cxx
+local.properties
+key.properties
+keystore.properties
+
+/.tauri
\ No newline at end of file
diff --git a/src-tauri/gen/android/app/.gitignore b/src-tauri/gen/android/app/.gitignore
new file mode 100644
index 00000000..5ad6a869
--- /dev/null
+++ b/src-tauri/gen/android/app/.gitignore
@@ -0,0 +1,3 @@
+/src/main/**/generated
+/src/main/jniLibs/**/*.so
+/src/main/assets/tauri.conf.json
\ No newline at end of file
diff --git a/src-tauri/gen/android/app/build.gradle.kts b/src-tauri/gen/android/app/build.gradle.kts
new file mode 100644
index 00000000..f26cb414
--- /dev/null
+++ b/src-tauri/gen/android/app/build.gradle.kts
@@ -0,0 +1,71 @@
+import java.util.Properties
+
+plugins {
+ id("com.android.application")
+ id("org.jetbrains.kotlin.android")
+ id("rust")
+}
+
+val tauriProperties = Properties().apply {
+ val propFile = file("tauri.properties")
+ if (propFile.exists()) {
+ propFile.inputStream().use { load(it) }
+ }
+}
+
+android {
+ compileSdk = 36
+ namespace = "app.risuko.mobile"
+ defaultConfig {
+ manifestPlaceholders["usesCleartextTraffic"] = "false"
+ applicationId = "app.risuko.mobile"
+ minSdk = 24
+ targetSdk = 36
+ versionCode = tauriProperties.getProperty("tauri.android.versionCode", "1").toInt()
+ versionName = tauriProperties.getProperty("tauri.android.versionName", "1.0")
+ }
+ buildTypes {
+ getByName("debug") {
+ manifestPlaceholders["usesCleartextTraffic"] = "true"
+ isDebuggable = true
+ isJniDebuggable = true
+ isMinifyEnabled = false
+ packaging { jniLibs.keepDebugSymbols.add("*/arm64-v8a/*.so")
+ jniLibs.keepDebugSymbols.add("*/armeabi-v7a/*.so")
+ jniLibs.keepDebugSymbols.add("*/x86/*.so")
+ jniLibs.keepDebugSymbols.add("*/x86_64/*.so")
+ }
+ }
+ getByName("release") {
+ isMinifyEnabled = true
+ proguardFiles(
+ *fileTree(".") { include("**/*.pro") }
+ .plus(getDefaultProguardFile("proguard-android-optimize.txt"))
+ .toList().toTypedArray()
+ )
+ }
+ }
+ kotlinOptions {
+ jvmTarget = "1.8"
+ }
+ buildFeatures {
+ buildConfig = true
+ }
+}
+
+rust {
+ rootDirRel = "../../../"
+}
+
+dependencies {
+ implementation("androidx.webkit:webkit:1.14.0")
+ implementation("androidx.appcompat:appcompat:1.7.1")
+ implementation("androidx.activity:activity-ktx:1.10.1")
+ implementation("com.google.android.material:material:1.12.0")
+ implementation("androidx.lifecycle:lifecycle-process:2.10.0")
+ testImplementation("junit:junit:4.13.2")
+ androidTestImplementation("androidx.test.ext:junit:1.1.4")
+ androidTestImplementation("androidx.test.espresso:espresso-core:3.5.0")
+}
+
+apply(from = "tauri.build.gradle.kts")
\ No newline at end of file
diff --git a/src-tauri/gen/android/app/proguard-rules.pro b/src-tauri/gen/android/app/proguard-rules.pro
new file mode 100644
index 00000000..4d581b80
--- /dev/null
+++ b/src-tauri/gen/android/app/proguard-rules.pro
@@ -0,0 +1,24 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+-keep class app.risuko.mobile.MainActivity { *; }
+-keep class app.risuko.mobile.RisukoForegroundService { *; }
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile
\ No newline at end of file
diff --git a/src-tauri/gen/android/app/proguard-tauri.pro b/src-tauri/gen/android/app/proguard-tauri.pro
new file mode 100644
index 00000000..065e16c0
--- /dev/null
+++ b/src-tauri/gen/android/app/proguard-tauri.pro
@@ -0,0 +1,5 @@
+# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
+
+-keep class app.risuko.mobile.TauriActivity {
+ public app.tauri.plugin.PluginManager getPluginManager();
+}
diff --git a/src-tauri/gen/android/app/src/main/AndroidManifest.xml b/src-tauri/gen/android/app/src/main/AndroidManifest.xml
new file mode 100644
index 00000000..d3ef2bc2
--- /dev/null
+++ b/src-tauri/gen/android/app/src/main/AndroidManifest.xml
@@ -0,0 +1,75 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src-tauri/gen/android/app/src/main/java/app/risuko/mobile/MainActivity.kt b/src-tauri/gen/android/app/src/main/java/app/risuko/mobile/MainActivity.kt
new file mode 100644
index 00000000..33e22398
--- /dev/null
+++ b/src-tauri/gen/android/app/src/main/java/app/risuko/mobile/MainActivity.kt
@@ -0,0 +1,397 @@
+package app.risuko.mobile
+
+import android.Manifest
+import android.content.ActivityNotFoundException
+import android.content.Intent
+import android.content.pm.PackageManager
+import android.graphics.Color
+import android.net.Uri
+import android.os.Build
+import android.os.Bundle
+import android.os.Environment
+import android.provider.DocumentsContract
+import android.provider.Settings
+import android.util.Log
+import android.webkit.WebView
+import androidx.activity.OnBackPressedCallback
+import androidx.activity.SystemBarStyle
+import androidx.activity.enableEdgeToEdge
+import androidx.activity.result.contract.ActivityResultContracts
+import androidx.core.app.ActivityCompat
+import androidx.core.content.ContextCompat
+import androidx.core.view.WindowCompat
+import java.util.concurrent.CountDownLatch
+import java.util.concurrent.TimeUnit
+
+class MainActivity : TauriActivity() {
+ private var pendingDirectoryRequestId: String? = null
+ private var appWebView: RustWebView? = null
+ private var requestedNotificationPermission = false
+ private val directoryPicker = registerForActivityResult(ActivityResultContracts.OpenDocumentTree()) { uri: Uri? ->
+ val requestId = pendingDirectoryRequestId
+ pendingDirectoryRequestId = null
+ if (requestId != null) {
+ if (uri != null) {
+ try {
+ contentResolver.takePersistableUriPermission(
+ uri,
+ Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION,
+ )
+ } catch (_: Exception) {
+ }
+ }
+ nativeOnDirectoryPicked(requestId, uri?.toString())
+ }
+ }
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ current = this
+ enableEdgeToEdge()
+ super.onCreate(savedInstanceState)
+ setSystemBarsForTheme(false)
+ onBackPressedDispatcher.addCallback(this, object : OnBackPressedCallback(true) {
+ override fun handleOnBackPressed() {
+ dispatchAndroidBack()
+ }
+ })
+ }
+
+ override fun onWebViewCreate(webView: WebView) {
+ appWebView = webView as? RustWebView
+ }
+
+ override fun onDestroy() {
+ if (current === this) {
+ current = null
+ }
+ super.onDestroy()
+ }
+
+ private external fun nativeOnDirectoryPicked(requestId: String, uri: String?)
+
+ private fun hasExternalStorageAccess(): Boolean {
+ return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
+ Environment.isExternalStorageManager()
+ } else {
+ ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED
+ }
+ }
+
+ private fun requestExternalStorageAccess(): Boolean {
+ if (hasExternalStorageAccess()) {
+ return true
+ }
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
+ val packageUri = Uri.parse("package:$packageName")
+ val intent = Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION, packageUri)
+ try {
+ startActivity(intent)
+ } catch (_: Exception) {
+ startActivity(Intent(Settings.ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION))
+ }
+ } else {
+ ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE), 4737)
+ }
+ return false
+ }
+
+ private fun setSystemBarsForTheme(darkMode: Boolean) {
+ // Android 15 (API 35) deprecated the `window.statusBarColor` and
+ // `window.navigationBarColor` setters. On those releases the framework
+ // draws system bars edge-to-edge and supplies its own scrim when needed.
+ // The androidx `enableEdgeToEdge(statusBarStyle, navigationBarStyle)`
+ // overload handles both cases: it sets the scrim color explicitly on
+ // API < 29/32, and on API >= 30 it flips icon appearance via
+ // `SystemBarStyle.dark`/`light` without going near the deprecated setters
+ val navScrimLight = Color.rgb(253, 248, 255)
+ val navScrimDark = Color.rgb(20, 18, 24)
+ val statusBarStyle = if (darkMode) {
+ SystemBarStyle.dark(Color.TRANSPARENT)
+ } else {
+ SystemBarStyle.light(Color.TRANSPARENT, Color.TRANSPARENT)
+ }
+ val navigationBarStyle = if (darkMode) {
+ SystemBarStyle.dark(navScrimDark)
+ } else {
+ SystemBarStyle.light(navScrimLight, navScrimDark)
+ }
+ enableEdgeToEdge(statusBarStyle, navigationBarStyle)
+ // `SystemBarStyle` handles icon appearance on API >= 30, but on older
+ // releases the inset controller is still the documented surface for
+ // flipping light/dark icons. Setting it explicitly also catches the
+ // rare case where the activity is rebuilt after a config change with a
+ // stale appearance. The call is cheap and idempotent either way
+ WindowCompat.getInsetsController(window, window.decorView).apply {
+ isAppearanceLightStatusBars = !darkMode
+ isAppearanceLightNavigationBars = !darkMode
+ }
+ }
+
+ private fun dispatchAndroidBack() {
+ val script = "window.dispatchEvent(new CustomEvent('risuko-android-back'))"
+ appWebView?.post {
+ appWebView?.evaluateJavascript(script, null)
+ }
+ }
+
+ private fun ensureNotificationPermission() {
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
+ return
+ }
+ if (ContextCompat.checkSelfPermission(this, Manifest.permission.POST_NOTIFICATIONS) == PackageManager.PERMISSION_GRANTED) {
+ return
+ }
+ if (requestedNotificationPermission) {
+ return
+ }
+ requestedNotificationPermission = true
+ ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.POST_NOTIFICATIONS), 4738)
+ }
+
+ companion object {
+ private var current: MainActivity? = null
+
+ @JvmStatic
+ fun pickDirectory(requestId: String): Boolean {
+ val activity = current ?: return false
+ activity.runOnUiThread {
+ try {
+ activity.pendingDirectoryRequestId = requestId
+ activity.directoryPicker.launch(null)
+ } catch (_: Exception) {
+ activity.pendingDirectoryRequestId = null
+ activity.nativeOnDirectoryPicked(requestId, null)
+ }
+ }
+ return true
+ }
+
+ @JvmStatic
+ fun requestAllFilesAccess(): Boolean {
+ val activity = current ?: return false
+ if (activity.hasExternalStorageAccess()) {
+ return true
+ }
+ activity.runOnUiThread {
+ activity.requestExternalStorageAccess()
+ }
+ return false
+ }
+
+ @JvmStatic
+ fun setSystemBars(darkMode: Boolean) {
+ current?.runOnUiThread {
+ current?.setSystemBarsForTheme(darkMode)
+ }
+ }
+
+ @JvmStatic
+ fun showDownloadNotification(progress: Int, activeCount: Int, detail: String) {
+ current?.runOnUiThread {
+ val activity = current ?: return@runOnUiThread
+ activity.ensureNotificationPermission()
+ RisukoForegroundService.show(activity, progress, activeCount, detail)
+ }
+ }
+
+ @JvmStatic
+ fun hideDownloadNotification() {
+ current?.runOnUiThread {
+ val activity = current ?: return@runOnUiThread
+ RisukoForegroundService.hide(activity)
+ }
+ }
+
+ // Opens `path` in a file manager and reports the outcome back so the
+ // caller can surface a useful error string. Returns "ok" on success or
+ // a diagnostic string otherwise. We try three intent shapes because no
+ // single one works on every device:
+ // 1. ACTION_VIEW with `vnd.android.document/directory` MIME wrapped
+ // in a chooser. Files by Google and AOSP DocumentsUI both claim
+ // this
+ // 2. Same intent without the chooser, so the system can launch the
+ // default handler directly when only one app matches
+ // 3. ACTION_VIEW with the URI alone, letting the documents provider
+ // infer the MIME. Broader compatibility at the cost of pulling in
+ // generic handlers
+ // If one variant throws, we move on to the next. We only give up and
+ // report back to Rust once all three fail
+ @JvmStatic
+ fun revealFolder(path: String): String {
+ val activity = current ?: return "no_activity"
+ val resultRef = java.util.concurrent.atomic.AtomicReference("ok")
+ val latch = CountDownLatch(1)
+ activity.runOnUiThread {
+ try {
+ resultRef.set(tryRevealFolder(activity, path))
+ } catch (e: Throwable) {
+ Log.w(REVEAL_TAG, "revealFolder threw for path=$path", e)
+ resultRef.set("error: ${e.javaClass.simpleName}: ${e.message ?: "(no message)"}")
+ }
+ latch.countDown()
+ }
+ val completed = try {
+ latch.await(5, TimeUnit.SECONDS)
+ } catch (_: InterruptedException) {
+ Thread.currentThread().interrupt()
+ return "interrupted"
+ }
+ if (!completed) {
+ return "timeout"
+ }
+ return resultRef.get()
+ }
+
+ private const val REVEAL_TAG = "RisukoReveal"
+
+ private fun tryRevealFolder(activity: MainActivity, path: String): String {
+ if (path.isBlank()) {
+ return "empty_path"
+ }
+ val docId = buildExternalStorageDocId(path)
+ if (docId == null) {
+ Log.w(REVEAL_TAG, "buildExternalStorageDocId returned null for path=$path")
+ return "invalid_path:$path"
+ }
+ val authority = "com.android.externalstorage.documents"
+ val docUri = try {
+ DocumentsContract.buildDocumentUri(authority, docId)
+ } catch (e: Throwable) {
+ Log.w(REVEAL_TAG, "buildDocumentUri failed for docId=$docId", e)
+ return "uri_error:$docId"
+ }
+ val treeUri = try {
+ DocumentsContract.buildTreeDocumentUri(authority, docId)
+ } catch (e: Throwable) {
+ Log.w(REVEAL_TAG, "buildTreeDocumentUri failed for docId=$docId", e)
+ null
+ }
+ val treeDocUri = treeUri?.let { tree ->
+ try {
+ DocumentsContract.buildDocumentUriUsingTree(tree, docId)
+ } catch (e: Throwable) {
+ Log.w(REVEAL_TAG, "buildDocumentUriUsingTree failed for docId=$docId", e)
+ null
+ }
+ }
+ Log.i(REVEAL_TAG, "revealFolder path=$path docUri=$docUri treeUri=$treeUri treeDocUri=$treeDocUri")
+
+ // We deliberately do NOT add FLAG_GRANT_READ_URI_PERMISSION here.
+ // Adding it makes the system enforce that the calling app already
+ // has permission on the URI, which we never asked for: the only
+ // SAF grant we hold is for the picker-selected download root, not
+ // this arbitrary subfolder. Files by Google and AOSP DocumentsUI
+ // both query the documents provider with their own credentials,
+ // so dropping the grant flag lets them resolve the URI on their
+ // own instead of failing with a SecurityException at startActivity
+ val dirMime = DocumentsContract.Document.MIME_TYPE_DIR
+ val newViewIntent: (uri: Uri, mime: String?) -> Intent = { target, mime ->
+ Intent(Intent.ACTION_VIEW).apply {
+ if (mime != null) {
+ setDataAndType(target, mime)
+ } else {
+ setData(target)
+ }
+ addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+ }
+ }
+ // Each attempt has two halves:
+ // - probe: the ACTION_VIEW intent we run `queryIntentActivities`
+ // against to decide if there's a real handler. We always probe
+ // the inner ACTION_VIEW; querying `Intent.ACTION_CHOOSER` would
+ // just return the system ChooserActivity and tell us nothing
+ // - launchFactory: builds the intent we actually `startActivity`
+ // on. For the chooser variant we wrap a fresh ACTION_VIEW in
+ // `Intent.createChooser`, which lets the user pick between
+ // multiple file managers and bypass any "always open with"
+ // default the system has stored
+ data class Attempt(
+ val label: String,
+ val probe: Intent,
+ val launchFactory: () -> Intent,
+ )
+ val attempts = mutableListOf()
+ if (treeDocUri != null) {
+ attempts += Attempt(
+ label = "tree-doc+dirmime",
+ probe = newViewIntent(treeDocUri, dirMime),
+ launchFactory = { newViewIntent(treeDocUri, dirMime) },
+ )
+ attempts += Attempt(
+ label = "tree-doc+nomime",
+ probe = newViewIntent(treeDocUri, null),
+ launchFactory = { newViewIntent(treeDocUri, null) },
+ )
+ }
+ attempts += Attempt(
+ label = "doc+dirmime",
+ probe = newViewIntent(docUri, dirMime),
+ launchFactory = { newViewIntent(docUri, dirMime) },
+ )
+ attempts += Attempt(
+ label = "chooser+doc+dirmime",
+ probe = newViewIntent(docUri, dirMime),
+ launchFactory = {
+ Intent.createChooser(newViewIntent(docUri, dirMime), "Open folder with").apply {
+ addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+ }
+ },
+ )
+ attempts += Attempt(
+ label = "doc+nomime",
+ probe = newViewIntent(docUri, null),
+ launchFactory = { newViewIntent(docUri, null) },
+ )
+
+ val errors = mutableListOf()
+ for (attempt in attempts) {
+ val resolved = activity.packageManager.queryIntentActivities(attempt.probe, 0)
+ if (resolved.isEmpty()) {
+ Log.w(REVEAL_TAG, "no handler for ${attempt.label} intent for path=$path")
+ errors.add("${attempt.label}: no_handler")
+ continue
+ }
+ try {
+ activity.startActivity(attempt.launchFactory())
+ Log.i(REVEAL_TAG, "revealFolder ${attempt.label} succeeded (resolved=${resolved.size})")
+ return "ok"
+ } catch (e: ActivityNotFoundException) {
+ Log.w(REVEAL_TAG, "${attempt.label} dispatch failed: ActivityNotFoundException", e)
+ errors.add("${attempt.label}: ActivityNotFoundException")
+ } catch (e: SecurityException) {
+ Log.w(REVEAL_TAG, "${attempt.label} dispatch failed: SecurityException", e)
+ errors.add("${attempt.label}: SecurityException: ${e.message ?: "(no message)"}")
+ } catch (e: Throwable) {
+ Log.w(REVEAL_TAG, "${attempt.label} dispatch failed: ${e.javaClass.simpleName}", e)
+ errors.add("${attempt.label}: ${e.javaClass.simpleName}: ${e.message ?: "(no message)"}")
+ }
+ }
+
+ Log.w(REVEAL_TAG, "revealFolder exhausted all attempts for path=$path: $errors")
+ return errors.joinToString("; ")
+ }
+
+ private fun buildExternalStorageDocId(path: String): String? {
+ val primaryPrefix = "/storage/emulated/0/"
+ return when {
+ path == "/storage/emulated/0" || path == "/storage/emulated/0/" -> "primary:"
+ path.startsWith(primaryPrefix) -> {
+ val rel = path.removePrefix(primaryPrefix).trimEnd('/')
+ if (rel.isEmpty()) "primary:" else "primary:$rel"
+ }
+ path.startsWith("/storage/") -> {
+ val rest = path.removePrefix("/storage/").trimEnd('/')
+ val slash = rest.indexOf('/')
+ if (slash < 0) {
+ "$rest:"
+ } else {
+ val volume = rest.substring(0, slash)
+ val rel = rest.substring(slash + 1)
+ if (rel.isEmpty()) "$volume:" else "$volume:$rel"
+ }
+ }
+ else -> null
+ }
+ }
+ }
+}
diff --git a/src-tauri/gen/android/app/src/main/java/app/risuko/mobile/RisukoForegroundService.kt b/src-tauri/gen/android/app/src/main/java/app/risuko/mobile/RisukoForegroundService.kt
new file mode 100644
index 00000000..beb3d70f
--- /dev/null
+++ b/src-tauri/gen/android/app/src/main/java/app/risuko/mobile/RisukoForegroundService.kt
@@ -0,0 +1,111 @@
+package app.risuko.mobile
+
+import android.app.Notification
+import android.app.NotificationChannel
+import android.app.NotificationManager
+import android.app.PendingIntent
+import android.app.Service
+import android.content.Context
+import android.content.Intent
+import android.os.Build
+import android.os.IBinder
+import androidx.core.app.NotificationCompat
+
+class RisukoForegroundService : Service() {
+ override fun onBind(intent: Intent?): IBinder? = null
+
+ override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
+ ensureChannel(this)
+ val progress = intent?.getIntExtra(EXTRA_PROGRESS, 0)?.coerceIn(0, 100) ?: 0
+ val activeCount = intent?.getIntExtra(EXTRA_ACTIVE_COUNT, 0)?.coerceAtLeast(0) ?: 0
+ val detail = intent?.getStringExtra(EXTRA_DETAIL).orEmpty()
+
+ if (activeCount <= 0) {
+ stopForeground(STOP_FOREGROUND_REMOVE)
+ stopSelf()
+ return START_NOT_STICKY
+ }
+
+ startForeground(NOTIFICATION_ID, buildNotification(this, progress, activeCount, detail))
+ return START_STICKY
+ }
+
+ companion object {
+ private const val CHANNEL_ID = "risuko_downloads"
+ private const val CHANNEL_NAME = "Downloads"
+ private const val NOTIFICATION_ID = 4737
+ private const val EXTRA_PROGRESS = "progress"
+ private const val EXTRA_ACTIVE_COUNT = "activeCount"
+ private const val EXTRA_DETAIL = "detail"
+
+ fun show(context: Context, progress: Int, activeCount: Int, detail: String) {
+ val intent = Intent(context, RisukoForegroundService::class.java).apply {
+ putExtra(EXTRA_PROGRESS, progress.coerceIn(0, 100))
+ putExtra(EXTRA_ACTIVE_COUNT, activeCount.coerceAtLeast(0))
+ putExtra(EXTRA_DETAIL, detail)
+ }
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ context.startForegroundService(intent)
+ } else {
+ context.startService(intent)
+ }
+ }
+
+ fun hide(context: Context) {
+ context.stopService(Intent(context, RisukoForegroundService::class.java))
+ }
+
+ private fun ensureChannel(context: Context) {
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
+ return
+ }
+ val manager = context.getSystemService(NotificationManager::class.java)
+ val existing = manager.getNotificationChannel(CHANNEL_ID)
+ if (existing != null) {
+ return
+ }
+ val channel = NotificationChannel(
+ CHANNEL_ID,
+ CHANNEL_NAME,
+ NotificationManager.IMPORTANCE_LOW,
+ ).apply {
+ setShowBadge(false)
+ }
+ manager.createNotificationChannel(channel)
+ }
+
+ private fun buildNotification(
+ context: Context,
+ progress: Int,
+ activeCount: Int,
+ detail: String,
+ ): Notification {
+ val launchIntent = context.packageManager.getLaunchIntentForPackage(context.packageName)
+ ?: Intent(context, MainActivity::class.java)
+ val pendingIntent = PendingIntent.getActivity(
+ context,
+ 0,
+ launchIntent,
+ PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT,
+ )
+ val title = if (activeCount == 1) {
+ "1 active download"
+ } else {
+ "$activeCount active downloads"
+ }
+ val text = detail.ifBlank { "$progress% complete" }
+
+ return NotificationCompat.Builder(context, CHANNEL_ID)
+ .setSmallIcon(R.drawable.ic_notification)
+ .setContentTitle(title)
+ .setContentText(text)
+ .setContentIntent(pendingIntent)
+ .setOngoing(true)
+ .setOnlyAlertOnce(true)
+ .setShowWhen(false)
+ .setProgress(100, progress.coerceIn(0, 100), false)
+ .setPriority(NotificationCompat.PRIORITY_LOW)
+ .build()
+ }
+ }
+}
diff --git a/src-tauri/gen/android/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/src-tauri/gen/android/app/src/main/res/drawable-v24/ic_launcher_foreground.xml
new file mode 100644
index 00000000..2b068d11
--- /dev/null
+++ b/src-tauri/gen/android/app/src/main/res/drawable-v24/ic_launcher_foreground.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src-tauri/gen/android/app/src/main/res/drawable/empty_splash_icon.xml b/src-tauri/gen/android/app/src/main/res/drawable/empty_splash_icon.xml
new file mode 100644
index 00000000..4ad30eb4
--- /dev/null
+++ b/src-tauri/gen/android/app/src/main/res/drawable/empty_splash_icon.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/src-tauri/gen/android/app/src/main/res/drawable/ic_launcher_background.xml b/src-tauri/gen/android/app/src/main/res/drawable/ic_launcher_background.xml
new file mode 100644
index 00000000..07d5da9c
--- /dev/null
+++ b/src-tauri/gen/android/app/src/main/res/drawable/ic_launcher_background.xml
@@ -0,0 +1,170 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src-tauri/gen/android/app/src/main/res/drawable/ic_notification.xml b/src-tauri/gen/android/app/src/main/res/drawable/ic_notification.xml
new file mode 100644
index 00000000..238de351
--- /dev/null
+++ b/src-tauri/gen/android/app/src/main/res/drawable/ic_notification.xml
@@ -0,0 +1,10 @@
+
+
+
+
diff --git a/src-tauri/gen/android/app/src/main/res/layout/activity_main.xml b/src-tauri/gen/android/app/src/main/res/layout/activity_main.xml
new file mode 100644
index 00000000..4fc24441
--- /dev/null
+++ b/src-tauri/gen/android/app/src/main/res/layout/activity_main.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/src-tauri/gen/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
new file mode 100644
index 00000000..2ffbf24b
--- /dev/null
+++ b/src-tauri/gen/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/src-tauri/gen/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 0000000000000000000000000000000000000000..98aec1240fd04a0c21de212264d8c24e2e78301b
GIT binary patch
literal 2826
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
literal 0
HcmV?d00001
diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png b/src-tauri/gen/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png
new file mode 100644
index 0000000000000000000000000000000000000000..98aec1240fd04a0c21de212264d8c24e2e78301b
GIT binary patch
literal 2826
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
literal 0
HcmV?d00001
diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png b/src-tauri/gen/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
new file mode 100644
index 0000000000000000000000000000000000000000..98aec1240fd04a0c21de212264d8c24e2e78301b
GIT binary patch
literal 2826
zcmV+l3-$DgP)wYT
zQyeVTbjyNKf3%i_B2{|oU3^}m%VilFYU=4bgX8$jHTPqH(dpb(rGL)NDZ>zk0EkC#
z=%_gGyw1=*Lye-DP7Tm#>?a=EFPCdcBm@9BAOMfY_VH)yMn=14qTT7vF$RM#mZUKl
z{OcL4T=_ea3J>^<#!jcxO#I2DbIVr0HO&C7r>PG`P0l?F3ZIe6<`7Yi=vr&f?QCs6
z5Nacprq9jWKp5zBt^4*rHD(^5QfTW}zm=c+AOcV;7l}oxQB&WvL9W#ldu(axA6Kvc
zfTThIvc&AQ>pxhr@(-bAxjFX}^D5x?mVfz+G4lWb`260ZRc{eq7(&Ui?3=(bK~w3m
zEN$7`g2y3b&IBJ+Fn^<7-!{;Hfl6W89fmY@F;R4-^ZQX#4_SlDWd=Ne5QYF4bl%N^
z3Ds1<78X4V00_Y2vYb5e7sGI?D&_V&?tFnpWg>tzcW;{j3|6a-LZSKnUZRFs@?3I-
zG?}m7OrW5m2IF!Q$q`sx^=5DH>BWm*nv-3IAvBSPTmu)*drXtQysG-$hUN+!59Dgr
z5Jg^(y??kVw0?<5H7E5hJ|}_05}U2V!z1nOJvDBRea6AqTqzlG@~r$7rGKJQ=+{Fc
zS9Sy-o9^myz5fDp>hUcbC>OH#-23MId=X~pd8%H=ruw^S=SF4LjDyP%5
z|BELt*6lW#dR5B#Mq}sEW54%InCk{(=Y>oAq(W7aOhfo$2-BvQTxvVybX%EpPP%Fl
z0x+6-%a3i{d-&zrmaiH+zB$vluc6~;*6boKTZ{k@4hbT4=fFk7Soi@M-B6UuG@CXb
z!4TztR1V{=UH6$nHmER-A+)aKjp7BH$I03m?3p6d7aJrcI9=z8q#weLv8spJUUU%q)**BrS!0CaSpRLFB=!Aj)P%=Yf;
zG3(HnWzb;i#__;J7q`dm^SX1=mJu#ef$IDET^_IV#tlVrq)L=))6$lbKlZuvJA3=i
zMnwexI36fJ@`}e}Bc=_8P)XsIuwJK|%KbhM>5{1^4C@jFSZ3Dh@%imEojLtcS}SZm7x
zx7$Lzm&>(F@B9siC2VRrc&_gAYrMoF)#@d$h=t0wAHMtk><(fI)zs}Nz2gN8qaXk}
zl{qK1_)^C)!hU}C`V7S)0Py)-T?2KEomFm+4FHBmJ2RCFiH%XnNxW%bES69fvKMu9
z94FsD{?Hx{TZ{l^E9SdB)`sS>LnGW5LCpY4Jqvzs3Uh_5qErLu5CSZ{M^Uqnk$A!I{|=2=PjMLkxpYXXe`n`
zlA~j!U_7pjjL+@1c)bqdy?Lx3aGWe6He6B`n?B(4kfp|u3#G-bC(jl_?)cCO1pIna
z_m%$IZ)-oP@2CU-4nvTsTqNZy$s>rvHFnd$7)(AHpT|A^gBgrhs!qO(2mA=&>OkGe
zvmb|gx!jhDZ(j5J-3UNu>^xlkHd*9!Ss-{~VlpSsEVOi=us93=kSs}0O}-6+J$6;!
z7B=8W=a|D`B*A!W2||cWp8cn0du{z*7DF&P)-y4qcD`|6Q+p+!D;qKO_ya!jJ&%(>
zo-m!}Z#=J52(1Lb4>2&rTqFxO@0g*nlJLF`HFKf>9`RHd}&W<3EbSF={k*6I65>
z`^g7Cdu_|%NjN$7f
zL(P^Uuiwe%CInY@yrt_zs6m#pFiUkipDm$aR2r3$Icoui5V6)f+?Ma^CvSI(U;xl|
z`5U#W2;qPL$)%Ytmt|73zM+N>ca@NR*~Kou;cp;TNb-aah7%J-mlQ+&FRY`rMx!+~V@sLTxZa(WVryEBd7%
zWaYHj^>3FyVsnPy!3af{%sElBy&(4iCWA+?ty}(v%|6n2`EZ2pY^LDZ`#wz&rVvyB
z==k}aChHKB&RtfpDM_5hV~G-kvy;SW*W?B4&aw9@HjbG4BM&vY`K}>jyG+!nfgybxgU(4&6a;VhD1CO(ZJIS>*1-`|55OdN0&e0xcfI=Ps%<=u
zgru@LM&8@^u+?D%fG0|~(I|9sTO+A(JaD?TV*A0Ln{2wMf`ln#sQ>`OC>z$iSCDgW
z=&=|8Y<5G-58n>xTgEIyKA)S%k&5{WZR!$r@;nMgA%Z5T>`v3)_iyYPtRpIX?%q!e
zGD<_K++KTQXVt+oe~Y%GrUgc*c){kflGlk$HH6EYg;ME8yLMD;8V>m^#-wvg=4}%3
z675ct&0*@*H+2m)_$DMjqSCZJ{AH;Z-?RL8a%o0{R6f7&WW(+Q)qlNa8Jr1C_lqWm
zQ5NQHSYG(7Or*X6mCx&{?>Kts)Vqd=Q@PXBP5V@vB+bmrSgBUb6LFFRyaXnlYqseu
zHiLe&tEIE5xx3mEyu$rBn(=SYKb3CT|MQ50#hT(^v8Fg!tSJr_Yl?%#n&M!wrZ`xv
c>6QiiFJ0Uu(5|@qcmMzZ07*qoM6N<$g0uo|W&i*H
literal 0
HcmV?d00001
diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/src-tauri/gen/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 0000000000000000000000000000000000000000..73847fd960ffb007f5a2802c1a846dd5cae4a362
GIT binary patch
literal 1774
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
literal 0
HcmV?d00001
diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png b/src-tauri/gen/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png
new file mode 100644
index 0000000000000000000000000000000000000000..5e95e2a75eba0cdb2a7b6d41be26bd3c3d08a922
GIT binary patch
literal 4009
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
literal 0
HcmV?d00001
diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/src-tauri/gen/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
new file mode 100644
index 0000000000000000000000000000000000000000..5e95e2a75eba0cdb2a7b6d41be26bd3c3d08a922
GIT binary patch
literal 4009
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
literal 0
HcmV?d00001
diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/src-tauri/gen/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000000000000000000000000000000000000..16773d7dc420a0ec2d2435a0a68ce18cd7db46bf
GIT binary patch
literal 6175
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
literal 0
HcmV?d00001
diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png b/src-tauri/gen/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png
new file mode 100644
index 0000000000000000000000000000000000000000..16773d7dc420a0ec2d2435a0a68ce18cd7db46bf
GIT binary patch
literal 6175
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
literal 0
HcmV?d00001
diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/src-tauri/gen/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
new file mode 100644
index 0000000000000000000000000000000000000000..16773d7dc420a0ec2d2435a0a68ce18cd7db46bf
GIT binary patch
literal 6175
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
literal 0
HcmV?d00001
diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/src-tauri/gen/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 0000000000000000000000000000000000000000..05a1bf2c8ba2ab17acb4bdca5d4275dad97539c4
GIT binary patch
literal 8465
zcmdT~1ydYNv|TJfaQB7a!7aE23+@u!b#Yj1kq|sVk}M9v-Q6`12n2U`39>kg!`tuu
ziuY=2rl-5Q?o@Z5KKIV)8>6ZI0p~U4YXAU%qogROjVOEmyD^Xv&(*F6O+@j^O67wb
z;N`z7r?VIe0MG%HQr?`;WI<5F9#4(-mQ{EMU^Px_*zYzCXxUPz*q=Uk7cu9b7p1LU0xYHXVn!)KkGfZ
z>`_E(@1{EzmJlbazXI31FxaDhu#rb##sA_egG4WTOBgsICyT;3K|E8oqj%FqKr2Kh
z?)|0_9RNVb&dELKf95u)eusz3Hm!+-=^fT`>rXWoM3-FB`vHOx870A$MgVJs@h4I2dB|{eq00^X4fRCOBb}=p5;P@qp9#nH3
ze>Wd#+um&|_t#nMvW8E#;<{@z^
zCL}r}Wex5XBRnEC6iF>K+N;kzFDJi0yrV^*O9&X??8`4Q3)0w)RvKrnP%h-;SV3)o
zZ?xohZa)Bnl4eE6_G7m4ytcRZEXbprh+-6Zg+lt$>Rg-&+YK?mp)&-fhWf@^zzz9b4wc|4dy1=jp0)BjZfhfe$o6sMCsW;MF%}fqB2|
z`IWZqSawj;%^$CRF?FL3O!*PlThJP1_ybrwKwuF_6~K7FlS7ME_s;uOZ(vJ;j1d9qUg4uOYF_LAJA!%$QVarn+}$
zcUH^rH6TBi#aIgYfMM$eSkhcZ$JE;!R}Z4021~(>`l&Rwu%nRm
z-jA9KiXkNS#+yp2+vckb!QVAt9$m_Z_4a$%SiDMLSNM2JbT}WN=A%M|e*LVdlOUCq
zwO6Ut+)VIq9{a-29Ue$1JP9~_*q%B>Y{=(+c^$2I)9qG*cnyu2?$^2#p?
zHzWo=BGiKW%qCpj?k9V{Rm}E0(WZr6wL0DS4tVYCeXzG}inxl)3fr6GmA+;C)FfaP
z0b^>NJu*@I9ND+iy121>Jq4>ezkk0ua1~3!55n8X(H?l&YE^l69mQq(+>)PhlICD{
z=oyi@L%^Z+?_aBOJq#}rVa5!yUHi#+bGQ&z1@C7XVP^|)nme}DwdWFqqdcN)yQCkpHv?~5yvxGc=%pJt2^L=TcElr?x764&|?HPvnsbWYBI>WzyC
zI050|()8c8+2RWBO}Rt5!~7_y3|hPwL;44Zp46Uf6OKMC-TE(qL)=bVy#M_JWqru+
zziri>EUOOctNAJr)fe}5SbdhXn3?pqV_gzoobk4>)&y0$u_%{dQ;@-|pN+S3w9-xw
zJ|XhkL`>c8{xi*Xj}lo%ZLXH5yI&$1BGg={!@zy!nDfi5z*}kg>u1?PGyV)5X_Edz
z_gX8V*)h?W7}e|)ul1G8Mr8_HNKt)`RcaVsLGh
z^<>naU(r3BQvIuJUamY6be?zm${UM<)ExCG%#v3t&Fk`nRuFvja2O*5C33|vVDWcd
zUh#R}xw~6hp&Mea@GMvd~!2ny|Y4TWuiseP7D
z!nm-0B{6zECxO~SrG1d=c3FSo0;?hy0Q>7x0%Y=}zI~*Ib8{z#k&(#!0@_;!2Ko&a
zCp0&i6JwGX)~B%VP(qc=SKYl=tgcpi?^=)M*6xhAoOgA=jJBCOK^dzT2`CyVLV-ty
zjop}-p@5Acdv5&k0ZRp#hp;mYM#jQCqJP8+z_`^8m8MUTFHMXQ=gRgb+Uu=ss!f2u
zsF-eh+?!?~R#f9;Q8~a(-W*FQZ3MVg*k`$Zcg;O+aFVNGH-JeJs$c!E>^wY(J1qsD
zF@3NbFP6Hji0|8sc)M*ausF1|l~{mAa7b(p3q6v`a2Yq?HY*jCZZs$KSm>4l>vQO8
z+A5M{7O1-5h4xv7gsc-K#qCt;2h%fFg#x5m6GziK0|&-UY{{OB+lzN$<=N_0pYMtJAw(C2|7(#zkx_fAg(k4p
z-mk{xbS>NL_5`JDy(ss5;h%%lCAIsC*1Y@G6HU3){c%igu1{Hj1pm&ZT*BJTjUt%O
zP<6%2m>Dii4aZzg)qvy6u@cR3Ju@BP-iLj2h}k=rE1&QByLm>jWZi5$p8ayv>J@^Z
z{A~p+Qz_3UyT#w#8VnTFgP^J)Nafwtk017=BrYC5sWD2PlE3r`R{NjAXv$a9ZQ39p
ziY0zgLETxu1})#J%HldVP5Z_;luwFuUt<(i)H<${E8cl%rESz!LMYGyA5B{Bv|+si
z@n5@69t1FAKsjQk&!!g@zp-HyfGjlS6jco_R~ih
zH>%Rm_rz#=Xr>^7uy%@p=m++0onq-n(`E0GcVipwA3QdhB{dYSH9+%84`+XO
z!BL_CAk{r2`)ZPmAgez!t1BJ{BS}9iS9dYfsaqK{*MNW?9@YFTaMcCW@l?|5Gqfx@R4Ypx}LmS{CY3}@5y*Q2QLhd26uUOAU`mR=UuR5oJ9Wzg5hxO7uxX8
z920e7A<%DrPtOt0&8>dtv{863U-rglYz`0pbxckA|F$_yTV(X0t_F!Rld?5*HCd8J
zr#2r1AYB>#)b*?aRk=Y5!B-8{&aXe7-J9G=^Q|Caivl^1OButA~&D+E>4*oVSe
zDHv0m&;Op@zJmfO#
z4AE}iIn@AA(Kk6}N6)8pg$YzPvomZShts>j7k=y9nXqYb$&SmOX~FiSXF61XZ<6@Y
zsZG2oJ!RV;*qhF_~vRQSQ5|C^P
zIFw_DZa~Z=IP0;g=#IxopEzJH!J}`4kx51tcV>ax?)!IM-ZZjde-h#^CWiKI+zazvF>#ShLEcTlbSj`Do`5i(Jjp03I+T
zj&)kRly$^pXg}sLU1`?_f>bI2=-H34Vt5puRK-VC9&b@$O`KlNpPv^%Z8HB_t!wsB
zBx^2jjR%O1_>#H!7cm90%V;@GQGRLo-*>xFKhtUI&rwzzD1h&lG1&QPVbW4@`xmH7
z1@w8UUPor;AqyZ@?CtRKAN^KI{v#ITGiud(^PCppY)bU{X@l5L*vR@bw3Grqwm7{mg11ZzEW8_DJb$0?+nyS86gk>35e}1)
zxwJC9u+$A9)iLA;t?M02YQdj{!O~HE!L0%77hA=SaCz2mPSzJ0Vq98io9E$JSb}Yc
z@loRn(W_Ds6pdyzHn`O^ni(t=Rp*H^-rzJMHA(<-emIsm*=rzxYjntaHuh_Fc-(=$
zM0Y7$BH*lP%RUos0pN4&hcS&i`|n^S4e$4XTa+J;8PHRb#o4_m%s5jNxY+&S=CmRu
zML2$?PPC(4_`3oy8Ooj8ek+QlUr(EtbPEudOWn{Wu4^Bj94FIuw78#al
zQH^rK`{@H9_vq=but#+>up%zdg^n={n4qpyx$6FYPq?`~QN2+yw_2N2ofE~-4ayCkwgnFKCy*^ibqO0?nvp*
zg)5izX?1guLXf9<700+bcU`DTnTtD(%k!^FbcJEz
zn#{jG`2aor3VMg{k|cn+@N~y2nuAzDuGT`>5mp3d#5
zcd9vBU0yi_6!HTVFtXhzpjXdWQ_K_dg;n#(NH$C~J&Vfj^3y(ZL9Rj?xQ}0h%i~aw
zA^nq2RAorXs0+J$Ejplp@EtTTb3=GdV#0mv0uD9s!4HG2(*Kn^RE5cG6)FQ^_uuXP
z7moWDn9VUuq>)ov-NmuUdoW;Cl&`?~`9ZE3evyH(9GT*Y
z=`7qf+|G;I*QDwe)d7a|>FX{nzjM-n$KdZT8j4{pq{AI(4XUvdh}})!dlmI1J{5$GNDKea5vq#|Cc*i)pVC0Zb-2_10t>cVoKU*=PS^n7uAu_%t3>I5b#zD!XeY5Y
z99Q7U<+P7<07(g!MnEanbs4C`G;PC3I!z6Uwh?_evKL5si>jLwgLyOCxMIe4r$R&R
z3^~m%S5JIiybkyyFa^oG8b5V4w@qzppd-Q#b8&mR%*rox%TR#)Oe6nr^+9=kt?elb
z=@ruNUqc|V*AR}i_zE-L^amF4SN$<)bt3g4JAnuKZzoe{+>UuPN1BBgP_G2n{3G*3
z;TRFIxYx-s->r++g@gA--IMg`_2bgfabzp928@{%x+I?lHu>EYN*c6loo2)rPE~r2
z=6z=B>0%!|H4A?2)x5SCel5*c>!g27eWA<(bv(PoxTC|6^TOmP^fOa@9WQo|!wKMT
zs$fLCVRHs`q3iykKiHqJ2tobDqbdNNBE_aNk^p&{%*=4N3)svzO-fK&X-K0{W*j4_
zgD1;yr#*!`C}{ih<1(P+Lg;`cQSKvA?r~>#t`yqbmbJ~u86be%QbJsp3wOVzf;Qqm
zDTb-(Q-L-a#69Xs85$zy^GY`d3FR*A=m?G4&m=gNZ;i)G2RT~)OWaKtlou3JpWIfo
zEhZHb@69%HKu{Iyn}HnJb6uF#7J?+ZMei#9orxZ#t3;Ul13{!YxknJFKj2YGnmZ32
zTMglsBgoYR0g8-lLpJsAs8$e;e8fc42bl;{O
zGpBppNUyMw8g8cqK+{NezTQaztp#q<1`TP>_KGUM#q^mn
z3@Q}Fm{PKsa&P`L&!~wrEs^+BWX6ozMx(fHaB7J+mKHeF`$oR~Qw=)6%G#4rQc~WN
z>1{AjGm{tkwn)I{vu++ULcA(eg(mAB$HpT?wm$xli!%+Hc8lC}Z@J{xCrIeby!(ne
zwewHB=VypFBZAEhVPw`s(+3+zM4=}{l^}K%AK2^{ls(UXzmXWNl5{K5ORwomGg6DC
zcKWW-p(?)$^LSGHZf$T{yhLU}qQgLS0aiu=4}ya`bC3?Oh>|o0d}>q{{q-j`$rTxS~)|k6WI*uwY3-X6BUa3iG{gSB&yS^!^ChigP`YY
zHS`n6i?F%mxyy~7q2u1@6#Cqi%^JMXei;P>9;=$p$yFQo^SQI^X%{VyQF~a(K%NqP
zE36j6X7fJr!J6bq=-WHRKVmJxvoais`;6RM7SvlcvbK1y+^c*-K-{*2V`)qn(~tW4
zw2Ils6!A#$mgMm$9H~ME+Y<%mk;VB};>h>6&!cnKUbUSZ~aKWY;B*lLR!{uZZ3sq|K+DyaFLY
zLzgEZ(yxx5e&UvR0M)d90V8OV$U}TOh`;_()!0`gBg04g_0&oH*Ws&{lQfQP0ASB!
zdv?J3#Q2A08hPW#52iGpaU6uomp@XzplPpUNC3_j)8^4*LyaT2FjhsJo6ghkWnGJT
z#`%{dd^^}SRFKmttWm|!gH&F^j@!_CcF~`^PS_(=dPUp^^-_iyP4bn(>mCaV33Fqg
z+oD*7z~&;Jwqj69`!5@+bs~IILd_dUIYBjSgu@=xZgzoKM2h-r&tCJj?+W06o)7CC
zeBycb`{~q>Vp}kS
zunLX0cZyJBmYQ1nl;ob_N8N_Pt~(p5>z<38Z`8(qgO60QIHkv>u9Ah99tXF{SznTe
zj0J0@yMoWVxw#g+^GS^$0E@xz=GV7;M^~}<4I+dZDP1TH2rp{F^%6Q8^8JhbM(%>@
z8-lkXZTF_*zYi_GNRR{I;6<_Na^yZ?rp245=NKY&2%uxxYv)2rrJTaFW}M?aQAuHC
z#m~0faYN0wR&&(>e?@NjTHIS72MxN*^#_Q!OXDQ~6BGJ#6Y5i#Nx)jo+Jdv(^_Qz1
zBKGyy(6v*>l;pXwd3grG=T<9A6YsU;50d+;@svfAB^`%7EasB5D{qT=
zu$McwG~`=v%tH?Dzb|idubi8|w6{ep2GM4-D9`LLe%Rk<<{qBm+VQwM`Qle!+}M2B
z?c>HXZh}sdjj?C$V?REtvfJ+Zb_I29%r-&(|zt-|&+~BBhQzbw692|C^cYXA(IA
zeta(bxY6ZVmQp7KSqcD|yWp!6}%?>w{pn>=9rEQbJMK#M<
zS=wh0`4con>Ji@lHXpxmtA=dutHFj8BhGZXszZa%r0rb#Db?FKRC^VRLKob
zRgh1Wd>|^zD`MroaMI*lUfhFInqwReDmoTvC^>nLzir+-JW6&MZL^UAG5ZH
zvQYus=8Y`#`o+q2ji>Gejjp|UCF4{gLSzwl2twVogi}g}4C@N%)-QUAtR#jT@9$O6
z@cF>Uu6o?C`(96-uGO8;-j)9SxyshD#Lp*q3unntwLXzpQ1KKh=x<5*nc5`rG!S3;
z07U%0u@{MLhsem#0T3qw?~aCF*BP`oOd_Dfp(yv|^N61R0ec0}Ue+_{*lmI*7Agi=
z%r9Rv6n*h1{hMiJRt>3WI+qG*Yt3(dCH_avFr84RXAPLK8qg3pXf7F2ma^E-fW+{}
z*V5IdfGg;>3s0CD2qoDz#p+{zn!T^)GFhE2Ww|5iB&5j_74p*48f!bNLCp~{`eu5A
zkmFp?vpS~Ea2$7*FKPX5t6rEB1g|p$;JQtjkNsds*Pw8{HELn5oA^RI$Op#Ck9Y6R^7PP(#$w*k41~%W&
z_<&yA+Z_w$`FNE_YTghlh9j417sY5Y`qAM0yKb}pCD;V((MeJ>3wTVlsfIZJ*3UM=
zGQLhqsR|0}vJ2_1MUlzSB*p*`TE7OGcdv@@uLvAldO}AAvL??ry942hMD)RL#~nmfxYvN9h`f(7K3uP1)NG=cIPf;pAc8fEy_K
zq8=@<6@mPQ6aT%&OJm&^Q{*mIUeR%|XeIy%;pw3vbAoq1ELwN@@ICetO%?M95$0Xa
zalBktgd5Y<3}!_()pZVrlw)l|4(L4jG3CDf;0f`c)G@42wj|$kgQSagN%_I!j8+YO
z-oAWmy+fr}j2$x9tf{5*D*RZI>H!(q9{775rJiys$HGy)Qh`ix*79qmL2S>yT5UQR
z0}VLM^x!bw-|tXab=7|OM1u2yB7R?HWd&Je)AMdA^-%!!c`>GWK;HHb69G1iH|;?z
z07SLKHFGd_(Dru{mQ?*Hjp`{&uPs-qEpSMjCSdX>cy)!s
z*%>y8k-Y`CA$>}Vnt*JwEdS0@W}K3>Ky*RWP#r==x_TuF4J3x7!acX
ND9NkKRmoU<{vTKLD;od+
literal 0
HcmV?d00001
diff --git a/src-tauri/gen/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png b/src-tauri/gen/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png
new file mode 100644
index 0000000000000000000000000000000000000000..05a1bf2c8ba2ab17acb4bdca5d4275dad97539c4
GIT binary patch
literal 8465
zcmdT~1ydYNv|TJfaQB7a!7aE23+@u!b#Yj1kq|sVk}M9v-Q6`12n2U`39>kg!`tuu
ziuY=2rl-5Q?o@Z5KKIV)8>6ZI0p~U4YXAU%qogROjVOEmyD^Xv&(*F6O+@j^O67wb
z;N`z7r?VIe0MG%HQr?`;WI<5F9#4(-mQ{EMU^Px_*zYzCXxUPz*q=Uk7cu9b7p1LU0xYHXVn!)KkGfZ
z>`_E(@1{EzmJlbazXI31FxaDhu#rb##sA_egG4WTOBgsICyT;3K|E8oqj%FqKr2Kh
z?)|0_9RNVb&dELKf95u)eusz3Hm!+-=^fT`>rXWoM3-FB`vHOx870A$MgVJs@h4I2dB|{eq00^X4fRCOBb}=p5;P@qp9#nH3
ze>Wd#+um&|_t#nMvW8E#;<{@z^
zCL}r}Wex5XBRnEC6iF>K+N;kzFDJi0yrV^*O9&X??8`4Q3)0w)RvKrnP%h-;SV3)o
zZ?xohZa)Bnl4eE6_G7m4ytcRZEXbprh+-6Zg+lt$>Rg-&+YK?mp)&-fhWf@^zzz9b4wc|4dy1=jp0)BjZfhfe$o6sMCsW;MF%}fqB2|
z`IWZqSawj;%^$CRF?FL3O!*PlThJP1_ybrwKwuF_6~K7FlS7ME_s;uOZ(vJ;j1d9qUg4uOYF_LAJA!%$QVarn+}$
zcUH^rH6TBi#aIgYfMM$eSkhcZ$JE;!R}Z4021~(>`l&Rwu%nRm
z-jA9KiXkNS#+yp2+vckb!QVAt9$m_Z_4a$%SiDMLSNM2JbT}WN=A%M|e*LVdlOUCq
zwO6Ut+)VIq9{a-29Ue$1JP9~_*q%B>Y{=(+c^$2I)9qG*cnyu2?$^2#p?
zHzWo=BGiKW%qCpj?k9V{Rm}E0(WZr6wL0DS4tVYCeXzG}inxl)3fr6GmA+;C)FfaP
z0b^>NJu*@I9ND+iy121>Jq4>ezkk0ua1~3!55n8X(H?l&YE^l69mQq(+>)PhlICD{
z=oyi@L%^Z+?_aBOJq#}rVa5!yUHi#+bGQ&z1@C7XVP^|)nme}DwdWFq