From 59468d1bc2cf4d8c3e2911c0d204f39a6ead7bcf Mon Sep 17 00:00:00 2001 From: Isilsolme Date: Sun, 31 May 2026 19:45:11 +0800 Subject: [PATCH 1/6] Fix language preference not persisting after app restart Use commit() instead of apply() for String preference writes to ensure the value is written to disk synchronously before the process is killed during restart(). Root cause: SharedPreferences.apply() writes to memory immediately but commits to disk asynchronously. When context.restart() calls Process.killProcess()+exitProcess(0) immediately after writing the language preference, the async disk write may not complete, causing the new process to read the old value. Closes #862 Co-Authored-By: Claude Opus 4.8 --- app/src/main/java/io/legado/app/ui/config/PrefDelegate.kt | 5 +++-- app/src/main/java/io/legado/app/utils/ContextExtensions.kt | 3 +++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/io/legado/app/ui/config/PrefDelegate.kt b/app/src/main/java/io/legado/app/ui/config/PrefDelegate.kt index 495fc67d5..9dad8c6f0 100644 --- a/app/src/main/java/io/legado/app/ui/config/PrefDelegate.kt +++ b/app/src/main/java/io/legado/app/ui/config/PrefDelegate.kt @@ -12,11 +12,12 @@ import io.legado.app.utils.getPrefFloat import io.legado.app.utils.getPrefInt import io.legado.app.utils.getPrefLong import io.legado.app.utils.getPrefString +import io.legado.app.utils.putPrefString import io.legado.app.utils.putPrefBoolean import io.legado.app.utils.putPrefFloat import io.legado.app.utils.putPrefInt import io.legado.app.utils.putPrefLong -import io.legado.app.utils.putPrefString +import io.legado.app.utils.putPrefStringSync import splitties.init.appCtx import kotlin.properties.ReadWriteProperty import kotlin.reflect.KProperty @@ -84,7 +85,7 @@ fun prefDelegate( override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) { if (_value.value != value) { when (value) { - is String? -> appCtx.putPrefString(key, value) + is String? -> appCtx.putPrefStringSync(key, value) is Int -> appCtx.putPrefInt(key, value) is Boolean -> appCtx.putPrefBoolean(key, value) is Long -> appCtx.putPrefLong(key, value) diff --git a/app/src/main/java/io/legado/app/utils/ContextExtensions.kt b/app/src/main/java/io/legado/app/utils/ContextExtensions.kt index 41b26c2bf..d7ddc57c1 100644 --- a/app/src/main/java/io/legado/app/utils/ContextExtensions.kt +++ b/app/src/main/java/io/legado/app/utils/ContextExtensions.kt @@ -223,6 +223,9 @@ fun Context.getPrefString(key: String, defValue: String? = null) = fun Context.putPrefString(key: String, value: String?) = defaultSharedPreferences.edit { putString(key, value) } +fun Context.putPrefStringSync(key: String, value: String?) = + defaultSharedPreferences.edit(commit = true) { putString(key, value) } + fun Context.getPrefStringSet( key: String, defValue: MutableSet? = null, From 6ebe36dec9a46eafe48fba382c2518ed463491d2 Mon Sep 17 00:00:00 2001 From: Isilsolme Date: Sun, 31 May 2026 19:51:35 +0800 Subject: [PATCH 2/6] Refine: make synchronous pref writes opt-in via sync parameter MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Only use commit() (sync disk write) for preferences that opt in with sync=true. The language preference in OtherConfig is currently the only one that needs this — it triggers an immediate process restart via context.restart(). Other string preferences continue using apply() (async) to avoid unnecessary main-thread blocking. Co-Authored-By: Claude Opus 4.8 --- app/src/main/java/io/legado/app/ui/config/PrefDelegate.kt | 6 ++++-- .../java/io/legado/app/ui/config/otherConfig/OtherConfig.kt | 3 ++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/io/legado/app/ui/config/PrefDelegate.kt b/app/src/main/java/io/legado/app/ui/config/PrefDelegate.kt index 9dad8c6f0..d1bcf32d2 100644 --- a/app/src/main/java/io/legado/app/ui/config/PrefDelegate.kt +++ b/app/src/main/java/io/legado/app/ui/config/PrefDelegate.kt @@ -35,6 +35,7 @@ fun prefDelegate( key: String, defaultValue: T, lifecycleOwner: LifecycleOwner? = null, + sync: Boolean = false, onValueChange: ((T) -> Unit)? = null ): PrefDelegate { return object : PrefDelegate, SharedPreferences.OnSharedPreferenceChangeListener, DefaultLifecycleObserver { @@ -85,7 +86,7 @@ fun prefDelegate( override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) { if (_value.value != value) { when (value) { - is String? -> appCtx.putPrefStringSync(key, value) + is String? -> if (sync) appCtx.putPrefStringSync(key, value) else appCtx.putPrefString(key, value) is Int -> appCtx.putPrefInt(key, value) is Boolean -> appCtx.putPrefBoolean(key, value) is Long -> appCtx.putPrefLong(key, value) @@ -112,8 +113,9 @@ fun prefStateDelegate( key: String, defaultValue: T, lifecycleOwner: LifecycleOwner? = null, + sync: Boolean = false, onValueChange: ((T) -> Unit)? = null ): PrefStateDelegate { - val delegate = prefDelegate(key, defaultValue, lifecycleOwner, onValueChange) + val delegate = prefDelegate(key, defaultValue, lifecycleOwner, sync, onValueChange) return PrefStateDelegate(delegate) } \ No newline at end of file diff --git a/app/src/main/java/io/legado/app/ui/config/otherConfig/OtherConfig.kt b/app/src/main/java/io/legado/app/ui/config/otherConfig/OtherConfig.kt index 538b8bf64..0dd7d5ad6 100644 --- a/app/src/main/java/io/legado/app/ui/config/otherConfig/OtherConfig.kt +++ b/app/src/main/java/io/legado/app/ui/config/otherConfig/OtherConfig.kt @@ -8,7 +8,8 @@ object OtherConfig { var language by prefDelegate( PreferKey.language, - "auto" + "auto", + sync = true ) var updateToVariant by prefDelegate( From 947dbbfff13cd18512be407fc06d44341ec009bd Mon Sep 17 00:00:00 2001 From: Isilsolme Date: Sun, 31 May 2026 20:52:05 +0800 Subject: [PATCH 3/6] Sort imports in PrefDelegate.kt Co-Authored-By: Claude Opus 4.8 --- app/src/main/java/io/legado/app/ui/config/PrefDelegate.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/io/legado/app/ui/config/PrefDelegate.kt b/app/src/main/java/io/legado/app/ui/config/PrefDelegate.kt index d1bcf32d2..363ef981a 100644 --- a/app/src/main/java/io/legado/app/ui/config/PrefDelegate.kt +++ b/app/src/main/java/io/legado/app/ui/config/PrefDelegate.kt @@ -12,11 +12,11 @@ import io.legado.app.utils.getPrefFloat import io.legado.app.utils.getPrefInt import io.legado.app.utils.getPrefLong import io.legado.app.utils.getPrefString -import io.legado.app.utils.putPrefString import io.legado.app.utils.putPrefBoolean import io.legado.app.utils.putPrefFloat import io.legado.app.utils.putPrefInt import io.legado.app.utils.putPrefLong +import io.legado.app.utils.putPrefString import io.legado.app.utils.putPrefStringSync import splitties.init.appCtx import kotlin.properties.ReadWriteProperty From d0e057e091c5fb19dec239fe27d774b551e1e45a Mon Sep 17 00:00:00 2001 From: fjsuta Date: Sun, 31 May 2026 14:37:48 +0000 Subject: [PATCH 4/6] =?UTF-8?q?feat:=20=E6=A3=80=E6=9F=A5WebDav=E5=A4=87?= =?UTF-8?q?=E4=BB=BD=E6=97=A0=E5=93=8D=E5=BA=94=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: traeagent --- .../legado/app/ui/config/backupConfig/BackupConfigScreen.kt | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/io/legado/app/ui/config/backupConfig/BackupConfigScreen.kt b/app/src/main/java/io/legado/app/ui/config/backupConfig/BackupConfigScreen.kt index 588435254..f6cd373b4 100644 --- a/app/src/main/java/io/legado/app/ui/config/backupConfig/BackupConfigScreen.kt +++ b/app/src/main/java/io/legado/app/ui/config/backupConfig/BackupConfigScreen.kt @@ -624,10 +624,8 @@ fun BackupConfigScreen( AppAlertDialog( show = showLoadingDialog, - onDismiss = {}, - onConfirm = {}, - title = loadingText, - onDismissRequest = { showLoadingDialog = false } + onDismissRequest = { showLoadingDialog = false }, + title = loadingText ) } From 3f84a74f7b33795454505f6cadb5fc99c10b5e9c Mon Sep 17 00:00:00 2001 From: fjsuta Date: Sun, 31 May 2026 16:13:24 +0000 Subject: [PATCH 5/6] =?UTF-8?q?feat:=20=E6=A3=80=E6=9F=A5WebDav=E5=A4=87?= =?UTF-8?q?=E4=BB=BD=E6=97=A0=E5=93=8D=E5=BA=94=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: traeagent --- gradle.properties | 4 +++- gradle/gradle-daemon-jvm.properties | 11 ----------- gradle/wrapper/gradle-wrapper.properties | 2 +- settings.gradle | 22 ++++------------------ 4 files changed, 8 insertions(+), 31 deletions(-) diff --git a/gradle.properties b/gradle.properties index b14520e17..ea723e68d 100644 --- a/gradle.properties +++ b/gradle.properties @@ -46,4 +46,6 @@ android.dependency.useConstraints=true android.generateSyncIssueWhenLibraryConstraintsAreEnabled=false android.r8.strictFullModeForKeepRules=false org.gradle.configuration-cache=true -android.newDsl=false \ No newline at end of file +android.newDsl=false +org.gradle.java.installations.auto-detect=true +org.gradle.java.installations.auto-download=false \ No newline at end of file diff --git a/gradle/gradle-daemon-jvm.properties b/gradle/gradle-daemon-jvm.properties index 5c34300fa..63e5bbdf4 100644 --- a/gradle/gradle-daemon-jvm.properties +++ b/gradle/gradle-daemon-jvm.properties @@ -1,13 +1,2 @@ #This file is generated by updateDaemonJvm -toolchainUrl.FREE_BSD.AARCH64=https\://api.foojay.io/disco/v3.0/ids/56a19bc915b9ba2eb62ba7554c61b919/redirect -toolchainUrl.FREE_BSD.X86_64=https\://api.foojay.io/disco/v3.0/ids/398ffe3949748bfb1d5636f023d228fd/redirect -toolchainUrl.LINUX.AARCH64=https\://api.foojay.io/disco/v3.0/ids/56a19bc915b9ba2eb62ba7554c61b919/redirect -toolchainUrl.LINUX.X86_64=https\://api.foojay.io/disco/v3.0/ids/398ffe3949748bfb1d5636f023d228fd/redirect -toolchainUrl.MAC_OS.AARCH64=https\://api.foojay.io/disco/v3.0/ids/e99bae143b75f9a10ead10248f02055e/redirect -toolchainUrl.MAC_OS.X86_64=https\://api.foojay.io/disco/v3.0/ids/04e088f8677de3b384108493cc9481d0/redirect -toolchainUrl.UNIX.AARCH64=https\://api.foojay.io/disco/v3.0/ids/56a19bc915b9ba2eb62ba7554c61b919/redirect -toolchainUrl.UNIX.X86_64=https\://api.foojay.io/disco/v3.0/ids/398ffe3949748bfb1d5636f023d228fd/redirect -toolchainUrl.WINDOWS.AARCH64=https\://api.foojay.io/disco/v3.0/ids/e55dccbfe27cb97945148c61a39c89c5/redirect -toolchainUrl.WINDOWS.X86_64=https\://api.foojay.io/disco/v3.0/ids/dbd05c4936d573642f94cd149e1356c8/redirect -toolchainVendor=JETBRAINS toolchainVersion=21 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index c82ad3ff0..45353aab8 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,7 +1,7 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists distributionUrl=https\://services.gradle.org/distributions/gradle-9.4.1-all.zip -networkTimeout=10000 +networkTimeout=300000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/settings.gradle b/settings.gradle index c94f12283..3b03df478 100644 --- a/settings.gradle +++ b/settings.gradle @@ -7,25 +7,15 @@ pluginManagement { includeGroupByRegex("androidx.*") } } - //原仓库 + maven { url 'https://maven.aliyun.com/repository/gradle-plugin' } + maven { url 'https://maven.aliyun.com/repository/public' } gradlePluginPortal() mavenCentral() - //镜像仓库,无法连接源仓库自行启用镜像仓库,不要提交修改 - //maven { url "https://maven-central-asia.storage-download.googleapis.com/maven2/" } - //maven { url 'https://maven.aliyun.com/repository/google' } - //maven { url 'https://maven.aliyun.com/repository/public' } - //maven { url 'https://maven.aliyun.com/repository/gradle-plugin' } - //maven { url 'https://repo.huaweicloud.com/repository/maven/' } } } -plugins { - id 'org.gradle.toolchains.foojay-resolver-convention' version '1.0.0' -} - dependencyResolutionManagement { repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) repositories { - //原仓库 google { content { includeGroupByRegex("com\\.android.*") @@ -33,6 +23,8 @@ dependencyResolutionManagement { includeGroupByRegex("androidx.*") } } + maven { url 'https://maven.aliyun.com/repository/google' } + maven { url 'https://maven.aliyun.com/repository/public' } maven { url = uri("https://jitpack.io") content { @@ -40,12 +32,6 @@ dependencyResolutionManagement { } } mavenCentral() - - //镜像仓库,无法连接源仓库自行启用镜像仓库,不要提交修改 - //maven { url "https://maven-central-asia.storage-download.googleapis.com/maven2/" } - //maven { url 'https://maven.aliyun.com/repository/google' } - //maven { url 'https://maven.aliyun.com/repository/public' } - //maven { url 'https://repo.huaweicloud.com/repository/maven/' } } } rootProject.name = 'legado' From 52f3bd686293d3b94649658a47b0785513d811ad Mon Sep 17 00:00:00 2001 From: fjsuta Date: Sun, 31 May 2026 16:19:54 +0000 Subject: [PATCH 6/6] =?UTF-8?q?ci:=20=E6=B7=BB=E5=8A=A0=E7=94=A8=E6=88=B7?= =?UTF-8?q?=E6=89=8B=E5=8A=A8=E8=A7=A6=E5=8F=91=E6=9E=84=E5=BB=BAAPK?= =?UTF-8?q?=E7=9A=84=E5=B7=A5=E4=BD=9C=E6=B5=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/build-apk-for-user.yml | 105 +++++++++++++++++++++++ 1 file changed, 105 insertions(+) create mode 100644 .github/workflows/build-apk-for-user.yml diff --git a/.github/workflows/build-apk-for-user.yml b/.github/workflows/build-apk-for-user.yml new file mode 100644 index 000000000..9d6f44cd4 --- /dev/null +++ b/.github/workflows/build-apk-for-user.yml @@ -0,0 +1,105 @@ +name: Build APK for User + +on: + workflow_dispatch: + inputs: + branch: + description: '要构建的分支' + required: true + default: 'trae/solo-agent-vhkVM3' + release_tag: + description: 'Release 标签 (留空则不上传到 Release)' + required: false + default: '' + +permissions: + contents: write + +jobs: + build: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v5 + with: + ref: ${{ github.event.inputs.branch }} + fetch-depth: 0 + + - name: Set up JDK 21 + uses: actions/setup-java@v5 + with: + distribution: 'temurin' + java-version: 21 + + - name: Set up Gradle + uses: gradle/actions/setup-gradle@v4 + + - name: Clear 18PlusList.txt + run: | + echo "清空18PlusList.txt" + echo "" > $GITHUB_WORKSPACE/app/src/main/assets/18PlusList.txt + + - name: Build Debug APK + run: | + chmod +x gradlew + ./gradlew assembleAppDebug --no-daemon + env: + COMMIT_NUMBER: ${{ github.run_number }} + APP_VERSION_NAME: debug-${{ github.run_number }} + + - name: Check Build Output + run: | + APK_PATH="$GITHUB_WORKSPACE/app/build/outputs/apk/app/debug" + echo "检查 APK 输出目录: $APK_PATH" + ls -la "$APK_PATH" || true + if [ -z "$(find "$APK_PATH" -name "*.apk" -print -quit)" ]; then + echo "未找到 APK 文件!" + exit 1 + fi + + - name: Rename APK Files + run: | + APK_PATH="$GITHUB_WORKSPACE/app/build/outputs/apk/app/debug" + cd "$APK_PATH" + for f in *.apk; do + mv "$f" "legado-debug-${GITHUB_RUN_NUMBER}-${f}" + done + ls -la + + - name: Create ZIP Archive + run: | + APK_PATH="$GITHUB_WORKSPACE/app/build/outputs/apk/app/debug" + ZIP_NAME="legado-debug-apks-${GITHUB_RUN_NUMBER}.zip" + cd "$APK_PATH" + zip "$GITHUB_WORKSPACE/$ZIP_NAME" *.apk + echo "ZIP 文件已创建: $ZIP_NAME" + ls -la "$GITHUB_WORKSPACE/$ZIP_NAME" + + - name: Upload APK Artifacts + uses: actions/upload-artifact@v4 + with: + name: legado-apks-${{ github.run_number }} + path: app/build/outputs/apk/app/debug/*.apk + + - name: Upload ZIP Artifact + uses: actions/upload-artifact@v4 + with: + name: legado-apks-zip-${{ github.run_number }} + path: legado-debug-apks-*.zip + + - name: Create Release and Upload + if: ${{ github.event.inputs.release_tag != '' }} + uses: softprops/action-gh-release@v2 + with: + tag_name: ${{ github.event.inputs.release_tag }} + name: Legado Debug Build ${{ github.event.inputs.release_tag }} + body: | + 自动构建的 Debug 版本 + - 分支: ${{ github.event.inputs.branch }} + - 构建号: ${{ github.run_number }} + prerelease: true + files: | + app/build/outputs/apk/app/debug/*.apk + legado-debug-apks-*.zip + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}