From adf038a964bb9af9ded364c8a6f319b6fe314408 Mon Sep 17 00:00:00 2001 From: Sylthionys <94494882+Sylthionys@users.noreply.github.com> Date: Sun, 14 Jun 2026 17:56:05 +0800 Subject: [PATCH 01/32] :bug: (ai): constrain category recognition to synced categories --- .../ezbook/server/ai/tools/CategoryTool.kt | 77 ++++++++++-- .../org/ezbook/server/db/dao/CategoryDao.kt | 5 +- .../org/ezbook/server/tools/BillService.kt | 40 ++++--- .../server/ai/tools/CategoryToolTest.kt | 111 ++++++++++++++++++ 4 files changed, 203 insertions(+), 30 deletions(-) create mode 100644 server/src/test/java/org/ezbook/server/ai/tools/CategoryToolTest.kt diff --git a/server/src/main/java/org/ezbook/server/ai/tools/CategoryTool.kt b/server/src/main/java/org/ezbook/server/ai/tools/CategoryTool.kt index 16df609c3..a38556899 100644 --- a/server/src/main/java/org/ezbook/server/ai/tools/CategoryTool.kt +++ b/server/src/main/java/org/ezbook/server/ai/tools/CategoryTool.kt @@ -16,8 +16,11 @@ package org.ezbook.server.ai.tools import org.ezbook.server.ai.AiManager +import org.ezbook.server.constant.BillType +import org.ezbook.server.constant.DataType import org.ezbook.server.constant.DefaultData import org.ezbook.server.db.Db +import org.ezbook.server.db.model.CategoryModel import org.ezbook.server.log.ServerLog import org.ezbook.server.tools.SettingUtils import org.ezbook.server.tools.runCatchingExceptCancel @@ -40,20 +43,25 @@ class CategoryTool { * @param data 原始账单数据(JSON字符串) * @param app 来源应用 * @param dataType 数据来源类型 + * @param categories 当前账本和账单类型下的分类 */ suspend fun execute( data: String, app: String, - dataType: org.ezbook.server.constant.DataType + dataType: DataType, + categories: List ): String? { val prompt = getPrompt() - // 记录输入摘要,避免日志过长 - ServerLog.d("分类匹配请求:data=${data.take(120)}, app=$app, dataType=$dataType") + ServerLog.d("分类匹配请求:dataType=$dataType") - val categories = Db.get().categoryDao().all() + val categoryPaths = categoryPaths(categories) + if (categoryPaths.isEmpty()) { + ServerLog.d("当前账本和账单类型没有可用分类,跳过AI分类") + return null + } + val fallback = fallbackCategory(categoryPaths) // 记录分类候选规模 - ServerLog.d("分类候选统计:total=${categories.size}") - val categoryNames = categories.joinToString(",") { it.name.toString() } + ServerLog.d("分类候选统计:total=${categoryPaths.size}") // 组装上下文信息,帮助 AI 进行语义分类 val user = """ Input: @@ -66,23 +74,66 @@ Input: ``` - Category Data: ``` - $categoryNames + ${categoryPaths.joinToString(",")} ``` """.trimIndent() // 调用 AI 进行分类选择 ServerLog.d("调用AI进行分类匹配...") - return runCatchingExceptCancel { + val response = runCatchingExceptCancel { val resp = AiManager.getInstance().request(prompt, user).getOrThrow() if (resp.isEmpty()) { // AI 无返回 ServerLog.d("AI分类返回空响应") - return null } - // 记录 AI 的原始输出(期望为单行纯文本) - ServerLog.d("AI分类结果:$resp") resp - }.getOrDefault("其他") + }.getOrElse { + ServerLog.d("AI分类请求失败,使用当前账本的兜底分类") + return fallback + } + + val selected = selectCategory(response, categoryPaths) + if (selected == null) { + ServerLog.d("AI分类结果不在当前账本候选中,使用兜底分类") + } + return selected ?: fallback + } + + suspend fun loadCategories(bookRemoteId: String, billType: BillType): List { + val categoryType = categoryType(billType) ?: return emptyList() + return Db.get().categoryDao().loadByBookAndType(bookRemoteId, categoryType.name) + } + + internal companion object { + fun categoryType(billType: BillType): BillType? = when { + billType.name.startsWith(BillType.Expend.name) -> BillType.Expend + billType.name.startsWith(BillType.Income.name) -> BillType.Income + else -> null + } + + fun categoryNames(categories: List): List = + categories.mapNotNull { it.name?.trim()?.takeIf(String::isNotEmpty) }.distinct() + + fun categoryPaths(categories: List): List { + val namesByRemoteId = categories + .filter { it.remoteId.isNotBlank() } + .associate { it.remoteId to it.name?.trim().orEmpty() } + return categories.mapNotNull { category -> + val name = category.name?.trim()?.takeIf(String::isNotEmpty) ?: return@mapNotNull null + val parentName = namesByRemoteId[category.remoteParentId].orEmpty() + if (parentName.isNotEmpty()) "$parentName - $name" else name + }.distinct() + } + + fun categoryExists(categoryName: String, categories: List): Boolean { + return categoryName.trim() in categoryPaths(categories) + } + + fun selectCategory(response: String, categoryNames: List): String? = + response.trim().takeIf(categoryNames::contains) + + fun fallbackCategory(categoryNames: List): String? = + categoryNames.firstOrNull { it == "其他" || it == "其它" } } -} \ No newline at end of file +} diff --git a/server/src/main/java/org/ezbook/server/db/dao/CategoryDao.kt b/server/src/main/java/org/ezbook/server/db/dao/CategoryDao.kt index a40b3234a..093bdfaf2 100644 --- a/server/src/main/java/org/ezbook/server/db/dao/CategoryDao.kt +++ b/server/src/main/java/org/ezbook/server/db/dao/CategoryDao.kt @@ -29,6 +29,9 @@ interface CategoryDao { @Query("SELECT * FROM CategoryModel WHERE remoteBookId=:book AND type = :type AND remoteParentId=:parent ORDER BY id ") suspend fun load(book: String, type: String, parent: String): List + @Query("SELECT * FROM CategoryModel WHERE remoteBookId=:book AND type = :type ORDER BY id") + suspend fun loadByBookAndType(book: String, type: String): List + @Query("SELECT * FROM CategoryModel WHERE name =:name AND (:book IS NULL OR remoteBookId = :book) AND (:type IS NULL OR type = :type) ORDER BY id DESC LIMIT 1") suspend fun getByName(book: String?, type: String?, name: String): CategoryModel? @@ -60,4 +63,4 @@ interface CategoryDao { @Query("SELECT * FROM CategoryModel") suspend fun all(): List -} \ No newline at end of file +} diff --git a/server/src/main/java/org/ezbook/server/tools/BillService.kt b/server/src/main/java/org/ezbook/server/tools/BillService.kt index 182625511..279e68b16 100644 --- a/server/src/main/java/org/ezbook/server/tools/BillService.kt +++ b/server/src/main/java/org/ezbook/server/tools/BillService.kt @@ -547,35 +547,43 @@ class BillService( JsonObject::class.java ) }.getOrNull() - ServerLog.d( - "Category result: book=${ - categoryJson.safeGetStringNonBlank( - "book", - "" - ) - }, cate=${categoryJson.safeGetStringNonBlank("category", "")}" - ) + ServerLog.d("Category rule evaluated") // 设置账本名称与分类(优先规则结果,否则默认值) // 将账本指针(如"默认账本")解析为数据库中真实存在的账本名称 val rawBookName = categoryJson.safeGetStringNonBlank("book", SettingUtils.bookName()) - bill.bookName = resolveBookByNameOrDefault(rawBookName).name + val resolvedBook = resolveBookByNameOrDefault(rawBookName) + bill.bookName = resolvedBook.name bill.cateName = categoryJson.safeGetStringNonBlank("category", "其他") bill.remark = categoryJson.safeGetStringNonBlank("remark", "") + val categoryTool = CategoryTool() + val categories = categoryTool.loadCategories(resolvedBook.remoteId, bill.type) + + // 先应用用户分类映射,再判断分类规则结果是否真实存在于当前账本。 + CategoryProcessor().setCategoryMap(bill) + var categoryExists = CategoryTool.categoryExists(bill.cateName, categories) + // AI分类识别需要总开关和分类开关同时开启 - if (!bill.hasValidCategory() && + if (!categoryExists && SettingUtils.featureAiAvailable() && SettingUtils.aiCategoryRecognition() ) { - bill.cateName = CategoryTool().execute( + bill.cateName = categoryTool.execute( win.toString(), bill.app, - dataType - ).takeUnless { it.isNullOrEmpty() } ?: "其他" - ServerLog.d("AI category: ${bill.cateName}") + dataType, + categories + ).takeUnless { it.isNullOrEmpty() } ?: bill.cateName + ServerLog.d("AI category evaluated") + + CategoryProcessor().setCategoryMap(bill) + categoryExists = CategoryTool.categoryExists(bill.cateName, categories) } - // 设置分类映射、查找 - CategoryProcessor().setCategoryMap(bill) + if (!categoryExists) { + bill.cateName = CategoryTool.fallbackCategory(CategoryTool.categoryPaths(categories)) + ?: bill.cateName + ServerLog.d("Category fallback evaluated") + } } /** diff --git a/server/src/test/java/org/ezbook/server/ai/tools/CategoryToolTest.kt b/server/src/test/java/org/ezbook/server/ai/tools/CategoryToolTest.kt new file mode 100644 index 000000000..a0c2dc1db --- /dev/null +++ b/server/src/test/java/org/ezbook/server/ai/tools/CategoryToolTest.kt @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2026 ankio + * Licensed under the Apache License, Version 3.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-3.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ezbook.server.ai.tools + +import org.ezbook.server.constant.BillType +import org.ezbook.server.db.model.CategoryModel +import org.junit.Assert.assertEquals +import org.junit.Assert.assertNull +import org.junit.Test + +class CategoryToolTest { + + @Test + fun categoryType_mapsBillSubtypesToStoredCategoryTypes() { + BillType.entries + .filter { it.name.startsWith(BillType.Expend.name) } + .forEach { assertEquals(BillType.Expend, CategoryTool.categoryType(it)) } + BillType.entries + .filter { it.name.startsWith(BillType.Income.name) } + .forEach { assertEquals(BillType.Income, CategoryTool.categoryType(it)) } + assertNull(CategoryTool.categoryType(BillType.Transfer)) + } + + @Test + fun categoryNames_removesBlankAndDuplicateNames() { + val categories = listOf( + category(" 通勤 "), + category(""), + category(null), + category("通勤"), + category("其它") + ) + + assertEquals(listOf("通勤", "其它"), CategoryTool.categoryNames(categories)) + } + + @Test + fun categoryPaths_preservesHierarchyAndDuplicateChildNames() { + val commute = parent("通勤", "parent-commute") + val travel = parent("旅行", "parent-travel") + val commuteTaxi = child("打车", "child-commute-taxi", commute.remoteId) + val travelTaxi = child("打车", "child-travel-taxi", travel.remoteId) + + assertEquals( + listOf("通勤", "旅行", "通勤 - 打车", "旅行 - 打车"), + CategoryTool.categoryPaths(listOf(commute, travel, commuteTaxi, travelTaxi)) + ) + } + + @Test + fun selectCategory_acceptsOnlyExistingCategory() { + val categories = listOf("通勤", "巴士", "地铁", "其它") + + assertEquals("地铁", CategoryTool.selectCategory(" 地铁\n", categories)) + assertNull(CategoryTool.selectCategory("公交地铁", categories)) + assertNull(CategoryTool.selectCategory("分类:地铁", categories)) + } + + @Test + fun categoryExists_validatesSingleAndParentChildCategories() { + val parent = category("通勤").apply { + remoteId = "parent-commute" + remoteParentId = "-1" + } + val child = category("地铁").apply { + remoteId = "child-metro" + remoteParentId = "parent-commute" + } + val categories = listOf(parent, child, category("其它")) + + assertEquals(true, CategoryTool.categoryExists("通勤", categories)) + assertEquals(true, CategoryTool.categoryExists("通勤 - 地铁", categories)) + assertEquals(false, CategoryTool.categoryExists("地铁", categories)) + assertEquals(false, CategoryTool.categoryExists("公交地铁", categories)) + assertEquals(false, CategoryTool.categoryExists("日常 - 地铁", categories)) + } + + @Test + fun fallbackCategory_usesExistingOtherCategory() { + assertEquals("其它", CategoryTool.fallbackCategory(listOf("通勤", "其它"))) + assertEquals("其他", CategoryTool.fallbackCategory(listOf("通勤", "其他"))) + assertNull(CategoryTool.fallbackCategory(listOf("通勤", "地铁"))) + } + + private fun category(name: String?) = CategoryModel().apply { + this.name = name + } + + private fun parent(name: String, remoteId: String) = category(name).apply { + this.remoteId = remoteId + remoteParentId = "-1" + } + + private fun child(name: String, remoteId: String, parentRemoteId: String) = category(name).apply { + this.remoteId = remoteId + remoteParentId = parentRemoteId + } +} From 4317bd9c406725af1d18e1128d765c3b794a75d2 Mon Sep 17 00:00:00 2001 From: Sylthionys <94494882+Sylthionys@users.noreply.github.com> Date: Sun, 14 Jun 2026 17:56:10 +0800 Subject: [PATCH 02/32] :bug: (tap): migrate TensorFlow Lite runtime to LiteRT --- gradle/libs.versions.toml | 6 +-- tap/build.gradle.kts | 4 +- .../net/ankio/tap/ExampleInstrumentedTest.kt | 24 --------- .../ankio/tap/TapTfRuntimeInstrumentedTest.kt | 50 +++++++++++++++++++ 4 files changed, 55 insertions(+), 29 deletions(-) delete mode 100644 tap/src/androidTest/java/net/ankio/tap/ExampleInstrumentedTest.kt create mode 100644 tap/src/androidTest/java/net/ankio/tap/TapTfRuntimeInstrumentedTest.kt diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index f3e516cae..2b6794f81 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -68,7 +68,7 @@ materialThemeBuilder = "1.5.1" autoresconfig = "1.2.2" lifecycleService = "2.9.2" # Machine learning (Columbus-style back tap detection) -tensorflowLite = "2.14.0" +liteRt = "2.1.0" junit = "4.13.2" junitVersion = "1.1.5" espressoCore = "3.5.1" @@ -133,7 +133,7 @@ junit = { group = "junit", name = "junit", version.ref = "junit" } androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" } androidx-espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" } androidx-appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompatVersion" } -tensorflow-lite = { module = "org.tensorflow:tensorflow-lite", version.ref = "tensorflowLite" } +litert = { module = "com.google.ai.edge.litert:litert", version.ref = "liteRt" } [plugins] androidApplication = { id = "com.android.application", version.ref = "agp" } @@ -142,4 +142,4 @@ androidLibrary = { id = "com.android.library", version.ref = "agp" } ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" } materialThemeBuilder = { id = "dev.rikka.tools.materialthemebuilder", version.ref = "materialThemeBuilder" } autoresconfig = { id = "dev.rikka.tools.autoresconfig", version.ref = "autoresconfig" } -kotlinJvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" } \ No newline at end of file +kotlinJvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" } diff --git a/tap/build.gradle.kts b/tap/build.gradle.kts index 6cfca21fd..37090adfe 100644 --- a/tap/build.gradle.kts +++ b/tap/build.gradle.kts @@ -28,7 +28,7 @@ android { dependencies { // Columbus / TapTap hybrid pipeline: ML inference on device - implementation(libs.tensorflow.lite) + implementation(libs.litert) implementation(libs.androidx.appcompat) implementation(libs.core.ktx) @@ -36,4 +36,4 @@ dependencies { testImplementation(libs.junit) androidTestImplementation(libs.androidx.espresso.core) androidTestImplementation(libs.androidx.junit) -} \ No newline at end of file +} diff --git a/tap/src/androidTest/java/net/ankio/tap/ExampleInstrumentedTest.kt b/tap/src/androidTest/java/net/ankio/tap/ExampleInstrumentedTest.kt deleted file mode 100644 index 8cbe00fc1..000000000 --- a/tap/src/androidTest/java/net/ankio/tap/ExampleInstrumentedTest.kt +++ /dev/null @@ -1,24 +0,0 @@ -package net.ankio.tap - -import androidx.test.platform.app.InstrumentationRegistry -import androidx.test.ext.junit.runners.AndroidJUnit4 - -import org.junit.Test -import org.junit.runner.RunWith - -import org.junit.Assert.* - -/** - * Instrumented test, which will execute on an Android device. - * - * See [testing documentation](http://d.android.com/tools/testing). - */ -@RunWith(AndroidJUnit4::class) -class ExampleInstrumentedTest { - @Test - fun useAppContext() { - // Context of the app under test. - val appContext = InstrumentationRegistry.getInstrumentation().targetContext - assertEquals("net.ankio.tap.test", appContext.packageName) - } -} \ No newline at end of file diff --git a/tap/src/androidTest/java/net/ankio/tap/TapTfRuntimeInstrumentedTest.kt b/tap/src/androidTest/java/net/ankio/tap/TapTfRuntimeInstrumentedTest.kt new file mode 100644 index 000000000..4b16976a6 --- /dev/null +++ b/tap/src/androidTest/java/net/ankio/tap/TapTfRuntimeInstrumentedTest.kt @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2026 ankio(ankio@ankio.net) + * Licensed under the Apache License, Version 3.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-3.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.ankio.tap + +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.platform.app.InstrumentationRegistry +import org.junit.Assert.assertTrue +import org.junit.Test +import org.junit.runner.RunWith +import org.tensorflow.lite.Interpreter +import java.io.FileInputStream +import java.nio.channels.FileChannel + +@RunWith(AndroidJUnit4::class) +class TapTfRuntimeInstrumentedTest { + + @Test + fun bundledModelsLoadWithLiteRtInterpreter() { + val assets = InstrumentationRegistry.getInstrumentation().targetContext.assets + + TapBuiltinModel.entries.forEach { model -> + assets.openFd(model.assetPath).use { descriptor -> + FileInputStream(descriptor.fileDescriptor).use { stream -> + val buffer = stream.channel.map( + FileChannel.MapMode.READ_ONLY, + descriptor.startOffset, + descriptor.declaredLength, + ) + Interpreter(buffer).use { interpreter -> + assertTrue("${model.id} has no input tensors", interpreter.inputTensorCount > 0) + assertTrue("${model.id} has no output tensors", interpreter.outputTensorCount > 0) + } + } + } + } + } +} From 96891d8c9a92ad978d1f0c4aaf72ced33e327631 Mon Sep 17 00:00:00 2001 From: Sylthionys <94494882+Sylthionys@users.noreply.github.com> Date: Sun, 14 Jun 2026 17:56:23 +0800 Subject: [PATCH 03/32] :rotating_light: (build): clean up Gradle and compiler warnings --- .gitattributes | 5 +++++ app/build.gradle.kts | 17 ++++++++++++----- app/src/main/AndroidManifest.xml | 3 +-- dex/build.gradle.kts | 4 ---- server/build.gradle.kts | 7 ------- .../java/org/ezbook/server/constant/Currency.kt | 2 +- .../java/org/ezbook/server/db/AppDatabase.kt | 2 ++ .../java/org/ezbook/server/server/BillRoutes.kt | 6 +++--- .../ezbook/server/tools/StatisticsService.kt | 14 +++++++------- .../org/ezbook/server/tools/SummaryService.kt | 6 +++--- 10 files changed, 34 insertions(+), 32 deletions(-) create mode 100644 .gitattributes diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 000000000..a40bfbbf1 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,5 @@ +.gitattributes text eol=lf +*.kt text eol=lf +*.kts text eol=lf +*.toml text eol=lf +*.xml text eol=lf diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 4bcb2cd1b..3de4494bf 100755 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -51,6 +51,13 @@ materialThemeBuilder { generatePalette = true } +val appVersionCode = calculateVersionCode() +val appVersionName = "4.0.2" + +base { + archivesName.set("app-$appVersionName($appVersionCode)") +} + android { namespace = "net.ankio.auto" compileSdk = 36 @@ -60,11 +67,10 @@ android { applicationId = "net.ankio.auto" minSdk = 29 targetSdk = 36 - versionCode = calculateVersionCode() - versionName = "4.0.2" + versionCode = appVersionCode + versionName = appVersionName testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" flavorDimensions += "version" - setProperty("archivesBaseName", "app-${versionName}(${versionCode})") @@ -117,8 +123,9 @@ android { ) } - // 如果以后要过滤 .so,改用 jniLibs.excludes += "lib/**/foo.so" - // jniLibs { excludes += "lib/**/yourNative.so" } + jniLibs { + useLegacyPackaging = true + } } diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 97cd431bc..ea4fc146b 100755 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -35,7 +35,6 @@ android:allowBackup="true" android:dataExtractionRules="@xml/data_extraction_rules" android:enableOnBackInvokedCallback="true" - android:extractNativeLibs="true" android:fullBackupContent="@xml/backup_rules" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" @@ -167,4 +166,4 @@ - \ No newline at end of file + diff --git a/dex/build.gradle.kts b/dex/build.gradle.kts index a7c4e4378..9108de145 100644 --- a/dex/build.gradle.kts +++ b/dex/build.gradle.kts @@ -17,10 +17,6 @@ kotlin { // KotlinJvmDsl jvmToolchain(21) } -repositories { - mavenCentral() -} - dependencies { implementation(libs.dexlib2) } diff --git a/server/build.gradle.kts b/server/build.gradle.kts index 9858fe070..c47f99a3c 100644 --- a/server/build.gradle.kts +++ b/server/build.gradle.kts @@ -44,13 +44,6 @@ android { } } -/* ---------- repositories ---------- */ -repositories { - google() // Required for Android dependencies - mavenCentral() // Required for KSP and other dependencies - maven { url = uri("https://www.jitpack.io") } -} - /* ---------- dependencies ---------- */ dependencies { diff --git a/server/src/main/java/org/ezbook/server/constant/Currency.kt b/server/src/main/java/org/ezbook/server/constant/Currency.kt index 23d8bf26a..3f4b5762d 100644 --- a/server/src/main/java/org/ezbook/server/constant/Currency.kt +++ b/server/src/main/java/org/ezbook/server/constant/Currency.kt @@ -32,7 +32,7 @@ private fun currencyIcon(code: String): String = * - 包含所有可用币种;用户通过设置页面勾选常用币种,下拉选择器仅展示常用项 */ enum class Currency( - @StringRes val currencyNameResId: Int, + @param:StringRes val currencyNameResId: Int, val currencyIconUrl: String ) { // 人民币(中国) diff --git a/server/src/main/java/org/ezbook/server/db/AppDatabase.kt b/server/src/main/java/org/ezbook/server/db/AppDatabase.kt index bfa2e3be9..4356f8a4e 100755 --- a/server/src/main/java/org/ezbook/server/db/AppDatabase.kt +++ b/server/src/main/java/org/ezbook/server/db/AppDatabase.kt @@ -1,3 +1,5 @@ +@file:Suppress("PARAMETER_NAME_CHANGED_ON_OVERRIDE") + /* * Copyright (C) 2023 ankio(ankio@ankio.net) * Licensed under the Apache License, Version 3.0 (the "License"); diff --git a/server/src/main/java/org/ezbook/server/server/BillRoutes.kt b/server/src/main/java/org/ezbook/server/server/BillRoutes.kt index 447bac5bf..371883a23 100644 --- a/server/src/main/java/org/ezbook/server/server/BillRoutes.kt +++ b/server/src/main/java/org/ezbook/server/server/BillRoutes.kt @@ -263,8 +263,8 @@ fun Route.billRoutes() { calendar.add(java.util.Calendar.MONTH, 1) val endTime = calendar.timeInMillis - val income = Db.get().billInfoDao().getMonthlyIncome(startTime, endTime) ?: 0.0 - val expense = Db.get().billInfoDao().getMonthlyExpense(startTime, endTime) ?: 0.0 + val income = Db.get().billInfoDao().getMonthlyIncome(startTime, endTime) + val expense = Db.get().billInfoDao().getMonthlyExpense(startTime, endTime) call.respond(ResultModel.ok(mapOf("income" to income, "expense" to expense))) } @@ -322,4 +322,4 @@ fun Route.billRoutes() { call.respond(ResultModel.ok("OK")) } } -} \ No newline at end of file +} diff --git a/server/src/main/java/org/ezbook/server/tools/StatisticsService.kt b/server/src/main/java/org/ezbook/server/tools/StatisticsService.kt index 55e2ac908..0d5092169 100644 --- a/server/src/main/java/org/ezbook/server/tools/StatisticsService.kt +++ b/server/src/main/java/org/ezbook/server/tools/StatisticsService.kt @@ -41,15 +41,15 @@ object StatisticsService { val periodDays = ((endTime - startTime) / (24L * 3600_000L)).coerceAtLeast(1) // 查询本期数据 - val totalIncome = dao.getMonthlyIncome(startTime, endTime) ?: 0.0 - val totalExpense = dao.getMonthlyExpense(startTime, endTime) ?: 0.0 + val totalIncome = dao.getMonthlyIncome(startTime, endTime) + val totalExpense = dao.getMonthlyExpense(startTime, endTime) val netIncome = totalIncome - totalExpense // 查询上期数据(用于同比) val duration = endTime - startTime val prevStart = startTime - duration - val prevIncome = dao.getMonthlyIncome(prevStart, startTime) ?: 0.0 - val prevExpense = dao.getMonthlyExpense(prevStart, startTime) ?: 0.0 + val prevIncome = dao.getMonthlyIncome(prevStart, startTime) + val prevExpense = dao.getMonthlyExpense(prevStart, startTime) // 查询分类统计(查询后删掉父类,仅保留子类;无子类则保留原分类) val expenseCategoryRows = dao.getExpenseCategoryStats(startTime, endTime) @@ -276,9 +276,9 @@ object StatisticsService { * 构建消费时间洞察数据 */ private fun buildTimeInsight(hourStats: Map): Map { - val hours = hourStats["hours"] as? List ?: emptyList() - val amounts = hourStats["amounts"] as? List ?: emptyList() - val counts = hourStats["counts"] as? List ?: emptyList() + val hours = (hourStats["hours"] as? List<*>)?.filterIsInstance().orEmpty() + val amounts = (hourStats["amounts"] as? List<*>)?.filterIsInstance().orEmpty() + val counts = (hourStats["counts"] as? List<*>)?.filterIsInstance().orEmpty() var peakIndex = 0 var peakAmount = 0.0 diff --git a/server/src/main/java/org/ezbook/server/tools/SummaryService.kt b/server/src/main/java/org/ezbook/server/tools/SummaryService.kt index 293e5e238..9de1545d5 100644 --- a/server/src/main/java/org/ezbook/server/tools/SummaryService.kt +++ b/server/src/main/java/org/ezbook/server/tools/SummaryService.kt @@ -28,7 +28,6 @@ import java.util.* */ object SummaryService { private val gson = Gson() - private val dateFormat = SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()) /** * 周期数据 @@ -50,8 +49,8 @@ object SummaryService { suspend fun getPeriodData(startTime: Long, endTime: Long): PeriodData { val dao = Db.get().billInfoDao() - val income = dao.getMonthlyIncome(startTime, endTime) ?: 0.0 - val expense = dao.getMonthlyExpense(startTime, endTime) ?: 0.0 + val income = dao.getMonthlyIncome(startTime, endTime) + val expense = dao.getMonthlyExpense(startTime, endTime) val netIncome = income - expense val savingsRate = if (income > 0) (netIncome / income) * 100 else 0.0 val expenseIncomeRatio = if (income > 0) (expense / income) * 100 else 0.0 @@ -101,6 +100,7 @@ object SummaryService { } val periodDays = ((endTime - startTime) / (24L * 3600_000L)).coerceAtLeast(1) + val dateFormat = SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()) // 构建分类历史对比 val categoryHistory = current.expenseCategories.map { curr -> From 4f7d62c31dde5ae9f3a5ec3c80b65266d55526fd Mon Sep 17 00:00:00 2001 From: Sylthionys <94494882+Sylthionys@users.noreply.github.com> Date: Mon, 15 Jun 2026 01:46:06 +0800 Subject: [PATCH 04/32] :bug: (ai): reject ambiguous child category mappings --- .../ezbook/server/ai/tools/CategoryTool.kt | 20 +++- .../org/ezbook/server/tools/BillService.kt | 18 ++-- .../ezbook/server/tools/CategoryProcessor.kt | 44 +++----- .../server/ai/tools/CategoryToolTest.kt | 17 +++ .../server/tools/CategoryProcessorTest.kt | 100 ++++++++++++++++++ 5 files changed, 164 insertions(+), 35 deletions(-) create mode 100644 server/src/test/java/org/ezbook/server/tools/CategoryProcessorTest.kt diff --git a/server/src/main/java/org/ezbook/server/ai/tools/CategoryTool.kt b/server/src/main/java/org/ezbook/server/ai/tools/CategoryTool.kt index a38556899..70ab31585 100644 --- a/server/src/main/java/org/ezbook/server/ai/tools/CategoryTool.kt +++ b/server/src/main/java/org/ezbook/server/ai/tools/CategoryTool.kt @@ -93,7 +93,7 @@ Input: return fallback } - val selected = selectCategory(response, categoryPaths) + val selected = resolveCategoryPath(response, categories) if (selected == null) { ServerLog.d("AI分类结果不在当前账本候选中,使用兜底分类") } @@ -130,6 +130,24 @@ Input: return categoryName.trim() in categoryPaths(categories) } + fun resolveCategoryPath(categoryName: String, categories: List): String? { + val name = categoryName.trim() + if (name.isEmpty()) return null + + val paths = categoryPaths(categories) + paths.firstOrNull { it == name }?.let { return it } + + val namesByRemoteId = categories + .filter { it.remoteId.isNotBlank() } + .associate { it.remoteId to it.name?.trim().orEmpty() } + return categories.mapNotNull { category -> + val childName = category.name?.trim()?.takeIf(String::isNotEmpty) + ?: return@mapNotNull null + val parentName = namesByRemoteId[category.remoteParentId].orEmpty() + if (childName == name && parentName.isNotEmpty()) "$parentName - $childName" else null + }.distinct().singleOrNull() + } + fun selectCategory(response: String, categoryNames: List): String? = response.trim().takeIf(categoryNames::contains) diff --git a/server/src/main/java/org/ezbook/server/tools/BillService.kt b/server/src/main/java/org/ezbook/server/tools/BillService.kt index 279e68b16..4626811c1 100644 --- a/server/src/main/java/org/ezbook/server/tools/BillService.kt +++ b/server/src/main/java/org/ezbook/server/tools/BillService.kt @@ -32,6 +32,7 @@ import org.ezbook.server.db.AppDatabase import org.ezbook.server.db.Db import org.ezbook.server.db.model.AppDataModel import org.ezbook.server.db.model.BillInfoModel +import org.ezbook.server.db.model.CategoryModel import org.ezbook.server.db.model.CurrencyModel import org.ezbook.server.engine.JsExecutor import org.ezbook.server.engine.RuleGenerator @@ -53,6 +54,11 @@ import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.withLock import org.ezbook.server.log.ServerLog +internal suspend fun resolveMappedCategory( + bill: BillInfoModel, + categories: List, + processor: CategoryProcessor = CategoryProcessor() +): String? = processor.setCategoryMap(bill, categories) /** * 账单核心业务服务类 @@ -559,11 +565,11 @@ class BillService( val categories = categoryTool.loadCategories(resolvedBook.remoteId, bill.type) // 先应用用户分类映射,再判断分类规则结果是否真实存在于当前账本。 - CategoryProcessor().setCategoryMap(bill) - var categoryExists = CategoryTool.categoryExists(bill.cateName, categories) + var resolvedCategory = resolveMappedCategory(bill, categories) + val needsAiCategory = !bill.hasValidCategory() // AI分类识别需要总开关和分类开关同时开启 - if (!categoryExists && + if ((needsAiCategory || resolvedCategory == null) && SettingUtils.featureAiAvailable() && SettingUtils.aiCategoryRecognition() ) { @@ -575,11 +581,10 @@ class BillService( ).takeUnless { it.isNullOrEmpty() } ?: bill.cateName ServerLog.d("AI category evaluated") - CategoryProcessor().setCategoryMap(bill) - categoryExists = CategoryTool.categoryExists(bill.cateName, categories) + resolvedCategory = resolveMappedCategory(bill, categories) } - if (!categoryExists) { + if (resolvedCategory == null) { bill.cateName = CategoryTool.fallbackCategory(CategoryTool.categoryPaths(categories)) ?: bill.cateName ServerLog.d("Category fallback evaluated") @@ -685,4 +690,3 @@ class BillService( } } } - diff --git a/server/src/main/java/org/ezbook/server/tools/CategoryProcessor.kt b/server/src/main/java/org/ezbook/server/tools/CategoryProcessor.kt index 87839460a..072b5a0df 100644 --- a/server/src/main/java/org/ezbook/server/tools/CategoryProcessor.kt +++ b/server/src/main/java/org/ezbook/server/tools/CategoryProcessor.kt @@ -15,8 +15,11 @@ package org.ezbook.server.tools +import org.ezbook.server.ai.tools.CategoryTool import org.ezbook.server.db.Db import org.ezbook.server.db.model.BillInfoModel +import org.ezbook.server.db.model.CategoryMapModel +import org.ezbook.server.db.model.CategoryModel /** * 分类处理工具 @@ -26,36 +29,23 @@ import org.ezbook.server.db.model.BillInfoModel * 2) 统一输出格式为:"父类 - 子类";若无子类则返回父类本身 * 3) 从分类名中解析父/子分类;若只有子类,通过映射后的结果获取父类 */ -class CategoryProcessor { +class CategoryProcessor( + private val loadMappings: suspend () -> List = { + Db.get().categoryMapDao().loadWithoutLimit() + } +) { - suspend fun setCategoryMap(billInfoModel: BillInfoModel) { + suspend fun setCategoryMap( + billInfoModel: BillInfoModel, + categories: List + ): String? { // 1) 先做字符串映射替换(长度降序),不改变格式 billInfoModel.cateName = mapCategory(billInfoModel.cateName) - // 2) 若当前没有子类,尝试从分类表判断它其实是子类 - val (parent, child) = billInfoModel.categoryPair() - if (child.isEmpty()) { - val typeName = billInfoModel.type.name - // 按名称+类型查询(book 传 null,避免因 remoteBookId 不可得而漏判) - val model = runCatchingExceptCancel { - // bookName 已在 BillService.categorize() 中解析为真实名称,直接查找 - val book = Db.get().bookNameDao().load() - .firstOrNull { it.name == billInfoModel.bookName } - Db.get().categoryDao().getByName(book?.remoteId ?: "", typeName, parent) - }.getOrNull() - - if (model != null && model.isChild()) { - // 直接按 remoteId 查询父类,避免全表扫描 - val parentModel = runCatchingExceptCancel { - Db.get().categoryDao().getByRemoteId(model.remoteParentId) - }.getOrNull() - val parentName = parentModel?.name?.trim().orEmpty() - val childName = model.name?.trim().orEmpty() - if (parentName.isNotEmpty() && childName.isNotEmpty()) { - billInfoModel.cateName = "$parentName - $childName" - } - } - } + // 2) 仅当当前账本中存在唯一匹配路径时,才展开裸子分类名。 + val resolvedCategory = CategoryTool.resolveCategoryPath(billInfoModel.cateName, categories) + resolvedCategory?.let { billInfoModel.cateName = it } + return resolvedCategory } /** @@ -67,7 +57,7 @@ class CategoryProcessor { private suspend fun mapCategory(original: String): String { if (original.isEmpty()) return original - val mappings = runCatchingExceptCancel { Db.get().categoryMapDao().loadWithoutLimit() } + val mappings = runCatchingExceptCancel { loadMappings() } .getOrNull() .orEmpty() diff --git a/server/src/test/java/org/ezbook/server/ai/tools/CategoryToolTest.kt b/server/src/test/java/org/ezbook/server/ai/tools/CategoryToolTest.kt index a0c2dc1db..9df62d6f2 100644 --- a/server/src/test/java/org/ezbook/server/ai/tools/CategoryToolTest.kt +++ b/server/src/test/java/org/ezbook/server/ai/tools/CategoryToolTest.kt @@ -88,11 +88,28 @@ class CategoryToolTest { assertEquals(false, CategoryTool.categoryExists("日常 - 地铁", categories)) } + @Test + fun resolveCategoryPath_normalizesOnlyUnambiguousChildNames() { + val commute = parent("通勤", "parent-commute") + val travel = parent("旅行", "parent-travel") + val metro = child("地铁", "child-metro", commute.remoteId) + val commuteTaxi = child("打车", "child-commute-taxi", commute.remoteId) + val travelTaxi = child("打车", "child-travel-taxi", travel.remoteId) + val categories = listOf(commute, travel, metro, commuteTaxi, travelTaxi) + + assertEquals("通勤", CategoryTool.resolveCategoryPath("通勤", categories)) + assertEquals("通勤 - 地铁", CategoryTool.resolveCategoryPath("地铁", categories)) + assertEquals("旅行 - 打车", CategoryTool.resolveCategoryPath("旅行 - 打车", categories)) + assertNull(CategoryTool.resolveCategoryPath("打车", categories)) + assertNull(CategoryTool.resolveCategoryPath("公交", categories)) + } + @Test fun fallbackCategory_usesExistingOtherCategory() { assertEquals("其它", CategoryTool.fallbackCategory(listOf("通勤", "其它"))) assertEquals("其他", CategoryTool.fallbackCategory(listOf("通勤", "其他"))) assertNull(CategoryTool.fallbackCategory(listOf("通勤", "地铁"))) + assertNull(CategoryTool.fallbackCategory(emptyList())) } private fun category(name: String?) = CategoryModel().apply { diff --git a/server/src/test/java/org/ezbook/server/tools/CategoryProcessorTest.kt b/server/src/test/java/org/ezbook/server/tools/CategoryProcessorTest.kt new file mode 100644 index 000000000..c0adcc3fb --- /dev/null +++ b/server/src/test/java/org/ezbook/server/tools/CategoryProcessorTest.kt @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2026 ankio + * Licensed under the Apache License, Version 3.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-3.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.ezbook.server.tools + +import kotlinx.coroutines.runBlocking +import org.ezbook.server.db.model.BillInfoModel +import org.ezbook.server.db.model.CategoryMapModel +import org.ezbook.server.db.model.CategoryModel +import org.junit.Assert.assertEquals +import org.junit.Assert.assertNull +import org.junit.Test + +class CategoryProcessorTest { + + @Test + fun resolveMappedCategory_keepsDuplicateChildNamesAmbiguous() = runBlocking { + val bill = BillInfoModel().apply { cateName = "打车" } + + val resolved = resolveMappedCategory(bill, duplicateTaxiCategories(), processor()) + + assertNull(resolved) + assertEquals("打车", bill.cateName) + } + + @Test + fun resolveMappedCategory_expandsOnlyUniqueChildNames() = runBlocking { + val commute = parent("通勤", "parent-commute") + val metro = child("地铁", "child-metro", commute.remoteId) + val bill = BillInfoModel().apply { cateName = "地铁" } + + val resolved = resolveMappedCategory(bill, listOf(commute, metro), processor()) + + assertEquals("通勤 - 地铁", resolved) + assertEquals("通勤 - 地铁", bill.cateName) + } + + @Test + fun resolveMappedCategory_appliesExplicitMappingBeforeUniqueResolution() = runBlocking { + val commute = parent("通勤", "parent-commute") + val travel = parent("旅行", "parent-travel") + val commuteTaxi = child("打车", "child-commute-taxi", commute.remoteId) + val travelTaxi = child("打车", "child-travel-taxi", travel.remoteId) + val bill = BillInfoModel().apply { cateName = "出租车" } + val mapping = CategoryMapModel().apply { + name = "出租车" + mapName = "旅行 - 打车" + } + + val resolved = resolveMappedCategory( + bill, + listOf(commute, travel, commuteTaxi, travelTaxi), + processor(listOf(mapping)) + ) + + assertEquals("旅行 - 打车", resolved) + assertEquals("旅行 - 打车", bill.cateName) + } + + private fun processor(mappings: List = emptyList()) = + CategoryProcessor { mappings } + + private fun duplicateTaxiCategories(): List { + val commute = parent("通勤", "parent-commute") + val travel = parent("旅行", "parent-travel") + return listOf( + commute, + travel, + child("打车", "child-commute-taxi", commute.remoteId), + child("打车", "child-travel-taxi", travel.remoteId) + ) + } + + private fun category(name: String) = CategoryModel().apply { + this.name = name + } + + private fun parent(name: String, remoteId: String) = category(name).apply { + this.remoteId = remoteId + remoteParentId = "-1" + } + + private fun child(name: String, remoteId: String, parentRemoteId: String) = + category(name).apply { + this.remoteId = remoteId + remoteParentId = parentRemoteId + } +} From 99d7e1b8d5f92cdb886be4d7442ad2288395df79 Mon Sep 17 00:00:00 2001 From: Sylthionys <94494882+Sylthionys@users.noreply.github.com> Date: Mon, 15 Jun 2026 01:46:14 +0800 Subject: [PATCH 05/32] :bug: (server): avoid duplicate local server initialization --- .../net/ankio/auto/service/BackgroundHttpService.kt | 7 ++++++- .../ankio/auto/xposed/hooks/common/CommonHooker.kt | 13 ++----------- server/src/main/java/org/ezbook/server/Server.kt | 9 +++++++++ 3 files changed, 17 insertions(+), 12 deletions(-) diff --git a/app/src/main/java/net/ankio/auto/service/BackgroundHttpService.kt b/app/src/main/java/net/ankio/auto/service/BackgroundHttpService.kt index 2e9c650ad..77cdf8d74 100644 --- a/app/src/main/java/net/ankio/auto/service/BackgroundHttpService.kt +++ b/app/src/main/java/net/ankio/auto/service/BackgroundHttpService.kt @@ -21,6 +21,11 @@ class BackgroundHttpService : ICoreService() { super.onCreate(coreService) if (WorkMode.isOcrOrLSPatch()) { + if (Server.isPortOccupied()) { + Logger.d("Server port ${Server.PORT} already occupied, skip duplicate initialization") + return + } + Logger.d("Initializing Xposed hooks for OCR mode or LSPatch mode") AppRuntime.manifest = AutoHooker() AppRuntime.modulePath = coreService.packageManager @@ -61,4 +66,4 @@ class BackgroundHttpService : ICoreService() { Logger.d("onDestroy invoked, cleaning up if necessary") if (::httpService.isInitialized) httpService.stopServer() } -} \ No newline at end of file +} diff --git a/app/src/main/java/net/ankio/auto/xposed/hooks/common/CommonHooker.kt b/app/src/main/java/net/ankio/auto/xposed/hooks/common/CommonHooker.kt index 0662b9567..4ad0457bf 100644 --- a/app/src/main/java/net/ankio/auto/xposed/hooks/common/CommonHooker.kt +++ b/app/src/main/java/net/ankio/auto/xposed/hooks/common/CommonHooker.kt @@ -19,24 +19,15 @@ import net.ankio.auto.BuildConfig import net.ankio.auto.xposed.core.logger.XposedLogger import net.ankio.auto.xposed.core.utils.AppRuntime import org.ezbook.server.Server -import java.io.File -import java.net.ServerSocket object CommonHooker { /** * 检查端口是否被占用。 * 占用则返回 true;未占用则返回 false。 */ - private fun isPortOccupied(port: Int): Boolean { - return try { - ServerSocket(port).use { false } - } catch (_: Throwable) { - true - } - } fun init() { XposedLogger.d("CommonHooker: start server for ${AppRuntime.manifest.packageName}") - if (isPortOccupied(Server.PORT)) { + if (Server.isPortOccupied()) { XposedLogger.d("CommonHooker: port ${Server.PORT} occupied, skip") return } @@ -55,4 +46,4 @@ object CommonHooker { XposedLogger.e("CommonHooker: init failed", e) } } -} \ No newline at end of file +} diff --git a/server/src/main/java/org/ezbook/server/Server.kt b/server/src/main/java/org/ezbook/server/Server.kt index 91f513629..5e87ca7a9 100644 --- a/server/src/main/java/org/ezbook/server/Server.kt +++ b/server/src/main/java/org/ezbook/server/Server.kt @@ -29,6 +29,7 @@ import org.ezbook.server.db.Db import org.ezbook.server.server.module import org.ezbook.server.log.ServerLog import org.ezbook.server.tools.SettingUtils +import java.net.ServerSocket class Server(private val context: Application) { @@ -75,6 +76,14 @@ class Server(private val context: Application) { var debugPackage = false lateinit var application: Application + fun isPortOccupied(): Boolean { + return try { + ServerSocket(PORT).use { false } + } catch (_: Throwable) { + true + } + } + /** * 统一的协程异常处理器:防止单个异常导致整个作用域崩溃 */ From f0e2a26fb0644bcac80639d9a3e70c6cf902afac Mon Sep 17 00:00:00 2001 From: Sylthionys <94494882+Sylthionys@users.noreply.github.com> Date: Mon, 15 Jun 2026 01:46:22 +0800 Subject: [PATCH 06/32] :rotating_light: (build): address explicit lint findings --- app/build.gradle.kts | 8 ++++---- .../selecttospeak/SelectToSpeakService.kt | 4 +++- .../main/java/net/ankio/auto/service/CoreService.kt | 3 ++- .../main/java/net/ankio/auto/service/OcrService.kt | 4 ++-- .../auto/ui/activity/FloatingWindowTriggerActivity.kt | 1 + .../ankio/auto/ui/components/ExpandableCardGroup.kt | 3 ++- .../net/ankio/auto/ui/components/GradientImageView.kt | 11 ++++++----- .../java/net/ankio/auto/ui/components/IconView.kt | 6 +++++- .../ankio/auto/ui/fragment/AnalysisDetailFragment.kt | 1 + .../net/ankio/auto/ui/fragment/AssetEditFragment.kt | 4 ++-- .../auto/ui/fragment/components/BookCardComponent.kt | 6 +++--- .../ui/fragment/intro/IntroPagePermissionFragment.kt | 6 +++--- .../java/net/ankio/auto/ui/models/ToolbarMenuItem.kt | 6 +++--- .../java/net/ankio/auto/ui/utils/PaletteManager.kt | 4 ++-- .../java/net/ankio/auto/utils/ExceptionHandler.kt | 2 +- .../main/java/net/ankio/auto/utils/LanguageUtils.kt | 2 +- .../java/net/ankio/auto/xposed/core/ui/ColorUtils.kt | 10 +++++----- .../auto/xposed/hooks/qianji/filter/AssetsFilter.kt | 4 ++-- .../auto/xposed/hooks/qianji/tools/QianJiBillType.kt | 3 +-- app/src/main/res/layout/card_monthly.xml | 1 + app/src/main/res/layout/component_ai.xml | 3 ++- app/src/main/res/layout/dialog_data_editor.xml | 1 + app/src/main/res/layout/float_tip_top.xml | 1 + app/src/main/res/layout/fragment_category_edit.xml | 1 + app/src/main/res/layout/fragment_plugin_data.xml | 3 ++- app/src/main/res/layout/item_page_signature.xml | 1 + app/src/main/res/menu/analysis_detail_menu.xml | 7 +++---- app/src/main/res/menu/asset_menu.xml | 7 +++---- app/src/main/res/menu/bill_menu.xml | 9 ++++----- app/src/main/res/menu/log_menu.xml | 7 +++---- app/src/main/res/menu/summary_menu.xml | 7 +++---- app/src/main/res/values-zh/strings.xml | 6 +++--- app/src/main/res/values/strings.xml | 8 ++++---- build.gradle.kts | 4 ++-- gradle/libs.versions.toml | 9 +++++++++ hook/build.gradle.kts | 4 ++-- ocr/build.gradle.kts | 3 +++ shell/build.gradle.kts | 9 ++++++--- tap/src/main/java/net/ankio/tap/TapBackDetector.kt | 2 +- .../tap/samsung/SamsungBackTapDetectionService.kt | 2 +- test/src/main/java/net/ankio/test/BaseTest.kt | 4 ++-- 41 files changed, 107 insertions(+), 80 deletions(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 3de4494bf..0fa426bad 100755 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -220,11 +220,11 @@ dependencies { implementation(libs.rikkaMaterialPreference) implementation(libs.about) - implementation("com.github.bumptech.glide:glide:4.16.0") + implementation(libs.glide) - implementation("com.tencent.bugly:crashreport:latest.release") - implementation("com.tencent:mmkv-static:1.3.5") - implementation("net.lingala.zip4j:zip4j:2.11.5") + implementation(libs.bugly) + implementation(libs.mmkv) + implementation(libs.zip4j) implementation(kotlin("reflect")) diff --git a/app/src/main/java/com/google/android/accessibility/selecttospeak/SelectToSpeakService.kt b/app/src/main/java/com/google/android/accessibility/selecttospeak/SelectToSpeakService.kt index ae1b0a279..03b2a3f09 100644 --- a/app/src/main/java/com/google/android/accessibility/selecttospeak/SelectToSpeakService.kt +++ b/app/src/main/java/com/google/android/accessibility/selecttospeak/SelectToSpeakService.kt @@ -13,6 +13,8 @@ * limitations under the License. */ +@file:Suppress("DEPRECATION") + package com.google.android.accessibility.selecttospeak import android.accessibilityservice.AccessibilityService @@ -310,7 +312,7 @@ class SelectToSpeakService : AccessibilityService() { private fun buildNodeDescriptor(node: AccessibilityNodeInfo): String { val cls = node.className?.toString()?.substringAfterLast('.') ?: "?" val id = - node.viewIdResourceName?.toString()?.substringAfterLast('/')?.takeIf { it.isNotBlank() } + node.viewIdResourceName?.substringAfterLast('/')?.takeIf { it.isNotBlank() } val name = node.contentDescription?.toString() ?.replace(Regex("\\d+(\\.\\d+)?"), "#") ?.replace(Regex("\\s+"), " ") diff --git a/app/src/main/java/net/ankio/auto/service/CoreService.kt b/app/src/main/java/net/ankio/auto/service/CoreService.kt index aa38909d4..a53a5babb 100644 --- a/app/src/main/java/net/ankio/auto/service/CoreService.kt +++ b/app/src/main/java/net/ankio/auto/service/CoreService.kt @@ -232,6 +232,7 @@ class CoreService : LifecycleService() { /** * 检查CoreService是否正在运行 */ + @Suppress("DEPRECATION") fun isRunning(context: Context): Boolean { val manager = context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager for (service in manager.getRunningServices(Integer.MAX_VALUE)) { @@ -300,4 +301,4 @@ class CoreService : LifecycleService() { } } } -} \ No newline at end of file +} diff --git a/app/src/main/java/net/ankio/auto/service/OcrService.kt b/app/src/main/java/net/ankio/auto/service/OcrService.kt index cca7c12fa..49fb46368 100644 --- a/app/src/main/java/net/ankio/auto/service/OcrService.kt +++ b/app/src/main/java/net/ankio/auto/service/OcrService.kt @@ -9,6 +9,7 @@ import android.os.VibrationEffect import android.os.Vibrator import android.os.VibratorManager import android.util.Base64 +import androidx.core.graphics.scale import androidx.lifecycle.lifecycleScope import com.google.android.accessibility.selecttospeak.SelectToSpeakService import kotlinx.coroutines.Dispatchers @@ -406,7 +407,7 @@ class OcrService : ICoreService() { val scale = OCR_MAX_SHORT_EDGE.toFloat() / shortSide val newW = (source.width * scale).toInt() val newH = (source.height * scale).toInt() - return Bitmap.createScaledBitmap(source, newW, newH, true) + return source.scale(newW, newH) } /** @@ -488,4 +489,3 @@ class OcrService : ICoreService() { - diff --git a/app/src/main/java/net/ankio/auto/ui/activity/FloatingWindowTriggerActivity.kt b/app/src/main/java/net/ankio/auto/ui/activity/FloatingWindowTriggerActivity.kt index 1e8586bb8..96b98cc12 100644 --- a/app/src/main/java/net/ankio/auto/ui/activity/FloatingWindowTriggerActivity.kt +++ b/app/src/main/java/net/ankio/auto/ui/activity/FloatingWindowTriggerActivity.kt @@ -75,6 +75,7 @@ class FloatingWindowTriggerActivity : BaseActivity() { * 创建快捷方式的返回结果 * 用于兼容老式启动器的 CREATE_SHORTCUT 机制 */ + @Suppress("DEPRECATION") private fun createShortcutResult() { // 创建启动 OCR 功能的 Intent val launchIntent = Intent(this, FloatingWindowTriggerActivity::class.java).apply { diff --git a/app/src/main/java/net/ankio/auto/ui/components/ExpandableCardGroup.kt b/app/src/main/java/net/ankio/auto/ui/components/ExpandableCardGroup.kt index f3e165270..4e8d50510 100644 --- a/app/src/main/java/net/ankio/auto/ui/components/ExpandableCardGroup.kt +++ b/app/src/main/java/net/ankio/auto/ui/components/ExpandableCardGroup.kt @@ -20,6 +20,7 @@ import android.content.Context import android.util.AttributeSet import android.view.View import android.widget.LinearLayout +import androidx.core.view.isVisible import com.google.android.material.card.MaterialCardView /** @@ -91,7 +92,7 @@ open class ExpandableCardGroup @JvmOverloads constructor( val visibleChildren = mutableListOf() for (i in 0 until childCount) { val child = getChildAt(i) - if (child.visibility == View.VISIBLE) { + if (child.isVisible) { visibleChildren.add(child) } } diff --git a/app/src/main/java/net/ankio/auto/ui/components/GradientImageView.kt b/app/src/main/java/net/ankio/auto/ui/components/GradientImageView.kt index f741d3c4a..c5625d66b 100644 --- a/app/src/main/java/net/ankio/auto/ui/components/GradientImageView.kt +++ b/app/src/main/java/net/ankio/auto/ui/components/GradientImageView.kt @@ -31,6 +31,8 @@ import android.graphics.drawable.BitmapDrawable import android.graphics.drawable.Drawable import android.util.AttributeSet import androidx.appcompat.widget.AppCompatImageView +import androidx.core.graphics.createBitmap +import androidx.core.graphics.withClip import net.ankio.auto.R /** @@ -131,10 +133,9 @@ class GradientImageView @JvmOverloads constructor( clipPath.reset() clipPath.addRoundRect(rectF, cornerRadii, Path.Direction.CW) - canvas.save() - canvas.clipPath(clipPath) - canvas.drawRect(rectF, paint) - canvas.restore() + canvas.withClip(clipPath) { + drawRect(rectF, paint) + } } } @@ -200,7 +201,7 @@ class GradientImageView @JvmOverloads constructor( val drawableWidth = if (drawable.intrinsicWidth > 0) drawable.intrinsicWidth else 1 val drawableHeight = if (drawable.intrinsicHeight > 0) drawable.intrinsicHeight else 1 - val bitmap = Bitmap.createBitmap(drawableWidth, drawableHeight, Bitmap.Config.ARGB_8888) + val bitmap = createBitmap(drawableWidth, drawableHeight) val canvas = Canvas(bitmap) drawable.setBounds(0, 0, canvas.width, canvas.height) drawable.draw(canvas) diff --git a/app/src/main/java/net/ankio/auto/ui/components/IconView.kt b/app/src/main/java/net/ankio/auto/ui/components/IconView.kt index f5507bba1..6d0ac3bc8 100644 --- a/app/src/main/java/net/ankio/auto/ui/components/IconView.kt +++ b/app/src/main/java/net/ankio/auto/ui/components/IconView.kt @@ -61,7 +61,11 @@ class IconView : ConstraintLayout { val textSizePx = a.getDimension( R.styleable.IconView_textSize, - 14f * resources.displayMetrics.scaledDensity + TypedValue.applyDimension( + TypedValue.COMPLEX_UNIT_SP, + 14f, + resources.displayMetrics + ) ) binding.iconViewText.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSizePx) diff --git a/app/src/main/java/net/ankio/auto/ui/fragment/AnalysisDetailFragment.kt b/app/src/main/java/net/ankio/auto/ui/fragment/AnalysisDetailFragment.kt index e6eec1504..f97027d89 100644 --- a/app/src/main/java/net/ankio/auto/ui/fragment/AnalysisDetailFragment.kt +++ b/app/src/main/java/net/ankio/auto/ui/fragment/AnalysisDetailFragment.kt @@ -302,6 +302,7 @@ class AnalysisDetailFragment : BaseWebViewFragment() { } // 设置选中的图标 - asset.icon?.let { iconUrl -> + asset.icon.let { iconUrl -> val matchedIcon = allIcons.find { it.icon == iconUrl } matchedIcon?.let { onIconSelected(it) } } @@ -392,4 +392,4 @@ class AssetEditFragment : BaseFragment() { } } } -} \ No newline at end of file +} diff --git a/app/src/main/java/net/ankio/auto/ui/fragment/components/BookCardComponent.kt b/app/src/main/java/net/ankio/auto/ui/fragment/components/BookCardComponent.kt index ea18c41ed..50f577346 100644 --- a/app/src/main/java/net/ankio/auto/ui/fragment/components/BookCardComponent.kt +++ b/app/src/main/java/net/ankio/auto/ui/fragment/components/BookCardComponent.kt @@ -182,8 +182,8 @@ class BookCardComponent(binding: CardBookBinding) : * 数据模型 - 基于 label 动态分配颜色 * ----------------------------------------------------- */ data class ActionTile( - @DrawableRes val icon: Int, - @StringRes val label: Int, + @param:DrawableRes val icon: Int, + @param:StringRes val label: Int, val onClick: () -> Unit ) @@ -265,4 +265,4 @@ class BookCardComponent(binding: CardBookBinding) : grid.addView(view, lp) } } -} \ No newline at end of file +} diff --git a/app/src/main/java/net/ankio/auto/ui/fragment/intro/IntroPagePermissionFragment.kt b/app/src/main/java/net/ankio/auto/ui/fragment/intro/IntroPagePermissionFragment.kt index b972a4c26..f770c37e4 100644 --- a/app/src/main/java/net/ankio/auto/ui/fragment/intro/IntroPagePermissionFragment.kt +++ b/app/src/main/java/net/ankio/auto/ui/fragment/intro/IntroPagePermissionFragment.kt @@ -68,9 +68,9 @@ class IntroPagePermissionFragment : BaseIntroPageFragment Boolean, // 检查权限的方法 val onClick: () -> Unit, // 点击跳转的方法 val isRequired: Boolean = true, // 是否为必需权限,默认为必需 diff --git a/app/src/main/java/net/ankio/auto/ui/models/ToolbarMenuItem.kt b/app/src/main/java/net/ankio/auto/ui/models/ToolbarMenuItem.kt index 0026028de..ab4d208eb 100644 --- a/app/src/main/java/net/ankio/auto/ui/models/ToolbarMenuItem.kt +++ b/app/src/main/java/net/ankio/auto/ui/models/ToolbarMenuItem.kt @@ -19,9 +19,9 @@ import androidx.annotation.DrawableRes import androidx.annotation.StringRes data class ToolbarMenuItem( - @StringRes val title: Int, - @DrawableRes val drawable: Int, + @param:StringRes val title: Int, + @param:DrawableRes val drawable: Int, var search: Boolean = false, val callback: () -> Unit, - ) \ No newline at end of file + ) diff --git a/app/src/main/java/net/ankio/auto/ui/utils/PaletteManager.kt b/app/src/main/java/net/ankio/auto/ui/utils/PaletteManager.kt index c7e36364e..688eb3fe3 100644 --- a/app/src/main/java/net/ankio/auto/ui/utils/PaletteManager.kt +++ b/app/src/main/java/net/ankio/auto/ui/utils/PaletteManager.kt @@ -38,8 +38,8 @@ object PaletteManager { * 双色结果(同一色系下的 强调/背景) */ data class Duo( - @ColorInt val emphasis: Int, - @ColorInt val background: Int + @param:ColorInt val emphasis: Int, + @param:ColorInt val background: Int ) // 简单缓存:key=index(1..50),value=颜色 int diff --git a/app/src/main/java/net/ankio/auto/utils/ExceptionHandler.kt b/app/src/main/java/net/ankio/auto/utils/ExceptionHandler.kt index 273097202..4593b31e5 100644 --- a/app/src/main/java/net/ankio/auto/utils/ExceptionHandler.kt +++ b/app/src/main/java/net/ankio/auto/utils/ExceptionHandler.kt @@ -85,7 +85,7 @@ class ExceptionHandler private constructor(private val context: Context) : saveLogToLocal(context, msg) // Bugly 上报 if (!PrefManager.debugMode) - CrashReport.postCatchedException(e) + CrashReport.postCatchedException(e) // 跳转错误界面 try { diff --git a/app/src/main/java/net/ankio/auto/utils/LanguageUtils.kt b/app/src/main/java/net/ankio/auto/utils/LanguageUtils.kt index fca92f83c..b5a842c79 100644 --- a/app/src/main/java/net/ankio/auto/utils/LanguageUtils.kt +++ b/app/src/main/java/net/ankio/auto/utils/LanguageUtils.kt @@ -91,7 +91,7 @@ object LanguageUtils { private fun getLocale(language: String): Locale { - return if (language == "SYSTEM") getSystemLocale() else Locale(language) + return if (language == "SYSTEM") getSystemLocale() else Locale.forLanguageTag(language) } private fun updateResourcesLocale( diff --git a/app/src/main/java/net/ankio/auto/xposed/core/ui/ColorUtils.kt b/app/src/main/java/net/ankio/auto/xposed/core/ui/ColorUtils.kt index e1242851b..ccc3b5e9b 100644 --- a/app/src/main/java/net/ankio/auto/xposed/core/ui/ColorUtils.kt +++ b/app/src/main/java/net/ankio/auto/xposed/core/ui/ColorUtils.kt @@ -17,7 +17,7 @@ package net.ankio.auto.xposed.core.ui import android.content.Context import android.content.res.Configuration -import android.graphics.Color +import androidx.core.graphics.toColorInt open class ColorUtils { open fun isDarkMode(context: Context): Boolean { @@ -34,15 +34,15 @@ open class ColorUtils { open val backgroundColorDark = "#2e2e2e" open fun getMainColor(context: Context): Int { - return Color.parseColor(if (isDarkMode(context)) mainColorDark else mainColorLight) + return (if (isDarkMode(context)) mainColorDark else mainColorLight).toColorInt() } open fun getSubColor(context: Context): Int { - return Color.parseColor(if (isDarkMode(context)) subColorDark else subColorLight) + return (if (isDarkMode(context)) subColorDark else subColorLight).toColorInt() } open fun getBackgroundColor(context: Context): Int { - return Color.parseColor(if (isDarkMode(context)) backgroundColorDark else backgroundColorLight) + return (if (isDarkMode(context)) backgroundColorDark else backgroundColorLight).toColorInt() } -} \ No newline at end of file +} diff --git a/app/src/main/java/net/ankio/auto/xposed/hooks/qianji/filter/AssetsFilter.kt b/app/src/main/java/net/ankio/auto/xposed/hooks/qianji/filter/AssetsFilter.kt index 2a92db4c8..ef55715dc 100644 --- a/app/src/main/java/net/ankio/auto/xposed/hooks/qianji/filter/AssetsFilter.kt +++ b/app/src/main/java/net/ankio/auto/xposed/hooks/qianji/filter/AssetsFilter.kt @@ -75,7 +75,7 @@ class AssetsFilter private constructor(private var filterObj: Any?) { (XposedHelpers.callMethod( filterObj, "getFirst" - ) as Any?)?.let { QjAssetAccountModel.fromObject(it) } + ))?.let { QjAssetAccountModel.fromObject(it) } /** 键名固定为 "assets" */ fun getKey(): String = XposedHelpers.callMethod(filterObj, "getKey") as String @@ -96,4 +96,4 @@ class AssetsFilter private constructor(private var filterObj: Any?) { else -> null } }.toCollection(LinkedHashSet()) -} \ No newline at end of file +} diff --git a/app/src/main/java/net/ankio/auto/xposed/hooks/qianji/tools/QianJiBillType.kt b/app/src/main/java/net/ankio/auto/xposed/hooks/qianji/tools/QianJiBillType.kt index bafb0167c..46ef139f1 100644 --- a/app/src/main/java/net/ankio/auto/xposed/hooks/qianji/tools/QianJiBillType.kt +++ b/app/src/main/java/net/ankio/auto/xposed/hooks/qianji/tools/QianJiBillType.kt @@ -69,7 +69,6 @@ enum class QianJiBillType(val value: Int) { BillType.IncomeReimbursement -> IncomeReimbursement.value BillType.IncomeRepayment -> IncomeRepayment.value BillType.IncomeRefund -> IncomeRefund.value - else -> Expend.value } } @@ -92,4 +91,4 @@ enum class QianJiBillType(val value: Int) { } -} \ No newline at end of file +} diff --git a/app/src/main/res/layout/card_monthly.xml b/app/src/main/res/layout/card_monthly.xml index eabe13998..463c5b36e 100644 --- a/app/src/main/res/layout/card_monthly.xml +++ b/app/src/main/res/layout/card_monthly.xml @@ -14,6 +14,7 @@ android:id="@+id/incomeExpenseLayout" android:layout_width="0dp" android:layout_height="wrap_content" + android:baselineAligned="false" android:orientation="horizontal" app:layout_constraintEnd_toEndOf="parent" diff --git a/app/src/main/res/layout/component_ai.xml b/app/src/main/res/layout/component_ai.xml index 9727bca61..a907df704 100644 --- a/app/src/main/res/layout/component_ai.xml +++ b/app/src/main/res/layout/component_ai.xml @@ -75,6 +75,7 @@ android:layout_marginTop="16dp" android:layout_width="match_parent" android:layout_height="wrap_content" + android:baselineAligned="false" android:orientation="horizontal" android:gravity="center_vertical"> @@ -196,4 +197,4 @@ - \ No newline at end of file + diff --git a/app/src/main/res/layout/dialog_data_editor.xml b/app/src/main/res/layout/dialog_data_editor.xml index bcabd7642..73df38031 100644 --- a/app/src/main/res/layout/dialog_data_editor.xml +++ b/app/src/main/res/layout/dialog_data_editor.xml @@ -23,6 +23,7 @@ diff --git a/app/src/main/res/layout/float_tip_top.xml b/app/src/main/res/layout/float_tip_top.xml index d52648344..0730f05f0 100644 --- a/app/src/main/res/layout/float_tip_top.xml +++ b/app/src/main/res/layout/float_tip_top.xml @@ -29,6 +29,7 @@ diff --git a/app/src/main/res/layout/fragment_plugin_data.xml b/app/src/main/res/layout/fragment_plugin_data.xml index 41e18d506..70d43743c 100644 --- a/app/src/main/res/layout/fragment_plugin_data.xml +++ b/app/src/main/res/layout/fragment_plugin_data.xml @@ -63,6 +63,7 @@ android:id="@+id/filterContainer" android:layout_width="match_parent" android:layout_height="wrap_content" + android:baselineAligned="false" android:gravity="center_vertical" android:orientation="horizontal"> @@ -159,4 +160,4 @@ android:layout_height="match_parent" /> - \ No newline at end of file + diff --git a/app/src/main/res/layout/item_page_signature.xml b/app/src/main/res/layout/item_page_signature.xml index 6d116bc19..129dbc335 100644 --- a/app/src/main/res/layout/item_page_signature.xml +++ b/app/src/main/res/layout/item_page_signature.xml @@ -20,6 +20,7 @@ diff --git a/app/src/main/res/menu/analysis_detail_menu.xml b/app/src/main/res/menu/analysis_detail_menu.xml index e6b76ee1e..c8198b1b1 100644 --- a/app/src/main/res/menu/analysis_detail_menu.xml +++ b/app/src/main/res/menu/analysis_detail_menu.xml @@ -1,9 +1,8 @@ - + - \ No newline at end of file + android:showAsAction="always" /> + diff --git a/app/src/main/res/menu/asset_menu.xml b/app/src/main/res/menu/asset_menu.xml index fbc580a0d..c57e23508 100644 --- a/app/src/main/res/menu/asset_menu.xml +++ b/app/src/main/res/menu/asset_menu.xml @@ -12,13 +12,12 @@ ~ See the License for the specific language governing permissions and ~ limitations under the License. --> - + + android:showAsAction="ifRoom" /> - \ No newline at end of file + diff --git a/app/src/main/res/menu/bill_menu.xml b/app/src/main/res/menu/bill_menu.xml index ba196d5ba..f160f9595 100644 --- a/app/src/main/res/menu/bill_menu.xml +++ b/app/src/main/res/menu/bill_menu.xml @@ -13,17 +13,16 @@ ~ limitations under the License. --> - + + android:showAsAction="ifRoom" /> - \ No newline at end of file + android:showAsAction="ifRoom" /> + diff --git a/app/src/main/res/menu/log_menu.xml b/app/src/main/res/menu/log_menu.xml index ce7729ea0..3a24e78df 100644 --- a/app/src/main/res/menu/log_menu.xml +++ b/app/src/main/res/menu/log_menu.xml @@ -1,10 +1,9 @@ - + + android:showAsAction="ifRoom" /> - \ No newline at end of file + diff --git a/app/src/main/res/menu/summary_menu.xml b/app/src/main/res/menu/summary_menu.xml index 4127d2c1e..08371931e 100644 --- a/app/src/main/res/menu/summary_menu.xml +++ b/app/src/main/res/menu/summary_menu.xml @@ -13,16 +13,15 @@ ~ limitations under the License. --> - + + android:showAsAction="always" /> diff --git a/app/src/main/res/values-zh/strings.xml b/app/src/main/res/values-zh/strings.xml index 098593436..97bc25bb7 100644 --- a/app/src/main/res/values-zh/strings.xml +++ b/app/src/main/res/values-zh/strings.xml @@ -222,15 +222,15 @@ WebDAV认证失败(HTTP %d),请检查用户名和密码 WebDAV路径不存在(HTTP %d),请检查配置的路径 WebDAV服务器错误(HTTP %d),请稍后重试 - WebDAV请求失败(HTTP %d):%s + WebDAV请求失败(HTTP %1$d):%2$s 无法连接到WebDAV服务器,请检查网络连接和服务器地址 无法连接到WebDAV服务器,请检查网络连接 WebDAV认证失败,请检查用户名和密码 WebDAV备份失败:%s WebDAV恢复失败:%s - 获取备份列表失败(HTTP %d):%s + 获取备份列表失败(HTTP %1$d):%2$s 获取备份列表失败:%s - 备份文件不存在(HTTP %d):%s + 备份文件不存在(HTTP %1$d):%2$s 下载备份文件失败:%s diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 3c9752c97..34b29df00 100755 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -222,15 +222,15 @@ WebDAV authentication failed (HTTP %d), please check username and password WebDAV path does not exist (HTTP %d), please check configured path WebDAV server error (HTTP %d), please try again later - WebDAV request failed (HTTP %d): %s + WebDAV request failed (HTTP %1$d): %2$s Unable to connect to WebDAV server, please check network connection and server address Unable to connect to WebDAV server, please check network connection WebDAV authentication failed, please check username and password WebDAV backup failed: %s WebDAV restore failed: %s - Failed to get backup list (HTTP %d): %s + Failed to get backup list (HTTP %1$d): %2$s Failed to get backup list: %s - Backup file does not exist (HTTP %d): %s + Backup file does not exist (HTTP %1$d): %2$s Failed to download backup file: %s Preparing backup... @@ -1165,4 +1165,4 @@ Are you sure you want to delete all financial analysis tasks? This operation cannot be undone. All analysis tasks cleared Clear failed, please try again - \ No newline at end of file + diff --git a/build.gradle.kts b/build.gradle.kts index da9ff1c3f..ebcd606bb 100755 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -12,5 +12,5 @@ plugins { } tasks.register("clean") { - delete(rootProject.buildDir) -} \ No newline at end of file + delete(rootProject.layout.buildDirectory) +} diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 2b6794f81..5894326dc 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -46,6 +46,9 @@ htmlKtx = "1.1.2" # Image Loading glide = "4.16.0" +bugly = "latest.release" +mmkv = "1.3.5" +zip4j = "2.11.5" # Development and Debug Tools leakcanaryAndroid = "2.14" @@ -59,6 +62,7 @@ kotlinxCoroutinesAndroid = "1.8.0" # Android System and Xposed xposed = "82" dexlib2 = "2.5.2" +shizuku = "13.1.5" # JavaScript Engine quickjsAndroid = "35726a9d28" @@ -112,6 +116,9 @@ html-ktx = { module = "dev.rikka.rikkax.html:html-ktx", version.ref = "htmlKtx" # Image Loading glide = { module = "com.github.bumptech.glide:glide", version.ref = "glide" } glideCompiler = { module = "com.github.bumptech.glide:compiler", version.ref = "glide" } +bugly = { module = "com.tencent.bugly:crashreport", version.ref = "bugly" } +mmkv = { module = "com.tencent:mmkv-static", version.ref = "mmkv" } +zip4j = { module = "net.lingala.zip4j:zip4j", version.ref = "zip4j" } # Development and Debug Tools leakcanary-android = { module = "com.squareup.leakcanary:leakcanary-android", version.ref = "leakcanaryAndroid" } @@ -122,6 +129,8 @@ kotlinx-coroutines-android = { module = "org.jetbrains.kotlinx:kotlinx-coroutine # Android System and Xposed xposed = { module = "de.robv.android.xposed:api", version.ref = "xposed" } dexlib2 = { module = "org.smali:dexlib2", version.ref = "dexlib2" } +shizuku-api = { module = "dev.rikka.shizuku:api", version.ref = "shizuku" } +shizuku-provider = { module = "dev.rikka.shizuku:provider", version.ref = "shizuku" } # JavaScript Engine quickjs-android = { module = "com.github.AutoAccountingOrg:quickjs-android", version.ref = "quickjsAndroid" } diff --git a/hook/build.gradle.kts b/hook/build.gradle.kts index 74e3c8694..6e05b6cee 100644 --- a/hook/build.gradle.kts +++ b/hook/build.gradle.kts @@ -19,7 +19,7 @@ android { } kotlin { compilerOptions { - jvmTarget.set(org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_21) + jvmTarget.set(org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_11) } } @@ -32,4 +32,4 @@ dependencies { testImplementation(libs.junit) androidTestImplementation(libs.androidx.espresso.core) androidTestImplementation(libs.androidx.junit) -} \ No newline at end of file +} diff --git a/ocr/build.gradle.kts b/ocr/build.gradle.kts index f4caa8014..282fb192f 100644 --- a/ocr/build.gradle.kts +++ b/ocr/build.gradle.kts @@ -43,4 +43,7 @@ android { dependencies { implementation(libs.paddleocr4android) implementation(libs.core.ktx) + testImplementation(libs.junit) + androidTestImplementation(libs.androidx.espresso.core) + androidTestImplementation(libs.androidx.junit) } diff --git a/shell/build.gradle.kts b/shell/build.gradle.kts index 3d99ed8aa..3f6949af0 100644 --- a/shell/build.gradle.kts +++ b/shell/build.gradle.kts @@ -42,6 +42,9 @@ dependencies { implementation(libs.core.ktx) // Shizuku API - 用于以 shell/system 身份访问系统服务 - implementation("dev.rikka.shizuku:api:13.1.5") - implementation("dev.rikka.shizuku:provider:13.1.5") -} \ No newline at end of file + implementation(libs.shizuku.api) + implementation(libs.shizuku.provider) + testImplementation(libs.junit) + androidTestImplementation(libs.androidx.espresso.core) + androidTestImplementation(libs.androidx.junit) +} diff --git a/tap/src/main/java/net/ankio/tap/TapBackDetector.kt b/tap/src/main/java/net/ankio/tap/TapBackDetector.kt index 96a5ca9e8..951a80898 100644 --- a/tap/src/main/java/net/ankio/tap/TapBackDetector.kt +++ b/tap/src/main/java/net/ankio/tap/TapBackDetector.kt @@ -152,7 +152,7 @@ class TapBackDetector( TapLogger.i( SUB, "started builtin=${model.id} path=${model.assetPath} samsung=${model.isSamsungRegi()} " + - "sensitivity=$sensitivity sensorThread=${looper.thread?.name} callback=${callbackLooper.thread?.name}", + "sensitivity=$sensitivity sensorThread=${looper.thread.name} callback=${callbackLooper.thread.name}", ) } diff --git a/tap/src/main/java/net/ankio/tap/samsung/SamsungBackTapDetectionService.kt b/tap/src/main/java/net/ankio/tap/samsung/SamsungBackTapDetectionService.kt index e6ec71f95..fa49c327f 100644 --- a/tap/src/main/java/net/ankio/tap/samsung/SamsungBackTapDetectionService.kt +++ b/tap/src/main/java/net/ankio/tap/samsung/SamsungBackTapDetectionService.kt @@ -66,7 +66,7 @@ class SamsungBackTapDetectionService( lastT, sSamplingIntervalInNano, ) - val v = Math.toDegrees(acos((lastZ / f).toDouble()).toDouble()).toInt() + val v = Math.toDegrees(acos((lastZ / f).toDouble())).toInt() mIsFlat[mFlatIndex] = if (v < 10 || v > 170) 1 else 0 val v1 = mFlatIndex + 1 mFlatIndex = v1 diff --git a/test/src/main/java/net/ankio/test/BaseTest.kt b/test/src/main/java/net/ankio/test/BaseTest.kt index 1e0b78d23..e876cc7b0 100644 --- a/test/src/main/java/net/ankio/test/BaseTest.kt +++ b/test/src/main/java/net/ankio/test/BaseTest.kt @@ -20,7 +20,7 @@ import com.google.gson.JsonObject import kotlinx.coroutines.* import java.io.File import java.net.HttpURLConnection -import java.net.URL +import java.net.URI import kotlin.system.measureTimeMillis /** @@ -93,7 +93,7 @@ abstract class BaseTest { "&type=${item.type}" + "&fromAppData=${item.fromAppData}" - val url = URL(urlString) + val url = URI(urlString).toURL() val connection = url.openConnection() as HttpURLConnection connection.requestMethod = "POST" From b3dc7c3663753364d40281f333f95c3017b3f9a0 Mon Sep 17 00:00:00 2001 From: Sylthionys <94494882+Sylthionys@users.noreply.github.com> Date: Tue, 16 Jun 2026 00:54:56 +0800 Subject: [PATCH 07/32] :rotating_light: (app): fix lifecycle and compatibility lint warnings --- app/build.gradle.kts | 6 ++++++ .../net/ankio/auto/service/overlay/RepeatToast.kt | 6 ++++-- .../ankio/auto/storage/backup/BackupManager.kt | 10 +++++++--- .../ankio/auto/storage/backup/RestoreManager.kt | 6 ++++-- .../auto/ui/components/BreathingGradientView.kt | 8 +++++--- .../ankio/auto/ui/components/SettingItemView.kt | 15 ++++++--------- .../java/net/ankio/auto/ui/utils/ImageUtils.kt | 6 +++--- .../auto/xposed/hooks/qianji/tools/QianJiUri.kt | 4 ++-- app/src/main/res/menu/summary_menu.xml | 6 +++--- app/src/main/res/xml/network_security_config.xml | 9 ++++++--- 10 files changed, 46 insertions(+), 30 deletions(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 0fa426bad..d8990bab0 100755 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -139,6 +139,12 @@ android { ) } + bundle { + language { + enableSplit = false + } + } + lint { checkReleaseBuilds = false abortOnError = false diff --git a/app/src/main/java/net/ankio/auto/service/overlay/RepeatToast.kt b/app/src/main/java/net/ankio/auto/service/overlay/RepeatToast.kt index f95c5323d..344918128 100644 --- a/app/src/main/java/net/ankio/auto/service/overlay/RepeatToast.kt +++ b/app/src/main/java/net/ankio/auto/service/overlay/RepeatToast.kt @@ -23,6 +23,7 @@ import android.view.Gravity import android.view.LayoutInflater import android.view.View import android.view.WindowManager +import android.widget.FrameLayout import android.widget.TextView import net.ankio.auto.R import net.ankio.auto.storage.Logger @@ -79,7 +80,8 @@ class RepeatToast( // 视图使用主题化的 Context 进行膨胀,保证主题属性可用 val themedCtx = context.toThemeCtx() - val view = LayoutInflater.from(themedCtx).inflate(R.layout.repeat_toast, null) + val inflationParent = FrameLayout(themedCtx) + val view = LayoutInflater.from(themedCtx).inflate(R.layout.repeat_toast, inflationParent, false) rootView = view val msgView = view.findViewById(R.id.message) @@ -155,4 +157,4 @@ class RepeatToast( }.onFailure { Logger.w("RepeatToast removeView failed: ${it.message}") } } } -} \ No newline at end of file +} diff --git a/app/src/main/java/net/ankio/auto/storage/backup/BackupManager.kt b/app/src/main/java/net/ankio/auto/storage/backup/BackupManager.kt index 2c056b435..33c9bf411 100644 --- a/app/src/main/java/net/ankio/auto/storage/backup/BackupManager.kt +++ b/app/src/main/java/net/ankio/auto/storage/backup/BackupManager.kt @@ -299,7 +299,7 @@ class BackupManager(private val context: Context) { DocumentsContract.getTreeDocumentId(uri) ) - context.contentResolver.query( + val cursor = context.contentResolver.query( childrenUri, arrayOf( DocumentsContract.Document.COLUMN_DISPLAY_NAME, @@ -308,7 +308,9 @@ class BackupManager(private val context: Context) { null, null, null - )?.use { cursor -> + ) + try { + if (cursor == null) return@withIO val nameIndex = cursor.getColumnIndexOrThrow(DocumentsContract.Document.COLUMN_DISPLAY_NAME) val idIndex = @@ -322,6 +324,8 @@ class BackupManager(private val context: Context) { backupFiles.add(Pair(name, fileUri)) } } + } finally { + cursor?.close() } // 按文件名排序(文件名包含时间戳,降序排列) @@ -416,4 +420,4 @@ class BackupManager(private val context: Context) { } -} \ No newline at end of file +} diff --git a/app/src/main/java/net/ankio/auto/storage/backup/RestoreManager.kt b/app/src/main/java/net/ankio/auto/storage/backup/RestoreManager.kt index c7baec7a5..9467d8e0f 100644 --- a/app/src/main/java/net/ankio/auto/storage/backup/RestoreManager.kt +++ b/app/src/main/java/net/ankio/auto/storage/backup/RestoreManager.kt @@ -62,12 +62,14 @@ class RestoreManager(private val context: Context) { throwable = null ) - inputStream.use { stream -> + try { val file = File(context.cacheDir, filename) - file.writeBytes(stream.readBytes()) + file.writeBytes(inputStream.readBytes()) // 解包并恢复数据 fileManager.unpackData(file) + } finally { + inputStream.close() } Logger.i("本地恢复完成") diff --git a/app/src/main/java/net/ankio/auto/ui/components/BreathingGradientView.kt b/app/src/main/java/net/ankio/auto/ui/components/BreathingGradientView.kt index d1700058e..a3f3084d3 100644 --- a/app/src/main/java/net/ankio/auto/ui/components/BreathingGradientView.kt +++ b/app/src/main/java/net/ankio/auto/ui/components/BreathingGradientView.kt @@ -52,7 +52,6 @@ class BreathingGradientView @JvmOverloads constructor( init { setWillNotDraw(false) - setupAnimator() } private fun setupAnimator() { @@ -65,13 +64,16 @@ class BreathingGradientView @JvmOverloads constructor( progress = it.animatedValue as Float invalidate() } + start() } } // Start animation when attached to window override fun onAttachedToWindow() { super.onAttachedToWindow() - animator?.start() + if (animator == null) { + setupAnimator() + } } // Stop animation when detached from window @@ -110,4 +112,4 @@ class BreathingGradientView @JvmOverloads constructor( shader.setLocalMatrix(matrix) canvas.drawRect(0f, 0f, width.toFloat(), height.toFloat(), paint) } -} \ No newline at end of file +} diff --git a/app/src/main/java/net/ankio/auto/ui/components/SettingItemView.kt b/app/src/main/java/net/ankio/auto/ui/components/SettingItemView.kt index 5a4ba1b1b..8a0395f4f 100644 --- a/app/src/main/java/net/ankio/auto/ui/components/SettingItemView.kt +++ b/app/src/main/java/net/ankio/auto/ui/components/SettingItemView.kt @@ -5,6 +5,7 @@ import android.util.AttributeSet import android.view.LayoutInflater import android.widget.FrameLayout import androidx.annotation.DrawableRes +import androidx.core.content.res.use import net.ankio.auto.R import net.ankio.auto.databinding.ViewSettingItemBinding import net.ankio.auto.ui.theme.DynamicColors @@ -22,24 +23,20 @@ class SettingItemView @JvmOverloads constructor( // 获取自定义属性 - context.obtainStyledAttributes(attrs, R.styleable.SettingItemView).apply { - try { + context.obtainStyledAttributes(attrs, R.styleable.SettingItemView).use { // 设置图标 - val iconRes = getResourceId(R.styleable.SettingItemView_settingIcon, 0) + val iconRes = it.getResourceId(R.styleable.SettingItemView_settingIcon, 0) if (iconRes != 0) { binding.settingIcon.setImageResource(iconRes) } // 设置标题 - binding.settingTitle.text = getString(R.styleable.SettingItemView_settingTitle) + binding.settingTitle.text = it.getString(R.styleable.SettingItemView_settingTitle) // 设置描述 - binding.settingDesc.text = getString(R.styleable.SettingItemView_settingDesc) + binding.settingDesc.text = it.getString(R.styleable.SettingItemView_settingDesc) binding.root.setCardBackgroundColor(DynamicColors.SurfaceColor1) - } finally { - recycle() - } } } @@ -63,4 +60,4 @@ class SettingItemView @JvmOverloads constructor( binding.settingIcon.setImageResource(iconRes) } -} \ No newline at end of file +} diff --git a/app/src/main/java/net/ankio/auto/ui/utils/ImageUtils.kt b/app/src/main/java/net/ankio/auto/ui/utils/ImageUtils.kt index 9e5726040..63c9912f0 100644 --- a/app/src/main/java/net/ankio/auto/ui/utils/ImageUtils.kt +++ b/app/src/main/java/net/ankio/auto/ui/utils/ImageUtils.kt @@ -87,7 +87,7 @@ fun ImageView.load( defaultResId: Int? = null, ) { - val glide = Glide.with(this) + var glide = Glide.with(this) .load( when { src.isNullOrBlank() -> defaultResId // 空串直接用占位图 @@ -96,7 +96,7 @@ fun ImageView.load( ) .error(defaultResId) if (defaultResId != null) { - glide.fallback(defaultResId) + glide = glide.fallback(defaultResId) } glide.into(this) } @@ -155,4 +155,4 @@ suspend fun ImageView.setAssetIconByName(name: String) { val asset = AssetsAPI.getByName(name) val icon = asset?.icon ?: "" this.load(icon, R.drawable.default_asset) -} \ No newline at end of file +} diff --git a/app/src/main/java/net/ankio/auto/xposed/hooks/qianji/tools/QianJiUri.kt b/app/src/main/java/net/ankio/auto/xposed/hooks/qianji/tools/QianJiUri.kt index cebdb2264..71faa122c 100644 --- a/app/src/main/java/net/ankio/auto/xposed/hooks/qianji/tools/QianJiUri.kt +++ b/app/src/main/java/net/ankio/auto/xposed/hooks/qianji/tools/QianJiUri.kt @@ -74,7 +74,7 @@ object QianJiUri { private fun formatTime(time: Long): String { // 时间格式为yyyy-MM-dd HH:mm:ss val date = Date(time) - val sdf = SimpleDateFormat("yyyy-MM-dd HH:mm:ss") + val sdf = SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault()) return sdf.format(date) -} \ No newline at end of file +} diff --git a/app/src/main/res/menu/summary_menu.xml b/app/src/main/res/menu/summary_menu.xml index 08371931e..688091435 100644 --- a/app/src/main/res/menu/summary_menu.xml +++ b/app/src/main/res/menu/summary_menu.xml @@ -17,11 +17,11 @@ + android:showAsAction="ifRoom" + android:title="@string/setting_privacy" /> diff --git a/app/src/main/res/xml/network_security_config.xml b/app/src/main/res/xml/network_security_config.xml index dd0ff0d6b..a01e232ce 100755 --- a/app/src/main/res/xml/network_security_config.xml +++ b/app/src/main/res/xml/network_security_config.xml @@ -13,6 +13,9 @@ ~ limitations under the License. --> - - - \ No newline at end of file + + + + From 81fda6caf0f09d5284e8bc85477f03865d21e16c Mon Sep 17 00:00:00 2001 From: Sylthionys <94494882+Sylthionys@users.noreply.github.com> Date: Tue, 16 Jun 2026 00:56:34 +0800 Subject: [PATCH 08/32] :bug: (ocr): separate notification launch and OCR action --- .../net/ankio/auto/service/CoreService.kt | 13 +++++++++--- .../net/ankio/auto/service/ocr/OcrTools.kt | 20 +++++++++++++++---- 2 files changed, 26 insertions(+), 7 deletions(-) diff --git a/app/src/main/java/net/ankio/auto/service/CoreService.kt b/app/src/main/java/net/ankio/auto/service/CoreService.kt index a53a5babb..76b2b72a2 100644 --- a/app/src/main/java/net/ankio/auto/service/CoreService.kt +++ b/app/src/main/java/net/ankio/auto/service/CoreService.kt @@ -15,6 +15,7 @@ import net.ankio.auto.constant.WorkMode import org.ezbook.server.intent.IntentType import net.ankio.auto.service.api.ICoreService import net.ankio.auto.storage.Logger +import net.ankio.auto.ui.activity.MainActivity import net.ankio.auto.utils.PrefManager /** @@ -144,18 +145,24 @@ class CoreService : LifecycleService() { * 创建一个低优先级、静默的通知,点击触发手动 OCR(通知不清除) */ private fun buildNotification(): Notification { + val appIntent = Intent(this, MainActivity::class.java) + val appPendingIntent = PendingIntent.getActivity( + this, 0, appIntent, + PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE + ) val ocrIntent = Intent(this, CoreService::class.java).apply { putExtra("intentType", IntentType.OCR.name) putExtra("manual", true) } - val pendingIntent = PendingIntent.getService( - this, 0, ocrIntent, + val ocrPendingIntent = PendingIntent.getService( + this, 1, ocrIntent, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE ) return NotificationCompat.Builder(this, channelId) .setSmallIcon(R.drawable.icon_auto) .setContentTitle(getString(R.string.service_notification_title)) - .setContentIntent(pendingIntent) + .setContentIntent(appPendingIntent) + .addAction(R.drawable.ic_ocr, getString(R.string.ocr_tile_title), ocrPendingIntent) .setOngoing(true) .setShowWhen(false) .setSilent(true) diff --git a/app/src/main/java/net/ankio/auto/service/ocr/OcrTools.kt b/app/src/main/java/net/ankio/auto/service/ocr/OcrTools.kt index 4fef3f988..97968dd6f 100644 --- a/app/src/main/java/net/ankio/auto/service/ocr/OcrTools.kt +++ b/app/src/main/java/net/ankio/auto/service/ocr/OcrTools.kt @@ -125,9 +125,21 @@ object OcrTools { } suspend fun collapseStatusBar() { - SelectToSpeakService.instance?.performGlobalAction( - AccessibilityService.GLOBAL_ACTION_DISMISS_NOTIFICATION_SHADE - ) + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + SelectToSpeakService.instance?.performGlobalAction( + AccessibilityService.GLOBAL_ACTION_DISMISS_NOTIFICATION_SHADE + ) + } else { + withContext(Dispatchers.IO) { + Shell(BuildConfig.APPLICATION_ID).use { shell -> + runCatching { + if (shell.checkPermission()) { + shell.exec("cmd statusbar collapse") + } + } + } + } + } delay(500) } -} \ No newline at end of file +} From 8d64a7ee4a88a5b6279c7c668363ba1db7ae4392 Mon Sep 17 00:00:00 2001 From: Sylthionys <94494882+Sylthionys@users.noreply.github.com> Date: Tue, 16 Jun 2026 00:58:43 +0800 Subject: [PATCH 09/32] :rotating_light: (build): enforce release lint correctness gates --- app/build.gradle.kts | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index d8990bab0..74a0406d8 100755 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -146,8 +146,18 @@ android { } lint { - checkReleaseBuilds = false - abortOnError = false + checkReleaseBuilds = true + abortOnError = true + warningsAsErrors = false + fatal += setOf( + "CheckResult", + "InlinedApi", + "NewApi", + "Recycle", + "MissingPermission", + "WrongConstant", + "LaunchActivityFromNotification", + ) } } From def1ee9d66327e4449a3ba5653cff195eeb62282 Mon Sep 17 00:00:00 2001 From: Sylthionys <94494882+Sylthionys@users.noreply.github.com> Date: Tue, 16 Jun 2026 01:05:20 +0800 Subject: [PATCH 10/32] :recycle: (ui): localize layout text and typography --- .../main/res/layout/adapter_category_list.xml | 6 +- .../res/layout/adapter_category_stats.xml | 3 +- app/src/main/res/layout/adapter_data_rule.xml | 5 +- app/src/main/res/layout/adapter_log.xml | 4 +- app/src/main/res/layout/adapter_map.xml | 5 +- app/src/main/res/layout/adapter_order.xml | 7 ++- .../res/layout/component_payment_info.xml | 9 ++- app/src/main/res/layout/dialog_loading.xml | 4 +- .../main/res/layout/dialog_regex_money.xml | 4 +- app/src/main/res/layout/fragment_tag_edit.xml | 1 + app/src/main/res/menu/analysis_menu.xml | 4 +- app/src/main/res/values-zh/strings.xml | 59 +++++++++--------- app/src/main/res/values/strings.xml | 61 ++++++++++--------- 13 files changed, 91 insertions(+), 81 deletions(-) diff --git a/app/src/main/res/layout/adapter_category_list.xml b/app/src/main/res/layout/adapter_category_list.xml index c81d8977e..fa9466052 100755 --- a/app/src/main/res/layout/adapter_category_list.xml +++ b/app/src/main/res/layout/adapter_category_list.xml @@ -39,9 +39,9 @@ android:ellipsize="end" android:gravity="center" android:singleLine="true" - android:text="哈哈哈" android:textColor="?attr/colorPrimary" - android:textSize="@dimen/font_size_normal" /> + android:textSize="@dimen/font_size_normal" + tools:text="示例分类" /> @@ -77,4 +77,4 @@ - \ No newline at end of file + diff --git a/app/src/main/res/layout/adapter_category_stats.xml b/app/src/main/res/layout/adapter_category_stats.xml index f8720e4c2..2101c9f65 100644 --- a/app/src/main/res/layout/adapter_category_stats.xml +++ b/app/src/main/res/layout/adapter_category_stats.xml @@ -1,6 +1,7 @@ + tools:text="0.0%" /> diff --git a/app/src/main/res/layout/adapter_data_rule.xml b/app/src/main/res/layout/adapter_data_rule.xml index 79c548ac2..9bd2a6746 100755 --- a/app/src/main/res/layout/adapter_data_rule.xml +++ b/app/src/main/res/layout/adapter_data_rule.xml @@ -47,7 +47,8 @@ android:layout_height="wrap_content" android:layout_weight="1" android:orientation="vertical" - android:paddingStart="4dp"> + android:paddingStart="4dp" + android:paddingEnd="4dp"> - \ No newline at end of file + diff --git a/app/src/main/res/layout/adapter_log.xml b/app/src/main/res/layout/adapter_log.xml index 67b233b9e..6435f8943 100644 --- a/app/src/main/res/layout/adapter_log.xml +++ b/app/src/main/res/layout/adapter_log.xml @@ -23,7 +23,7 @@ android:layout_width="0dp" android:layout_height="wrap_content" android:layout_marginStart="@dimen/one_padding" - android:textSize="10sp" + android:textSize="11sp" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toEndOf="@id/date" app:layout_constraintTop_toTopOf="parent" @@ -40,4 +40,4 @@ app:layout_constraintTop_toBottomOf="@id/app" tools:text="这是一个这是一个非常详细的日志...这是一个这是一个非常详细的日志这是一个这是一个非常详细的日志这是一个这是一个非常详细的日志这是一个这是一个非常详细的日志这是一个这是一个非常详细的日志这是一个这是一个非常详细的日志这是一个这是一个非常详细的日志这是一个这是一个非常详细的日志这是一个这是一个非常详细的日志这是一个这是一个非常详细的日志" /> - \ No newline at end of file + diff --git a/app/src/main/res/layout/adapter_map.xml b/app/src/main/res/layout/adapter_map.xml index a88188b89..b8a54ca80 100644 --- a/app/src/main/res/layout/adapter_map.xml +++ b/app/src/main/res/layout/adapter_map.xml @@ -57,7 +57,7 @@ android:enabled="false" android:minHeight="24dp" android:text="@string/regexLabel" - android:textSize="10sp" + android:textSize="11sp" android:visibility="visible" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" @@ -72,7 +72,6 @@ android:layout_height="@dimen/image_size_small" android:layout_gravity="center_vertical" android:layout_marginHorizontal="@dimen/one_padding" - android:contentDescription="" android:importantForAccessibility="no" android:scaleType="centerInside" android:src="@drawable/icon_map_right" /> @@ -90,4 +89,4 @@ app:textSize="@dimen/abc_text_size_medium_material" tools:text="映射文本很长很长映射文本很长很长很长映射文本很长很长很长映射文本很长很长很长映射文本很长很长很长映射文本很长很长很长很长" /> - \ No newline at end of file + diff --git a/app/src/main/res/layout/adapter_order.xml b/app/src/main/res/layout/adapter_order.xml index 907232061..55a8538fe 100755 --- a/app/src/main/res/layout/adapter_order.xml +++ b/app/src/main/res/layout/adapter_order.xml @@ -15,6 +15,7 @@ + android:textStyle="bold" + tools:text="1月12日" /> - \ No newline at end of file + diff --git a/app/src/main/res/layout/component_payment_info.xml b/app/src/main/res/layout/component_payment_info.xml index 12476b096..f8f265026 100644 --- a/app/src/main/res/layout/component_payment_info.xml +++ b/app/src/main/res/layout/component_payment_info.xml @@ -74,8 +74,9 @@ android:background="?attr/selectableItemBackgroundBorderless" android:clickable="true" android:focusable="true" + android:contentDescription="@string/switch_transfer_direction" android:gravity="center" - android:text="➜" /> + android:text="@string/transfer_direction_arrow" /> + android:text="@string/transfer_direction_arrow" /> + android:text="@string/transfer_direction_arrow" /> - \ No newline at end of file + diff --git a/app/src/main/res/layout/dialog_regex_money.xml b/app/src/main/res/layout/dialog_regex_money.xml index 6ac08f973..fdad98499 100755 --- a/app/src/main/res/layout/dialog_regex_money.xml +++ b/app/src/main/res/layout/dialog_regex_money.xml @@ -38,7 +38,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_margin="@dimen/border_side" - android:text=" ~ " /> + android:text="@string/range_separator" /> - \ No newline at end of file + diff --git a/app/src/main/res/layout/fragment_tag_edit.xml b/app/src/main/res/layout/fragment_tag_edit.xml index 71ea94438..d6ab9f377 100644 --- a/app/src/main/res/layout/fragment_tag_edit.xml +++ b/app/src/main/res/layout/fragment_tag_edit.xml @@ -92,6 +92,7 @@ android:id="@+id/tag_group_input" android:layout_width="match_parent" android:layout_height="wrap_content" + android:hint="@string/tag_group" android:inputType="text" android:maxLength="20" android:completionThreshold="0" /> diff --git a/app/src/main/res/menu/analysis_menu.xml b/app/src/main/res/menu/analysis_menu.xml index 69265689f..9f6283914 100644 --- a/app/src/main/res/menu/analysis_menu.xml +++ b/app/src/main/res/menu/analysis_menu.xml @@ -18,5 +18,5 @@ android:id="@+id/action_clear_all" android:icon="@drawable/menu_icon_clear" android:showAsAction="ifRoom" - android:title="清空所有分析" /> - \ No newline at end of file + android:title="@string/clear_all_analysis" /> + diff --git a/app/src/main/res/values-zh/strings.xml b/app/src/main/res/values-zh/strings.xml index 97bc25bb7..45be01885 100644 --- a/app/src/main/res/values-zh/strings.xml +++ b/app/src/main/res/values-zh/strings.xml @@ -88,7 +88,7 @@ 条件不能为空 账本不能为空 分类不能为空 - 上传中... + 上传中… 更新 分享日志 调试模式 @@ -112,12 +112,12 @@ 编辑账本 编辑账本 输入账本名称 - 账本名称应为 1-20 个字符 + 账本名称应为 1–20 个字符 账本图标 点击选择图标 图标已选择 创建 - 保存中... + 保存中… 账本名称是必需的 @@ -195,7 +195,7 @@ 备份 WebDav 服务提供商 备份成功 - 恢复中... + 恢复中… 无法恢复 【%s】 版本数据,请将应用降级到该版本后重试。 数据库备份失败。 恢复成功 @@ -203,8 +203,8 @@ 网络错误,请在设置中更改源后重试。 服务器上未找到备份文件 下载备份文件 - 准备上传... - 打包数据... + 准备上传… + 打包数据… 请先选择备份保存路径 @@ -248,9 +248,9 @@ 更新规则:%s 两侧的替换内容不能为空。 没有内容被替换 - 打包日志... + 打包日志… 无需更新。长按强制更新。 - 检查更新... + 检查更新… 您确定要删除此账单吗? 您确定要删除此数据吗? 复制 @@ -278,15 +278,15 @@ 仅匹配 仅未匹配 - 下载规则... + 下载规则… 更新失败 - 移除无效规则... + 移除无效规则… 添加规则:%s 解压完成 解压:%s 自动 - 搜索... + 搜索… 购买 通知监控 请选择记账应用 @@ -335,8 +335,8 @@ 在账单页面显示识别规则 自动生成 AI 功能 - 测试规则... - 加载中... + 测试规则… + 加载中… API 地址 自动记录账单时显示提醒 清除数据库 @@ -602,7 +602,7 @@ 运行 AI 辅助 - AI 正在优化您的代码... + AI 正在优化您的代码… AI 优化失败:%s AI 未返回优化代码 没有代码可优化 @@ -934,7 +934,7 @@ 共 %1$d 项 - 清除中... + 清除中… 从不 未设置 %d 秒 @@ -984,18 +984,18 @@ 必须先启用资产管理功能 - 准备备份... - 备份数据库... - 备份偏好设置... - 创建索引文件... - 压缩文件... - 准备恢复... - 解压备份... - 验证备份... - 恢复数据库... - 恢复偏好设置... - 清理缓存... - 重启应用... + 准备备份… + 备份数据库… + 备份偏好设置… + 创建索引文件… + 压缩文件… + 准备恢复… + 解压备份… + 验证备份… + 恢复数据库… + 恢复偏好设置… + 清理缓存… + 重启应用… Canary版本风险警告 @@ -1011,7 +1011,7 @@ JS代码为空 - 执行中... + 执行中… 执行结果 已保存 测试数据为空 @@ -1064,7 +1064,7 @@ 错误 - 开始同步... + 开始同步… 同步完成!已同步 %1$d 条账单 没有需要同步的账单 不去重 @@ -1144,4 +1144,5 @@ 确定要删除所有财务分析任务吗?此操作不可恢复。 已清空所有分析任务 清空失败,请重试 + 切换转账方向 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 34b29df00..3273b1ba6 100755 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -87,7 +87,7 @@ Condition cannot be empty Book cannot be empty Category cannot be empty - Uploading... + Uploading… Update Share log Debug mode @@ -111,12 +111,12 @@ Edit Book Edit Book Enter book name - Book name should be 1-20 characters + Book name should be 1–20 characters Book Icon Select an icon Icon selected Create - Saving... + Saving… Book name is required @@ -195,7 +195,7 @@ Backup WebDav service provider Backup successful - Restoring... + Restoring… Cannot restore 【%s】 version data, please downgrade the app to that version and try again. Database backup failed. Restore successful @@ -203,8 +203,8 @@ Network error, please change source in settings and try again. No backup file found on server Downloading backup file - Preparing to upload... - Packing data... + Preparing to upload… + Packing data… Please select backup save path first @@ -233,18 +233,18 @@ Backup file does not exist (HTTP %1$d): %2$s Failed to download backup file: %s - Preparing backup... - Backing up database... - Backing up preferences... - Creating index file... - Compressing files... - Preparing restore... - Extracting backup... - Validating backup... - Restoring database... - Restoring preferences... - Clearing cache... - Restarting app... + Preparing backup… + Backing up database… + Backing up preferences… + Creating index file… + Compressing files… + Preparing restore… + Extracting backup… + Validating backup… + Restoring database… + Restoring preferences… + Clearing cache… + Restarting app… Remark format Supports using the following Chinese variables:\nBasic: 【Merchant name】, 【Item name】, 【Amount】, 【Category】, 【Book】, 【Source】, 【Original asset】, 【Target asset】, 【Channel】\nExtended: 【Rule name】, 【AI】, 【Currency type】, 【Fee】, 【Tags】, 【Transaction type】, 【Time】 Countdown duration (seconds) @@ -259,9 +259,9 @@ Update Rule: %s The replacement content on both sides must not be empty. No content was replaced - Packing logs... + Packing logs… No update needed. Long press to force update. - Checking for updates... + Checking for updates… Are you sure you want to delete this bill? Are you sure you want to delete this data? Copy @@ -289,9 +289,9 @@ Matched only Unmatched only - Downloading rules... + Downloading rules… Update failed - Removing invalid rules... + Removing invalid rules… Added rule: %s Extraction complete Extracting: %s @@ -314,7 +314,7 @@ %1$s - Enable SMS notifications from your bank %1$s - Grant notification access or set scope to Android System - Search... + Search… Purchase Notification monitoring Please select accounting app @@ -349,8 +349,8 @@ Display the name of the matching rule when editing bills Auto generate AI features - Testing rule... - Loading... + Testing rule… + Loading… API address Show notification for auto-recorded bills Clear database @@ -638,7 +638,7 @@ Run AI Assist - AI is optimizing your code... + AI is optimizing your code… AI optimization failed: %s AI returned no optimized code No code to optimize @@ -899,7 +899,7 @@ When OCR mode uses accessibility authorization, auto-trigger OCR when switching to remembered pages - Clearing... + Clearing… Never Not Set %d seconds @@ -1006,7 +1006,7 @@ This week JS code is empty Test data is empty - Executing... + Executing… Execution Result Saved @@ -1027,7 +1027,7 @@ Ask for confirmation before removing a bill - Starting sync... + Starting sync… Sync completed! %1$d bills synced No bills to sync Last Week @@ -1165,4 +1165,7 @@ Are you sure you want to delete all financial analysis tasks? This operation cannot be undone. All analysis tasks cleared Clear failed, please try again + Switch transfer direction + + From a58bcf001809b92410d7e00e2af0258d523406bf Mon Sep 17 00:00:00 2001 From: Sylthionys <94494882+Sylthionys@users.noreply.github.com> Date: Tue, 16 Jun 2026 01:08:48 +0800 Subject: [PATCH 11/32] :wheelchair: (ui): mark decorative images inaccessible --- app/src/main/res/layout/adapter_book.xml | 3 ++- app/src/main/res/layout/adapter_category_list.xml | 1 + app/src/main/res/layout/adapter_data.xml | 1 + app/src/main/res/layout/adapter_order_item.xml | 1 - app/src/main/res/layout/component_category_icon_more.xml | 2 ++ app/src/main/res/layout/float_tip.xml | 1 + app/src/main/res/layout/float_tip_left.xml | 1 + app/src/main/res/layout/float_tip_top.xml | 2 +- app/src/main/res/layout/fragment_asset_edit.xml | 3 ++- app/src/main/res/layout/fragment_book_edit.xml | 3 ++- app/src/main/res/layout/fragment_category_edit.xml | 1 + app/src/main/res/layout/fragment_intro_page_sync.xml | 1 + app/src/main/res/layout/item_color.xml | 1 + app/src/main/res/layout/menu_item.xml | 1 + app/src/main/res/layout/setting_item_color.xml | 3 ++- app/src/main/res/layout/setting_item_switch.xml | 1 + app/src/main/res/layout/setting_item_text.xml | 1 + app/src/main/res/layout/status_page.xml | 2 ++ app/src/main/res/layout/view_expandable_card.xml | 4 +++- 19 files changed, 26 insertions(+), 7 deletions(-) diff --git a/app/src/main/res/layout/adapter_book.xml b/app/src/main/res/layout/adapter_book.xml index 99483f70c..7a8daa9b2 100755 --- a/app/src/main/res/layout/adapter_book.xml +++ b/app/src/main/res/layout/adapter_book.xml @@ -14,6 +14,7 @@ android:id="@+id/book" android:layout_width="0dp" android:layout_height="0dp" + android:importantForAccessibility="no" android:scaleType="centerCrop" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" @@ -144,4 +145,4 @@ - \ No newline at end of file + diff --git a/app/src/main/res/layout/adapter_category_list.xml b/app/src/main/res/layout/adapter_category_list.xml index fa9466052..4dc281495 100755 --- a/app/src/main/res/layout/adapter_category_list.xml +++ b/app/src/main/res/layout/adapter_category_list.xml @@ -59,6 +59,7 @@ android:layout_height="20dp" android:layout_marginStart="50dp" android:layout_marginTop="-10dp" + android:importantForAccessibility="no" android:src="@drawable/bg_three" /> diff --git a/app/src/main/res/layout/adapter_data.xml b/app/src/main/res/layout/adapter_data.xml index b22a9080a..3fee71843 100755 --- a/app/src/main/res/layout/adapter_data.xml +++ b/app/src/main/res/layout/adapter_data.xml @@ -41,6 +41,7 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:adjustViewBounds="true" + android:importantForAccessibility="no" android:scaleType="fitXY" android:id="@+id/appIcon" android:src="@mipmap/ic_launcher" diff --git a/app/src/main/res/layout/adapter_order_item.xml b/app/src/main/res/layout/adapter_order_item.xml index 939d8809a..9b9313168 100755 --- a/app/src/main/res/layout/adapter_order_item.xml +++ b/app/src/main/res/layout/adapter_order_item.xml @@ -268,7 +268,6 @@ android:layout_height="20dp" android:layout_marginEnd="4dp" android:layout_gravity="center_vertical" - android:contentDescription="" android:importantForAccessibility="no" android:src="@drawable/ic_sync" app:tint="?colorPrimary" /> diff --git a/app/src/main/res/layout/component_category_icon_more.xml b/app/src/main/res/layout/component_category_icon_more.xml index 5300030e4..5cd61bcbc 100644 --- a/app/src/main/res/layout/component_category_icon_more.xml +++ b/app/src/main/res/layout/component_category_icon_more.xml @@ -16,6 +16,7 @@ android:layout_height="@dimen/image_size" android:layout_gravity="center_horizontal" android:background="@drawable/rounded_border" + android:importantForAccessibility="no" android:padding="7dp" android:scaleType="fitCenter" android:src="@drawable/default_cate" /> @@ -26,6 +27,7 @@ android:layout_height="@dimen/image_size_small_min" android:layout_gravity="end|bottom" android:background="@drawable/rounded_border_2" + android:importantForAccessibility="no" android:padding="2dp" android:src="@drawable/ic_more2" /> diff --git a/app/src/main/res/layout/float_tip.xml b/app/src/main/res/layout/float_tip.xml index fa583d58c..e5b4e2715 100755 --- a/app/src/main/res/layout/float_tip.xml +++ b/app/src/main/res/layout/float_tip.xml @@ -58,6 +58,7 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:adjustViewBounds="true" + android:importantForAccessibility="no" android:scaleType="centerInside" android:tint="?colorPrimary" android:src="@drawable/default_cate" diff --git a/app/src/main/res/layout/float_tip_left.xml b/app/src/main/res/layout/float_tip_left.xml index cb9bba700..42797a98b 100755 --- a/app/src/main/res/layout/float_tip_left.xml +++ b/app/src/main/res/layout/float_tip_left.xml @@ -74,6 +74,7 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:adjustViewBounds="true" + android:importantForAccessibility="no" android:scaleType="centerInside" android:tint="?colorPrimary" android:src="@drawable/default_cate" diff --git a/app/src/main/res/layout/float_tip_top.xml b/app/src/main/res/layout/float_tip_top.xml index 0730f05f0..39a18994a 100644 --- a/app/src/main/res/layout/float_tip_top.xml +++ b/app/src/main/res/layout/float_tip_top.xml @@ -46,6 +46,7 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:adjustViewBounds="true" + android:importantForAccessibility="no" android:scaleType="centerInside" android:tint="?attr/colorOnPrimary" app:srcCompat="@drawable/ic_ai" /> @@ -140,4 +141,3 @@ - diff --git a/app/src/main/res/layout/fragment_asset_edit.xml b/app/src/main/res/layout/fragment_asset_edit.xml index e0e365428..fdf8ec355 100644 --- a/app/src/main/res/layout/fragment_asset_edit.xml +++ b/app/src/main/res/layout/fragment_asset_edit.xml @@ -120,6 +120,7 @@ android:layout_width="40dp" android:layout_height="40dp" android:layout_marginEnd="12dp" + android:importantForAccessibility="no" android:scaleType="fitCenter" android:padding="6dp" tools:src="@drawable/default_asset" /> @@ -202,4 +203,4 @@ app:iconTint="?attr/colorOnPrimary" app:cornerRadius="28dp" /> - \ No newline at end of file + diff --git a/app/src/main/res/layout/fragment_book_edit.xml b/app/src/main/res/layout/fragment_book_edit.xml index 8795f5e01..af399b041 100644 --- a/app/src/main/res/layout/fragment_book_edit.xml +++ b/app/src/main/res/layout/fragment_book_edit.xml @@ -95,6 +95,7 @@ android:id="@+id/bookIconPreview" android:layout_width="0dp" android:layout_height="0dp" + android:importantForAccessibility="no" android:scaleType="centerCrop" android:src="@drawable/default_book" app:layout_constraintBottom_toBottomOf="parent" @@ -141,4 +142,4 @@ - \ No newline at end of file + diff --git a/app/src/main/res/layout/fragment_category_edit.xml b/app/src/main/res/layout/fragment_category_edit.xml index 081f4f133..a60633b39 100755 --- a/app/src/main/res/layout/fragment_category_edit.xml +++ b/app/src/main/res/layout/fragment_category_edit.xml @@ -126,6 +126,7 @@ android:layout_height="40dp" android:layout_marginEnd="12dp" android:background="?attr/selectableItemBackgroundBorderless" + android:importantForAccessibility="no" android:scaleType="fitCenter" android:padding="6dp" android:tint="?colorPrimary" diff --git a/app/src/main/res/layout/fragment_intro_page_sync.xml b/app/src/main/res/layout/fragment_intro_page_sync.xml index c13f9540b..99c256d9e 100644 --- a/app/src/main/res/layout/fragment_intro_page_sync.xml +++ b/app/src/main/res/layout/fragment_intro_page_sync.xml @@ -54,6 +54,7 @@ android:id="@+id/sync_image" android:layout_width="wrap_content" android:layout_height="wrap_content" + android:importantForAccessibility="no" android:src="@drawable/ic_warning" android:tint="@color/log_warning" /> diff --git a/app/src/main/res/layout/item_color.xml b/app/src/main/res/layout/item_color.xml index d405541b8..fae142c67 100644 --- a/app/src/main/res/layout/item_color.xml +++ b/app/src/main/res/layout/item_color.xml @@ -39,6 +39,7 @@ android:layout_width="16dp" android:layout_height="16dp" android:layout_gravity="center" + android:importantForAccessibility="no" android:src="@drawable/ic_check" android:visibility="gone" /> diff --git a/app/src/main/res/layout/menu_item.xml b/app/src/main/res/layout/menu_item.xml index e25a6c5b8..34b195da9 100644 --- a/app/src/main/res/layout/menu_item.xml +++ b/app/src/main/res/layout/menu_item.xml @@ -14,6 +14,7 @@ android:layout_height="24dp" android:layout_gravity="center_vertical" android:layout_marginEnd="16dp" + android:importantForAccessibility="no" android:tint="#8E8E8E" android:tintMode="screen" android:src="@mipmap/ic_launcher" /> diff --git a/app/src/main/res/layout/setting_item_color.xml b/app/src/main/res/layout/setting_item_color.xml index 865c3b5c9..27f2ea641 100644 --- a/app/src/main/res/layout/setting_item_color.xml +++ b/app/src/main/res/layout/setting_item_color.xml @@ -25,6 +25,7 @@ android:id="@+id/icon" android:layout_width="@dimen/image_size" android:layout_height="@dimen/image_size" + android:importantForAccessibility="no" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" @@ -55,4 +56,4 @@ app:layout_constraintEnd_toEndOf="parent" app:layout_constraintTop_toTopOf="parent" /> - \ No newline at end of file + diff --git a/app/src/main/res/layout/setting_item_switch.xml b/app/src/main/res/layout/setting_item_switch.xml index db92858fd..ff2211a92 100644 --- a/app/src/main/res/layout/setting_item_switch.xml +++ b/app/src/main/res/layout/setting_item_switch.xml @@ -23,6 +23,7 @@ android:id="@+id/icon" android:layout_width="@dimen/image_size" android:layout_height="@dimen/image_size" + android:importantForAccessibility="no" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" diff --git a/app/src/main/res/layout/setting_item_text.xml b/app/src/main/res/layout/setting_item_text.xml index 71aebbdcd..018b41905 100644 --- a/app/src/main/res/layout/setting_item_text.xml +++ b/app/src/main/res/layout/setting_item_text.xml @@ -24,6 +24,7 @@ android:id="@+id/icon" android:layout_width="@dimen/image_size" android:layout_height="@dimen/image_size" + android:importantForAccessibility="no" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" diff --git a/app/src/main/res/layout/status_page.xml b/app/src/main/res/layout/status_page.xml index db35bf1bc..7c85b41b5 100644 --- a/app/src/main/res/layout/status_page.xml +++ b/app/src/main/res/layout/status_page.xml @@ -31,6 +31,7 @@ android:id="@+id/empty_icon" android:layout_width="200dp" android:layout_height="200dp" + android:importantForAccessibility="no" android:src="@drawable/ic_empty" /> @@ -44,7 +45,8 @@ android:id="@+id/stateIcon" android:layout_width="24dp" android:layout_height="24dp" - android:layout_marginStart="12dp" /> + android:layout_marginStart="12dp" + android:importantForAccessibility="no" /> Date: Tue, 16 Jun 2026 01:15:18 +0800 Subject: [PATCH 12/32] :globe_with_meridians: (ui): format dynamic text with resources --- .../ankio/auto/ui/adapter/AnalysisTaskAdapter.kt | 5 +++-- .../java/net/ankio/auto/ui/adapter/AppAdapter.kt | 5 +++-- .../auto/ui/adapter/CurrencySelectorAdapter.kt | 13 +++++++++---- .../ui/dialog/components/AmountDisplayComponent.kt | 3 ++- .../auto/ui/fragment/CategoryRulePageFragment.kt | 2 +- .../ui/fragment/components/StatusCardComponent.kt | 2 +- app/src/main/java/net/ankio/auto/utils/BillTool.kt | 12 ++++++------ app/src/main/res/values-zh/strings.xml | 2 ++ app/src/main/res/values/strings.xml | 5 +++++ 9 files changed, 32 insertions(+), 17 deletions(-) diff --git a/app/src/main/java/net/ankio/auto/ui/adapter/AnalysisTaskAdapter.kt b/app/src/main/java/net/ankio/auto/ui/adapter/AnalysisTaskAdapter.kt index d8be1385c..89c322f85 100644 --- a/app/src/main/java/net/ankio/auto/ui/adapter/AnalysisTaskAdapter.kt +++ b/app/src/main/java/net/ankio/auto/ui/adapter/AnalysisTaskAdapter.kt @@ -112,7 +112,8 @@ class AnalysisTaskAdapter : BaseAdapter() { } catch (e: Exception) { // 处理包信息获取异常,设置默认值 binding.appName.text = data.packageName - binding.appVersionName.text = "Unknown" + binding.appVersionName.text = binding.root.context.getString(R.string.unknown) binding.appPackageName.text = data.packageName binding.checkbox.isChecked = data.isSelected } @@ -129,4 +130,4 @@ class AppAdapter : BaseAdapter() { } -} \ No newline at end of file +} diff --git a/app/src/main/java/net/ankio/auto/ui/adapter/CurrencySelectorAdapter.kt b/app/src/main/java/net/ankio/auto/ui/adapter/CurrencySelectorAdapter.kt index e58ee25eb..a275fbab7 100644 --- a/app/src/main/java/net/ankio/auto/ui/adapter/CurrencySelectorAdapter.kt +++ b/app/src/main/java/net/ankio/auto/ui/adapter/CurrencySelectorAdapter.kt @@ -24,6 +24,7 @@ import net.ankio.auto.ui.api.BaseViewHolder import net.ankio.auto.ui.utils.load import org.ezbook.server.constant.Currency import org.ezbook.server.db.model.CurrencyModel +import java.util.Locale /** * 币种选择适配器 @@ -118,7 +119,11 @@ class CurrencySelectorAdapter( && data.code != baseCurrencyCode && data.rate > 0 if (showRate) { - binding.rateText.text = "≈ ${formatRate(data.rate)} $baseCurrencyCode" + binding.rateText.text = context.getString( + R.string.approximate_rate_format, + formatRate(data.rate), + baseCurrencyCode + ) binding.rateText.visibility = View.VISIBLE } else { binding.rateText.visibility = View.GONE @@ -127,9 +132,9 @@ class CurrencySelectorAdapter( /** 格式化汇率数值:保留合理精度 */ private fun formatRate(rate: Double): String = when { - rate >= 100 -> String.format("%.0f", rate) - rate >= 1 -> String.format("%.2f", rate) - else -> String.format("%.4f", rate) + rate >= 100 -> String.format(Locale.getDefault(), "%.0f", rate) + rate >= 1 -> String.format(Locale.getDefault(), "%.2f", rate) + else -> String.format(Locale.getDefault(), "%.4f", rate) } override fun areItemsSame(oldItem: CurrencyModel, newItem: CurrencyModel): Boolean = diff --git a/app/src/main/java/net/ankio/auto/ui/dialog/components/AmountDisplayComponent.kt b/app/src/main/java/net/ankio/auto/ui/dialog/components/AmountDisplayComponent.kt index 1cc8dbfa8..4f56b8e5c 100644 --- a/app/src/main/java/net/ankio/auto/ui/dialog/components/AmountDisplayComponent.kt +++ b/app/src/main/java/net/ankio/auto/ui/dialog/components/AmountDisplayComponent.kt @@ -35,6 +35,7 @@ import net.ankio.auto.utils.BillTool import net.ankio.auto.utils.PrefManager import org.ezbook.server.constant.BillType import org.ezbook.server.db.model.BillInfoModel +import java.text.NumberFormat import kotlin.math.abs /** @@ -247,7 +248,7 @@ class AmountDisplayComponent( * @param amount 金额数值 */ private fun setAmount(amount: Double) { - binding.amountContainer.text = amount.toString() + binding.amountContainer.text = NumberFormat.getNumberInstance().format(amount) } /** diff --git a/app/src/main/java/net/ankio/auto/ui/fragment/CategoryRulePageFragment.kt b/app/src/main/java/net/ankio/auto/ui/fragment/CategoryRulePageFragment.kt index 2b1bcf5a4..38e641539 100644 --- a/app/src/main/java/net/ankio/auto/ui/fragment/CategoryRulePageFragment.kt +++ b/app/src/main/java/net/ankio/auto/ui/fragment/CategoryRulePageFragment.kt @@ -312,7 +312,7 @@ class CategoryRulePageFragment : */ private fun updateBatchDeleteUI(selectedCount: Int) { // 更新删除按钮文本和状态 - binding.batchDeleteButton.text = getString(R.string.delete_data) + "($selectedCount)" + binding.batchDeleteButton.text = getString(R.string.delete_data_count, selectedCount) binding.batchDeleteButton.isEnabled = selectedCount > 0 // 更新全选按钮文本 diff --git a/app/src/main/java/net/ankio/auto/ui/fragment/components/StatusCardComponent.kt b/app/src/main/java/net/ankio/auto/ui/fragment/components/StatusCardComponent.kt index cc7dd63ca..70cef696b 100644 --- a/app/src/main/java/net/ankio/auto/ui/fragment/components/StatusCardComponent.kt +++ b/app/src/main/java/net/ankio/auto/ui/fragment/components/StatusCardComponent.kt @@ -105,7 +105,7 @@ class StatusCardComponent(binding: CardStatusBinding) : drawable = R.drawable.home_active_error ) } - binding.subtitleText.text = "v${data.versionName}" + binding.subtitleText.text = context.getString(R.string.version_format, data.versionName) binding.ruleVersionText.text = data.ruleVersion binding.ruleUpdateText.text = data.ruleUpdate diff --git a/app/src/main/java/net/ankio/auto/utils/BillTool.kt b/app/src/main/java/net/ankio/auto/utils/BillTool.kt index 3b04a2238..7746babd5 100644 --- a/app/src/main/java/net/ankio/auto/utils/BillTool.kt +++ b/app/src/main/java/net/ankio/auto/utils/BillTool.kt @@ -118,19 +118,19 @@ object BillTool { view.setTextColor(color) // 有货币单位则追加到金额后面,如 "- 100.0 USD" val suffix = if (currencyUnit.isNullOrEmpty()) "" else " $currencyUnit" - when (t) { + val sign = when (t) { BillType.Expend, BillType.ExpendReimbursement, BillType.ExpendLending, BillType.ExpendRepayment -> { - view.text = "- $price$suffix" + "- " } BillType.Income, BillType.IncomeLending, BillType.IncomeRepayment, BillType.IncomeReimbursement -> { - view.text = "+ $price$suffix" + "+ " } - else -> { - view.text = "$price$suffix" - } + else -> "" } + view.text = + view.context.getString(R.string.bill_amount_format, sign, price.toString(), suffix) } /** diff --git a/app/src/main/res/values-zh/strings.xml b/app/src/main/res/values-zh/strings.xml index 45be01885..d86299af2 100644 --- a/app/src/main/res/values-zh/strings.xml +++ b/app/src/main/res/values-zh/strings.xml @@ -1145,4 +1145,6 @@ 已清空所有分析任务 清空失败,请重试 切换转账方向 + ≈ %1$s %2$s + 删除(%d) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 3273b1ba6..b941a8f9f 100755 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1168,4 +1168,9 @@ Switch transfer direction + %d%% + ≈ %1$s %2$s + Delete (%d) + v%s + %1$s%2$s%3$s From 62411bf3eaf2bd59d752c7a7725fae65e06cb955 Mon Sep 17 00:00:00 2001 From: Sylthionys <94494882+Sylthionys@users.noreply.github.com> Date: Tue, 16 Jun 2026 01:20:55 +0800 Subject: [PATCH 13/32] :globe_with_meridians: (ui): use plurals for counted text --- .../ui/dialog/components/BillTagComponent.kt | 4 +- .../dialog/components/PaymentInfoComponent.kt | 6 +- .../ui/fragment/CategoryRulePageFragment.kt | 2 +- .../intro/IntroPagePermissionFragment.kt | 8 ++- .../DataManagementPreferenceFragment.kt | 6 +- .../settings/InteractionPreferenceFragment.kt | 2 +- .../settings/RecordingPreferenceFragment.kt | 18 +++++- .../java/net/ankio/auto/utils/BillTool.kt | 4 +- app/src/main/res/values-zh/strings.xml | 50 +++++++++++---- app/src/main/res/values/strings.xml | 62 +++++++++++++++---- .../main/res/xml/settings_data_management.xml | 2 +- app/src/main/res/xml/settings_recording.xml | 2 +- 12 files changed, 130 insertions(+), 36 deletions(-) diff --git a/app/src/main/java/net/ankio/auto/ui/dialog/components/BillTagComponent.kt b/app/src/main/java/net/ankio/auto/ui/dialog/components/BillTagComponent.kt index 90508333f..260c9779d 100644 --- a/app/src/main/java/net/ankio/auto/ui/dialog/components/BillTagComponent.kt +++ b/app/src/main/java/net/ankio/auto/ui/dialog/components/BillTagComponent.kt @@ -82,7 +82,9 @@ class BillTagComponent( updateBillTags(selected) }, onSelectionLimitReached = { limit -> - ToastUtils.info(context.getString(R.string.bill_tag_limit, limit)) + ToastUtils.info( + context.resources.getQuantityString(R.plurals.bill_tag_limit, limit, limit) + ) } ) recyclerView.adapter = adapter diff --git a/app/src/main/java/net/ankio/auto/ui/dialog/components/PaymentInfoComponent.kt b/app/src/main/java/net/ankio/auto/ui/dialog/components/PaymentInfoComponent.kt index cf3662807..02db07a7e 100644 --- a/app/src/main/java/net/ankio/auto/ui/dialog/components/PaymentInfoComponent.kt +++ b/app/src/main/java/net/ankio/auto/ui/dialog/components/PaymentInfoComponent.kt @@ -198,7 +198,11 @@ class PaymentInfoComponent( val text = if (selectedBills.isEmpty()) { context.getString(R.string.float_choose_bill) } else { - context.getString(R.string.float_choose_bills, selectedBills.size) + context.resources.getQuantityString( + R.plurals.float_choose_bills, + selectedBills.size, + selectedBills.size + ) } binding.chooseBillButton.text = text } diff --git a/app/src/main/java/net/ankio/auto/ui/fragment/CategoryRulePageFragment.kt b/app/src/main/java/net/ankio/auto/ui/fragment/CategoryRulePageFragment.kt index 38e641539..f715515fc 100644 --- a/app/src/main/java/net/ankio/auto/ui/fragment/CategoryRulePageFragment.kt +++ b/app/src/main/java/net/ankio/auto/ui/fragment/CategoryRulePageFragment.kt @@ -330,7 +330,7 @@ class CategoryRulePageFragment : private fun showBatchDeleteConfirmDialog(count: Int) { BaseSheetDialog.create(requireActivity()) .setTitle(getString(R.string.batch_delete_title)) - .setMessage(getString(R.string.batch_delete_confirm, count)) + .setMessage(resources.getQuantityString(R.plurals.batch_delete_confirm, count, count)) .setPositiveButton(getString(R.string.sure_msg)) { _, _ -> performBatchDelete() } diff --git a/app/src/main/java/net/ankio/auto/ui/fragment/intro/IntroPagePermissionFragment.kt b/app/src/main/java/net/ankio/auto/ui/fragment/intro/IntroPagePermissionFragment.kt index f770c37e4..4ae185dd6 100644 --- a/app/src/main/java/net/ankio/auto/ui/fragment/intro/IntroPagePermissionFragment.kt +++ b/app/src/main/java/net/ankio/auto/ui/fragment/intro/IntroPagePermissionFragment.kt @@ -252,7 +252,13 @@ class IntroPagePermissionFragment : BaseIntroPageFragment("setting_backup_keep_count")?.apply { summary = - getString(R.string.setting_backup_keep_count_summary, PrefManager.backupKeepCount) + resources.getQuantityString( + R.plurals.setting_backup_keep_count_summary, + PrefManager.backupKeepCount, + PrefManager.backupKeepCount + ) } // 更新WebDAV配置显示 diff --git a/app/src/main/java/net/ankio/auto/ui/fragment/settings/InteractionPreferenceFragment.kt b/app/src/main/java/net/ankio/auto/ui/fragment/settings/InteractionPreferenceFragment.kt index de3e4321c..4fda040ef 100644 --- a/app/src/main/java/net/ankio/auto/ui/fragment/settings/InteractionPreferenceFragment.kt +++ b/app/src/main/java/net/ankio/auto/ui/fragment/settings/InteractionPreferenceFragment.kt @@ -132,7 +132,7 @@ class InteractionPreferenceFragment : BasePreferenceFragment() { private fun Preference.updateSummary() { val timeoutValue = PrefManager.floatTimeoutOff summary = if (timeoutValue > 0) { - getString(R.string.setting_timeout_seconds, timeoutValue) + resources.getQuantityString(R.plurals.setting_timeout_seconds, timeoutValue, timeoutValue) } else { getString(R.string.setting_float_badge_disabled) } diff --git a/app/src/main/java/net/ankio/auto/ui/fragment/settings/RecordingPreferenceFragment.kt b/app/src/main/java/net/ankio/auto/ui/fragment/settings/RecordingPreferenceFragment.kt index 45426e592..3ea45444d 100644 --- a/app/src/main/java/net/ankio/auto/ui/fragment/settings/RecordingPreferenceFragment.kt +++ b/app/src/main/java/net/ankio/auto/ui/fragment/settings/RecordingPreferenceFragment.kt @@ -208,7 +208,11 @@ class RecordingPreferenceFragment : BasePreferenceFragment() { private fun updateSelectedCurrenciesSummary() { findPreference("selectedCurrencies")?.apply { val count = PrefManager.getSelectedCurrencySet().size - summary = getString(R.string.setting_selected_currencies_summary, count) + summary = resources.getQuantityString( + R.plurals.setting_selected_currencies_summary, + count, + count + ) } } @@ -336,13 +340,21 @@ class RecordingPreferenceFragment : BasePreferenceFragment() { // 自动去重时间阈值 findPreference("autoGroupTimeThreshold")?.apply { val threshold = PrefManager.autoGroupTimeThreshold - summary = getString(R.string.setting_auto_group_time_threshold_summary, threshold) + summary = resources.getQuantityString( + R.plurals.setting_auto_group_time_threshold_summary, + threshold, + threshold + ) } // 转账合并时间阈值 findPreference("autoTransferTimeThreshold")?.apply { val threshold = PrefManager.autoTransferTimeThreshold - summary = getString(R.string.setting_auto_transfer_time_threshold_summary, threshold) + summary = resources.getQuantityString( + R.plurals.setting_auto_transfer_time_threshold_summary, + threshold, + threshold + ) } } diff --git a/app/src/main/java/net/ankio/auto/utils/BillTool.kt b/app/src/main/java/net/ankio/auto/utils/BillTool.kt index 7746babd5..f6cf6d560 100644 --- a/app/src/main/java/net/ankio/auto/utils/BillTool.kt +++ b/app/src/main/java/net/ankio/auto/utils/BillTool.kt @@ -186,7 +186,9 @@ object BillTool { } } - ToastUtils.info(autoApp.getString(R.string.sync_completed, syncedCount)) + ToastUtils.info( + autoApp.resources.getQuantityString(R.plurals.sync_completed, syncedCount, syncedCount) + ) } diff --git a/app/src/main/res/values-zh/strings.xml b/app/src/main/res/values-zh/strings.xml index d86299af2..fbf0948ce 100644 --- a/app/src/main/res/values-zh/strings.xml +++ b/app/src/main/res/values-zh/strings.xml @@ -41,7 +41,9 @@ 确认 批量删除 批量删除 - 确认删除 %d 条规则?删除后无法恢复。 + + 确认删除 %d 条规则?删除后无法恢复。 + 全选 取消全选 添加 @@ -321,7 +323,9 @@ 借出 借入 请选择账单 - 已选择 %d 个账单 + + 已选择 %d 个账单 + 稳定版 测试版 金丝雀版 @@ -689,7 +693,9 @@ 选择或输入组名 标签 暂无可用标签 - 最多选择 %d 个标签 + + 最多选择 %d 个标签 + 选择标签 选择颜色 标签创建成功 @@ -751,11 +757,15 @@ 自动去重 自动识别重复账单并进行去重 去重时间阈值 - 前后 %d 秒内的相似账单会被去重 + + 前后 %d 秒内的相似账单会被去重 + 自动识别转账账单 将短时间内发生的相同金额的收入和支出账单识别为转账账单 转账合并时间阈值 - 前后 %d 秒内的相同金额收入和支出账单会被识别为转账 + + 前后 %d 秒内的相同金额收入和支出账单会被识别为转账 + 自动记录账单时显示提醒 成功加载时显示提醒(只在Xposed模式) 默认账本 @@ -786,7 +796,10 @@ 本位币 当前:%s 常用币种 - 已选择 %d 个常用币种 + + 已选择 %d 个常用币种 + + 配置常用币种 报销管理 启用报销跟踪 债务管理 @@ -800,7 +813,10 @@ 自动备份 自动本地备份数据 备份保留数量 - 保留 %d 个备份文件(适用于本地和WebDAV备份) + + 保留 %d 个备份文件(适用于本地和WebDAV备份) + + 配置本地和 WebDAV 备份保留数量 备份保留数量已更新为 %d 请输入1到100之间的有效数字 WebDAV 服务器地址 @@ -897,7 +913,9 @@ 可选 - %d个可选权限未授予,可能影响部分功能 + + %d 个可选权限未授予,可能影响部分功能 + 允许应用读取传入通知以捕获账单提醒。OCR 模式下为可选。 允许应用读取短信以获取支付确认码。OCR 模式下为可选。 @@ -925,19 +943,25 @@ 禁用规则 确定要禁用规则"%s"吗? 未找到规则 - 已成功禁用 %d 条规则 + + 已成功禁用 %d 条规则 + 禁用规则失败 替换预览 确认替换 - 共 %1$d 项 + + 共 %1$d 项 + 清除中… 从不 未设置 - %d 秒 + + %d 秒 + 编辑账单 关闭 自动记录 @@ -1065,7 +1089,9 @@ 开始同步… - 同步完成!已同步 %1$d 条账单 + + 同步完成!已同步 %1$d 条账单 + 没有需要同步的账单 不去重 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index b941a8f9f..463b0da2f 100755 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -40,7 +40,10 @@ Confirm Batch delete Batch delete - Are you sure you want to delete %d rules? This action cannot be undone. + + Are you sure you want to delete %d rule? This action cannot be undone. + Are you sure you want to delete %d rules? This action cannot be undone. + Select all Deselect all Add @@ -333,7 +336,10 @@ Lend Borrow Please select bill - Selected %d bills + + Selected %d bill + Selected %d bills + Stable Beta Canary @@ -552,7 +558,10 @@ Continue Some required permissions are still missing. Please grant them before continuing. optional - %d optional permissions not granted, may affect some features + + %d optional permission not granted, may affect some features + %d optional permissions not granted, may affect some features + Root or Shizuku @@ -706,7 +715,10 @@ Disable Rule Are you sure you want to disable the rule \"%s\"? Rule not found - Successfully disabled %d rule(s) + + Successfully disabled %d rule + Successfully disabled %d rules + Failed to disable rule @@ -721,7 +733,10 @@ Confirm Replace Preview Confirm Replace - %1$d items total + + %1$d item total + %1$d items total + Rules contain errors @@ -762,7 +777,10 @@ Select or enter group name Tags No tags available - You can select up to %d tags + + You can select up to %d tag + You can select up to %d tags + Select tags Select Color Are you sure to delete tag "%s"? @@ -822,11 +840,17 @@ Automatic deduplication Automatically detect and merge duplicate bills Deduplication Time Threshold - Similar bills within %d seconds will be deduplicated + + Similar bills within %d second will be deduplicated + Similar bills within %d seconds will be deduplicated + Auto Transfer Recognition Identify income and expense bills with the same amount within a short time as transfer bills Transfer Merge Time Threshold - Income and expense bills with the same amount within %d seconds will be identified as transfer + + Income and expense bills with the same amount within %d second will be identified as transfer + Income and expense bills with the same amount within %d seconds will be identified as transfer + Show notification when auto recording bills Show notification when loading successfully Default Book @@ -902,7 +926,10 @@ Clearing… Never Not Set - %d seconds + + %d second + %d seconds + Edit Bill Asset Management Enable asset management features @@ -911,7 +938,11 @@ Base Currency Current: %s Favorite Currencies - Selected %d currencies for quick access + + Selected %d currency for quick access + Selected %d currencies for quick access + + Configure currencies for quick access Reimbursement Enable reimbursement tracking Debt Management @@ -925,7 +956,11 @@ Auto Backup Automatically backup data locally Backup Keep Count - Keep %d backup files (applies to both local and WebDAV backups) + + Keep %d backup file (applies to both local and WebDAV backups) + Keep %d backup files (applies to both local and WebDAV backups) + + Configure how many local and WebDAV backups to keep Backup keep count updated to %d Please enter a valid number between 1 and 100 WebDAV Server URL @@ -1028,7 +1063,10 @@ Starting sync… - Sync completed! %1$d bills synced + + Sync completed! %1$d bill synced + Sync completed! %1$d bills synced + No bills to sync Last Week Last 30 Days diff --git a/app/src/main/res/xml/settings_data_management.xml b/app/src/main/res/xml/settings_data_management.xml index 2df458fbd..b6bb853e8 100644 --- a/app/src/main/res/xml/settings_data_management.xml +++ b/app/src/main/res/xml/settings_data_management.xml @@ -13,7 +13,7 @@ diff --git a/app/src/main/res/xml/settings_recording.xml b/app/src/main/res/xml/settings_recording.xml index 5bfd6d407..013617c50 100644 --- a/app/src/main/res/xml/settings_recording.xml +++ b/app/src/main/res/xml/settings_recording.xml @@ -168,7 +168,7 @@ android:icon="@drawable/float_money" android:key="selectedCurrencies" app:dependency="featureMultiCurrency" - android:summary="@string/setting_selected_currencies_summary" + android:summary="@string/setting_selected_currencies_summary_default" android:title="@string/setting_selected_currencies" /> Date: Tue, 16 Jun 2026 01:25:52 +0800 Subject: [PATCH 14/32] :zap: (ui): use precise list and layout updates --- .../auto/ui/adapter/CategoryRuleAdapter.kt | 10 ++++++-- .../auto/ui/adapter/TagSelectorAdapter.kt | 10 ++++++-- .../settings/PageSignaturesFragment.kt | 23 ++++++++++--------- app/src/main/res/layout/adapter_app.xml | 6 +++-- .../res/layout/adapter_asset_group_header.xml | 4 +++- .../main/res/layout/adapter_asset_list.xml | 6 +++-- app/src/main/res/layout/adapter_assets.xml | 4 +++- app/src/main/res/layout/adapter_auto_app.xml | 6 +++-- app/src/main/res/layout/adapter_backup.xml | 4 +++- app/src/main/res/layout/adapter_book_bill.xml | 4 +++- .../res/layout/adapter_currency_select.xml | 4 +++- .../main/res/layout/adapter_order_item.xml | 2 +- .../res/layout/adapter_replace_preview.xml | 4 +++- .../main/res/layout/component_book_header.xml | 5 +++- .../main/res/layout/dialog_bill_select.xml | 2 +- app/src/main/res/layout/dialog_loading.xml | 1 - .../main/res/layout/fragment_plugin_data.xml | 11 ++++++--- 17 files changed, 72 insertions(+), 34 deletions(-) diff --git a/app/src/main/java/net/ankio/auto/ui/adapter/CategoryRuleAdapter.kt b/app/src/main/java/net/ankio/auto/ui/adapter/CategoryRuleAdapter.kt index c119528b9..4b2cd3bb7 100755 --- a/app/src/main/java/net/ankio/auto/ui/adapter/CategoryRuleAdapter.kt +++ b/app/src/main/java/net/ankio/auto/ui/adapter/CategoryRuleAdapter.kt @@ -71,7 +71,7 @@ class CategoryRuleAdapter( if (!value) { selectedIds.clear() } - notifyDataSetChanged() + notifySelectionStateChanged() } /** @@ -229,10 +229,16 @@ class CategoryRuleAdapter( selectedIds.clear() selectedIds.addAll(getItems().map { it.id }) } - notifyDataSetChanged() + notifySelectionStateChanged() onSelectionChanged?.invoke(selectedIds.size) } + private fun notifySelectionStateChanged() { + if (itemCount > 0) { + notifyItemRangeChanged(0, itemCount) + } + } + /** * 移除选中的项目 */ diff --git a/app/src/main/java/net/ankio/auto/ui/adapter/TagSelectorAdapter.kt b/app/src/main/java/net/ankio/auto/ui/adapter/TagSelectorAdapter.kt index 653b57756..a5c29b3fa 100644 --- a/app/src/main/java/net/ankio/auto/ui/adapter/TagSelectorAdapter.kt +++ b/app/src/main/java/net/ankio/auto/ui/adapter/TagSelectorAdapter.kt @@ -67,7 +67,7 @@ class TagSelectorAdapter( this.selectedTags.addAll( if (selectionLimit > 0) selectedTags.take(selectionLimit) else selectedTags ) - notifyDataSetChanged() + notifySelectionStateChanged() } companion object { @@ -222,7 +222,13 @@ class TagSelectorAdapter( fun clearSelection() { if (isEditMode) return selectedTags.clear() - notifyDataSetChanged() + notifySelectionStateChanged() + } + + private fun notifySelectionStateChanged() { + if (itemCount > 0) { + notifyItemRangeChanged(0, itemCount) + } } override fun areItemsSame(oldItem: TagModel, newItem: TagModel): Boolean { diff --git a/app/src/main/java/net/ankio/auto/ui/fragment/settings/PageSignaturesFragment.kt b/app/src/main/java/net/ankio/auto/ui/fragment/settings/PageSignaturesFragment.kt index 52cb5b8ab..79aec08b5 100644 --- a/app/src/main/java/net/ankio/auto/ui/fragment/settings/PageSignaturesFragment.kt +++ b/app/src/main/java/net/ankio/auto/ui/fragment/settings/PageSignaturesFragment.kt @@ -18,7 +18,9 @@ package net.ankio.auto.ui.fragment.settings import android.view.LayoutInflater import android.view.ViewGroup import androidx.navigation.fragment.findNavController +import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.ListAdapter import androidx.recyclerview.widget.RecyclerView import net.ankio.auto.R import net.ankio.auto.databinding.FragmentPageSignaturesBinding @@ -79,14 +81,7 @@ class PageSignaturesFragment : BaseFragment() { private class Adapter( private val onDelete: (PageSignature) -> Unit - ) : RecyclerView.Adapter() { - - private var items: List = emptyList() - - fun submitList(list: List) { - items = list - notifyDataSetChanged() - } + ) : ListAdapter(DiffCallback) { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): VH { val b = @@ -95,7 +90,7 @@ class PageSignaturesFragment : BaseFragment() { } override fun onBindViewHolder(holder: VH, position: Int) { - val sig = items[position] + val sig = getItem(position) val appInfo = getAppInfoFromPackageName(sig.packageName) holder.binding.appIcon.setImageDrawable(appInfo?.icon) holder.binding.appName.text = appInfo?.name ?: sig.packageName @@ -109,8 +104,14 @@ class PageSignaturesFragment : BaseFragment() { holder.itemView.setOnLongClickListener { onDelete(sig); true } } - override fun getItemCount(): Int = items.size - class VH(val binding: ItemPageSignatureBinding) : RecyclerView.ViewHolder(binding.root) + + private object DiffCallback : DiffUtil.ItemCallback() { + override fun areItemsTheSame(oldItem: PageSignature, newItem: PageSignature): Boolean = + oldItem.key() == newItem.key() + + override fun areContentsTheSame(oldItem: PageSignature, newItem: PageSignature): Boolean = + oldItem == newItem + } } } diff --git a/app/src/main/res/layout/adapter_app.xml b/app/src/main/res/layout/adapter_app.xml index 823ae8a9a..5dd1d37c1 100644 --- a/app/src/main/res/layout/adapter_app.xml +++ b/app/src/main/res/layout/adapter_app.xml @@ -1,3 +1,4 @@ + + android:paddingEnd="24dp" + tools:ignore="Overdraw"> - \ No newline at end of file + diff --git a/app/src/main/res/layout/adapter_asset_group_header.xml b/app/src/main/res/layout/adapter_asset_group_header.xml index 3dc0cbc32..0190c3322 100644 --- a/app/src/main/res/layout/adapter_asset_group_header.xml +++ b/app/src/main/res/layout/adapter_asset_group_header.xml @@ -12,6 +12,7 @@ ~ See the License for the specific language governing permissions and ~ limitations under the License. --> + + android:paddingVertical="12dp" + tools:ignore="Overdraw"> + + android:minHeight="72dp" + tools:ignore="Overdraw"> - \ No newline at end of file + diff --git a/app/src/main/res/layout/adapter_assets.xml b/app/src/main/res/layout/adapter_assets.xml index 071c8f076..26d23edc2 100755 --- a/app/src/main/res/layout/adapter_assets.xml +++ b/app/src/main/res/layout/adapter_assets.xml @@ -1,3 +1,4 @@ + + android:paddingEnd="24dp" + tools:ignore="Overdraw"> + android:paddingEnd="24dp" + tools:ignore="Overdraw"> - \ No newline at end of file + diff --git a/app/src/main/res/layout/adapter_backup.xml b/app/src/main/res/layout/adapter_backup.xml index ed7fc95c9..427603569 100644 --- a/app/src/main/res/layout/adapter_backup.xml +++ b/app/src/main/res/layout/adapter_backup.xml @@ -1,3 +1,4 @@ + + android:paddingVertical="16dp" + tools:ignore="Overdraw"> + android:paddingEnd="16dp" + tools:ignore="Overdraw"> + + android:paddingEnd="16dp" + tools:ignore="Overdraw"> + + android:padding="16dp" + tools:ignore="Overdraw"> + + android:layout_height="wrap_content" + tools:ignore="MergeRootFrame"> diff --git a/app/src/main/res/layout/dialog_loading.xml b/app/src/main/res/layout/dialog_loading.xml index 11d930289..b77991d70 100644 --- a/app/src/main/res/layout/dialog_loading.xml +++ b/app/src/main/res/layout/dialog_loading.xml @@ -17,7 +17,6 @@ xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent" - android:background="@color/transparent" android:gravity="center" android:orientation="vertical" android:padding="20dp"> diff --git a/app/src/main/res/layout/fragment_plugin_data.xml b/app/src/main/res/layout/fragment_plugin_data.xml index 70d43743c..d3109440d 100644 --- a/app/src/main/res/layout/fragment_plugin_data.xml +++ b/app/src/main/res/layout/fragment_plugin_data.xml @@ -15,6 +15,7 @@ @@ -59,21 +60,25 @@ app:layout_constraintTop_toTopOf="parent"> + + android:gravity="start|center_vertical" + android:orientation="horizontal" + tools:ignore="UselessParent"> + + android:orientation="horizontal" + tools:ignore="UselessParent"> Date: Tue, 16 Jun 2026 01:34:07 +0800 Subject: [PATCH 15/32] :art: (resources): audit icon and vector assets --- .../default_asset.png | Bin .../default_book.webp | Bin .../default_cate.png | Bin .../res/drawable/bottom_unselect_setting.xml | 3 +++ .../main/res/drawable/home_app_book_data.xml | 3 +++ app/src/main/res/drawable/home_msg_telegram.xml | 3 +++ app/src/main/res/drawable/ic_apps.xml | 7 +++++-- .../res/drawable/ic_bill_flag_not_budget.xml | 5 ++++- app/src/main/res/drawable/ic_bug_report.xml | 7 +++++-- .../main/res/drawable/ic_calendar_month2.xml | 3 +++ app/src/main/res/drawable/ic_empty.xml | 5 ++++- app/src/main/res/drawable/ic_key.xml | 5 ++++- app/src/main/res/drawable/ic_net_error.xml | 5 ++++- app/src/main/res/drawable/img_geekbar.xml | 5 ++++- .../main/res/drawable/scanner_svgrepo_com.xml | 3 +++ .../res/drawable/setting2_icon_language.xml | 5 ++++- .../main/res/drawable/xposed_framework_icon.xml | 3 +++ .../ic_launcher.xml | 3 ++- .../ic_launcher_round.xml | 3 ++- app/src/main/res/mipmap-hdpi/ic_launcher.webp | Bin 3022 -> 0 bytes .../main/res/mipmap-hdpi/ic_launcher_round.webp | Bin 5398 -> 0 bytes app/src/main/res/mipmap-mdpi/ic_launcher.webp | Bin 2108 -> 0 bytes .../main/res/mipmap-mdpi/ic_launcher_round.webp | Bin 3330 -> 0 bytes app/src/main/res/mipmap-xhdpi/ic_launcher.webp | Bin 4288 -> 0 bytes .../res/mipmap-xhdpi/ic_launcher_round.webp | Bin 7418 -> 0 bytes app/src/main/res/mipmap-xxhdpi/ic_launcher.webp | Bin 6830 -> 0 bytes .../res/mipmap-xxhdpi/ic_launcher_round.webp | Bin 11984 -> 0 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.webp | Bin 9628 -> 0 bytes .../res/mipmap-xxxhdpi/ic_launcher_round.webp | Bin 16806 -> 0 bytes 29 files changed, 56 insertions(+), 12 deletions(-) rename app/src/main/res/{drawable => drawable-nodpi}/default_asset.png (100%) rename app/src/main/res/{drawable => drawable-nodpi}/default_book.webp (100%) mode change 100755 => 100644 rename app/src/main/res/{drawable => drawable-nodpi}/default_cate.png (100%) mode change 100755 => 100644 rename app/src/main/res/{mipmap-anydpi-v26 => mipmap-anydpi}/ic_launcher.xml (74%) mode change 100755 => 100644 rename app/src/main/res/{mipmap-anydpi-v26 => mipmap-anydpi}/ic_launcher_round.xml (74%) mode change 100755 => 100644 delete mode 100755 app/src/main/res/mipmap-hdpi/ic_launcher.webp delete mode 100755 app/src/main/res/mipmap-hdpi/ic_launcher_round.webp delete mode 100755 app/src/main/res/mipmap-mdpi/ic_launcher.webp delete mode 100755 app/src/main/res/mipmap-mdpi/ic_launcher_round.webp delete mode 100755 app/src/main/res/mipmap-xhdpi/ic_launcher.webp delete mode 100755 app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp delete mode 100755 app/src/main/res/mipmap-xxhdpi/ic_launcher.webp delete mode 100755 app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp delete mode 100755 app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp delete mode 100755 app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp diff --git a/app/src/main/res/drawable/default_asset.png b/app/src/main/res/drawable-nodpi/default_asset.png similarity index 100% rename from app/src/main/res/drawable/default_asset.png rename to app/src/main/res/drawable-nodpi/default_asset.png diff --git a/app/src/main/res/drawable/default_book.webp b/app/src/main/res/drawable-nodpi/default_book.webp old mode 100755 new mode 100644 similarity index 100% rename from app/src/main/res/drawable/default_book.webp rename to app/src/main/res/drawable-nodpi/default_book.webp diff --git a/app/src/main/res/drawable/default_cate.png b/app/src/main/res/drawable-nodpi/default_cate.png old mode 100755 new mode 100644 similarity index 100% rename from app/src/main/res/drawable/default_cate.png rename to app/src/main/res/drawable-nodpi/default_cate.png diff --git a/app/src/main/res/drawable/bottom_unselect_setting.xml b/app/src/main/res/drawable/bottom_unselect_setting.xml index 98f720aa6..68b1302bc 100755 --- a/app/src/main/res/drawable/bottom_unselect_setting.xml +++ b/app/src/main/res/drawable/bottom_unselect_setting.xml @@ -13,11 +13,14 @@ ~ limitations under the License. --> + + + + android:tint="?colorOnBackground" + tools:ignore="VectorPath"> - \ No newline at end of file + diff --git a/app/src/main/res/drawable/ic_bill_flag_not_budget.xml b/app/src/main/res/drawable/ic_bill_flag_not_budget.xml index edc86db39..8ba22811e 100644 --- a/app/src/main/res/drawable/ic_bill_flag_not_budget.xml +++ b/app/src/main/res/drawable/ic_bill_flag_not_budget.xml @@ -1,9 +1,12 @@ + + android:tint="?colorOnBackground" + tools:ignore="VectorPath"> diff --git a/app/src/main/res/drawable/ic_bug_report.xml b/app/src/main/res/drawable/ic_bug_report.xml index 5145336f3..6693fedd7 100644 --- a/app/src/main/res/drawable/ic_bug_report.xml +++ b/app/src/main/res/drawable/ic_bug_report.xml @@ -1,10 +1,13 @@ + + android:tint="?colorOnBackground" + tools:ignore="VectorPath"> - \ No newline at end of file + diff --git a/app/src/main/res/drawable/ic_calendar_month2.xml b/app/src/main/res/drawable/ic_calendar_month2.xml index 388df5766..3c0c86915 100644 --- a/app/src/main/res/drawable/ic_calendar_month2.xml +++ b/app/src/main/res/drawable/ic_calendar_month2.xml @@ -13,11 +13,14 @@ ~ limitations under the License. --> + + + android:viewportHeight="500" + tools:ignore="VectorPath,VectorRaster"> + android:tint="?colorOnBackground" + tools:ignore="VectorPath"> diff --git a/app/src/main/res/drawable/ic_net_error.xml b/app/src/main/res/drawable/ic_net_error.xml index 9ba93eb91..cad70426c 100644 --- a/app/src/main/res/drawable/ic_net_error.xml +++ b/app/src/main/res/drawable/ic_net_error.xml @@ -1,8 +1,11 @@ + + android:viewportHeight="500" + tools:ignore="VectorPath,VectorRaster"> diff --git a/app/src/main/res/drawable/img_geekbar.xml b/app/src/main/res/drawable/img_geekbar.xml index 20a5b83ac..0cde36030 100644 --- a/app/src/main/res/drawable/img_geekbar.xml +++ b/app/src/main/res/drawable/img_geekbar.xml @@ -1,9 +1,12 @@ + + android:tint="?colorOnBackground" + tools:ignore="VectorPath"> diff --git a/app/src/main/res/drawable/scanner_svgrepo_com.xml b/app/src/main/res/drawable/scanner_svgrepo_com.xml index 0f250b6af..7be761bf7 100644 --- a/app/src/main/res/drawable/scanner_svgrepo_com.xml +++ b/app/src/main/res/drawable/scanner_svgrepo_com.xml @@ -13,11 +13,14 @@ ~ limitations under the License. --> + diff --git a/app/src/main/res/drawable/setting2_icon_language.xml b/app/src/main/res/drawable/setting2_icon_language.xml index f7d7a3749..64e800499 100644 --- a/app/src/main/res/drawable/setting2_icon_language.xml +++ b/app/src/main/res/drawable/setting2_icon_language.xml @@ -1,9 +1,12 @@ + + android:tint="?colorOnBackground" + tools:ignore="VectorPath"> diff --git a/app/src/main/res/drawable/xposed_framework_icon.xml b/app/src/main/res/drawable/xposed_framework_icon.xml index 972f7614e..eebd39bfe 100644 --- a/app/src/main/res/drawable/xposed_framework_icon.xml +++ b/app/src/main/res/drawable/xposed_framework_icon.xml @@ -13,11 +13,14 @@ ~ limitations under the License. --> + - \ No newline at end of file + + diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/app/src/main/res/mipmap-anydpi/ic_launcher_round.xml old mode 100755 new mode 100644 similarity index 74% rename from app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml rename to app/src/main/res/mipmap-anydpi/ic_launcher_round.xml index be316184e..1e48b4cc0 --- a/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml +++ b/app/src/main/res/mipmap-anydpi/ic_launcher_round.xml @@ -2,4 +2,5 @@ - \ No newline at end of file + + diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher.webp b/app/src/main/res/mipmap-hdpi/ic_launcher.webp deleted file mode 100755 index 0d0a90081d48c5c2092e35173f50bdea207e3ffb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3022 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 diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher.webp b/app/src/main/res/mipmap-xhdpi/ic_launcher.webp deleted file mode 100755 index 2c21550e5124e47c87e8fd274be90c64c5a82d13..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4288 zcmV;x5I^ryNk&Gv5C8yIMM6+kP&iDh5C8x#U%(d-35IRkMuP41KIiryIL{0b{ht7= z=>?fn774hbvFsmDHi3yt=i4p55%f~6{`akqMQm*3+N`?hjZo>GG_a}d!Sg` zwoJL7cid;v;H-p5B63&*kxbVGkS617L04eeJ-dGY|Gsc+TRZmL=Y8Mr`(hiFF#^sY zWhUa(>8(lFw$)mdZNGRNxQ(Ppu4U%EoBe@B`TyHCasaT%y;5SI9g&*MR|VdUqLt^3;19Pj)H%>>vZW)N`Z%N zkitksRH2mX1x-*nK1mxI4-t&p@%H|s38?GZkovWL8HAw2ciZw~fPzpl5<3F25c0r3 z_J)4&V&D@HI(QBNw*HSw8j4I+0D@4!5(v(LIP{0gWGz`-+}F+j$(X!PYySe2n? zgn%qd+n00tnOyfQcl!En>Jn!1*V(##Jy0*L_t48mQ99Jy*dlx17H8fPVS zw2iijCC%p9W*=<&W;_I4(qlZr0v$ez2GB(}o-l62VppzAuv5O#sm#5ko1t)<=nct$@tmiDK+^M0{_XoGDPk1iaa7oC*Kf(h5F zCX8?(Q3(*|fG}mk6|?}efY)IXSrQ5lcgzP%nKkx z=osSUu)b+?Ta>TJ^zB7z{Y2!;&XPu5GUd+WTI;rzmJ|RP7@CkTwZE*!JODDoBn_nE zr0H7!_o;7&ZPnbD96R#t0ic5D1;do!poN zO(#_-lPXyY(?q1$MP-z1#{k|CNzrZdh)J3OH@4_<*F4rZ%L3fV2_m9fzG-Ieh*MJT z!jFvg?L_=vOp$WXJv0EZ{H$#uXO39DM(~*(nt58XOQ6!sulFv?RoJXT0*E@`jKqo)R@UYY^&YwWHLQYMD?gRpS`Tp>9pWSyZ6a% zCE$#42$V)dv;}b1kD9yX@^{r&Qf@o4!U>0Rw1TYuR$N)Qwp+#eNsXPvPS)Ti zXBQiu7UlFb-=?*0xw~?Aw-R_Foj7U{IsNzZolFK-_jOG&7(j2{fMi0 z+l8;s`vpWq#3h*a1U-ETN5^L2M(O$%4%7zXu6-aJ8dTOQgRbYuTJ7uB|GjI!m2TgY zj)?V&VscQP(x3gH3!j~8z@UdQmqBR!IqA|>XFXdwr=|xd0e}*FjBG4#Gi&KVo(ac; zxaP+cNP77D>6ig5iC=S=`0U>=4K5`>y@pVY-xA2XsIzuyAbYVH+7^ZRvn6s#;wU z0YW#3ofAx};kd$TbbIk$W8*NO-(4V9H?;}4SvBb*O6hoN-tv&PrO4F=eTiwFt-jY6Nj}TFt1Y5WmMb1XvRt~z_ka$PT zOk*X`+CO)Qk8$so`R|tHi?_IFIKKC(_kMZxZJ5^8tmSLXFS5tL6#xJcgIS3sP!;rB zzCl*4ZEpSN4zoSKbb#GGS=)RoT@r5PidiD}1eWX;w@>8-7nm|{U(TxatvtBtak(uS z51@8k@C9V&CCOMYLJ?~#InV{>wwfb+sa9fI?K&0CN*&0{p(BafMBLIh4 zsvlkbYqGo9%gv0d^|+&51cFSGR+AfYNyW-SFU9ZR<7bvQYwpcVTb@_(DT!{ z23%a$Pm%rH=)J6^NVwOmQHpMhupc*_R)WyNP>d^F&(k(eFKOb^&f#uvbW+b-UA1&I z2ZSC6j46j1EIerb?zpO0U%GY@X6P34e+QEsqWWl%90Po7A&R-GJi21^inzl5I_Wj`|hQ<&5vLXNLwuI z%&e1E;z|t2YC|ixwzYmW9FVH6l^6+Setu;K=yn`qvUQSX_FBs?pV*K2_66K1BOQ~U zIT(DH{9%ixEf5-SqDDmDTz$LNetB^J4K-T~vv@F)oCHgk&x?|>o(I-8O{IdQ{l@|0 zSk?5TTn7<5kE)p`0thmt$KUIuNg*Xbg2DeX0KJi dt1Q)=)^A}NK4IMa9k^%B1) z6*3G*VH#7Dn49$WtkK8_SGIBqDiKjBrQ&0L1OcihoBN*J?&bY%3#-RQo<>q!Sh;J4 z>tmpkhkf;Y#ykcm_gyv9>+eP(PzGRzN_2!fIQlE|uCBAGdKy=GF^+X;!qCQQ3K4I_ zxMkCM$3L64bUk0aX~XejWXRJ2OO0DR)%`csOoqz5lb1On?l{MZhdLkpzqr-G))Qz2 z$=TfQB~|s!I$0nAG5O!IRl2+l5!d&HDPxqa4J2i$%<{;}g@VM$9Uw{-;?3SjNP7p3 z(v_@w5n<~lsRW~xF-!=wmL+|dg&Sc0yR^}IO{k1H`;VUHumT*b^bfkuu#7`!)G8)%rVW+{jPAOl>Oy+;};L!$cvXq9E^ZyL`2=9yk|Jn)kaETpKob?wx@|BfN}|A!o)xK4VmhoQK{EU zI}*Bwt_kdp2cvz3srsfsCybs_L_;1e=%;i?{g#-Q3u(=e-^0}kV_fyA{hLy>Zn(5C zcHhO+XydS`Q$9vnmZhh>SSf>s242rtYYSFb`fW0ch>Rbllu~%W6hA0ojk;8u9|Q$~ z{YE*H^3|Q`HD!{xAt}`EX>2>MPd*Q%#~9>FK>#B}CqOOCuIFMOw$bTS+Uyk5fn*di z&}Cw%j4={GPu|W&i{d89{gpATb96#E+Hy8?^~#shsnZL?rqh-#3CtwzOm*@}?>MRb zB+v6)TW%9j!(Q`<1~iBOE1_ z$R#0TZp9Fx=@wszr_?9oPHJXNE5*WH2T(hzw!ZjRyjn$j;i<>PoW7r4SFbGL zvVS)@0I9X=A)MWKoQi8vGd;xOl(0@Dl1VGj0Q+SWsd!kjU~iAH4;{emIvGeHqED?i zI!Z+<(u#zNaK{D;?8G?2#nIAcy#Gb_KJ0q>FK@e62j0k!^Fga&Hm7XWE3PFSvVMh2 zg)Z&H9i<4bJaj)@EtZV}k%u3@OVP?a6sJmBa3?GFB6GUp z;ZWJ`^3kW!$*$f=9M?(~fG}FOOKixO<;%(asA-{l(zMo4PTlk49CBNABX59rz@d`| zWY?KVna>RG+i6Zt^v}Povh*$RbGW@c=Y-yaLILqry28p7SjaRWgqKVrKmmRwqakIL iJOu)$kbyA~BH$hFOjJ?^$L8O=obVg3Zs*8Fr3e6m!bKkd diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp deleted file mode 100755 index c562df8e049c3ce5eb2518130120de03dae5c974..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7418 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 diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp b/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp deleted file mode 100755 index 2e54b1240f1e2c3887fb6b07503233f621ebbd57..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6830 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 diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp deleted file mode 100755 index ddc305de76b7c16abed7d86ef8cdfe6079ccc24f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 11984 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 diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp deleted file mode 100755 index f6cb4e6d5b97b3ca5d86e07790b606e3c90ca30b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9628 zcmV;NC1cuBNk&GLB>(_dMM6+kP&iD7B>(^~zrZgLO*l*-H*7dbC^E187q*dIx!4WX6RyHDttWxj+J+($FZFtGNSMs-^#uhBgGC3^kXz%>S8-HDl}o8dt{H$vRdR`vp_Yri?HK ztRu0S+FE0*#tLA{SZQmFwyXgd8H}}>1mN@8h6-S)B$6S0HFN^-qGuken%n`;PTz%G z01RQ7>LL+i0PcoP-vNLnNg$pL>HB|HF;(AEUx_YV1i)3TYZ5?`5cr&W?^91miLNHJ z)o)b3%$| zd)|c8p!}bUqDxHxkrj3^kpm4PvOhM@_ zMA}efFam{wyS|VQD(itSd-ig=^8YKAlz-2<`{uYj#XKMHK;-ZOF7phyyEC$GbEi8P z>2rQP-8lTvH|T1jegT}RQ+H*ugWF_S=Aqq)YN#UOZrZgeuAQx8j&@Qd{sBO9#?A zu(hI>9c||9nMhCUh>94oTBh_ld0?5v(BthEE;D0A#$Q-wns7(EV`7OJ+ZX9AXfunY zbVc~GZQE^Y+qP^2|6B9DK0^tD7UDFg;JF#aWx)HuC_ zI@!$tq2-j5EmR9V6#fMzyOT-Ldi?hy_hC&_v-=(K6&Otc(c8k$`Ib+Go@~>XoJrCn zcwr6p`1arQ|24R?Ti*Ul(%ZeG@zcI#JAzNmeCv}iS^nR!H0AjxeBDX(w${7tl5hO= zcNXMJiNEBNrwM~vs!gy=AK&@0cXr3See6&AhW!Y(p`l>n)KoN$1=#x0UvYsS^<{Z? zKtWvKZZ+pi{zUT7Tu@k}$yxN>ee_#PYgc^Z+r6WCErLzFVERQ3+=_zoZ~Bp+6?enC z-TCp~FdoSSB?=`Eq6Uaz_(!bG-g)ECNW0-Dy7*{uGj);px&No`ScJ0~(vUcs-mfGc(7XdiBo=}>mX#z>p)VJj5^j!*Ej>tfsf`o&6Ss+16FJZXfG*K9$xQc5 z_bJ^+x`pm^x)r+gJb+1~6d|=CjrCdB(b)3tQA*@dfItL|1Aq$+6cH$#QrEms=jpVc zPUlQ#MprJWim-WP=Jl55n&y~#Pu$>1>3A9tfD%9sL54yxA`%hS<#bEhk7x6e&gFC! zx@4Halo6>VnmtQ97FxcYbi}&>k?(^X2wN*sK(vR&NSK-M*5`CDxJyDNP$C&SE4!@h ziF2G*+$~BxG$4>d;4h{Rz|8=NM4~KYtc>D&3fFpO?M!>dT>`M83F-jOERGzz<<(kD zHM!;eKpT`}OQ(1M!UaoQP-HA3g|e7=6trem&uP!c?m90UVh05OizJLuFKp)T+!1pZyBme;@fC7-(I<=hkW0FgD+aMiT7GJP%6@OSxtQ%MYfV(nO z41x#@1icmxM3j*S5j-<{6ar|Dsr5`cM-DGbH5eyA_zQl(;{HU=EkAW-Mg&0dAV?u1 zCJbeyZHBlDs!&acl#U;G@&mE~02B^9Gm5?MiSu+$v3_d^(-X*CfG~&PE$5AK>g(`y zLf+;C;I5#M!i>U5Sqya0EhK?1c#U9oBTBh>b01a(jwZew_<7&}XbcAwft6(zdlvSI zQ+P`VW*l^*W)`+QL*5NMVW=&*D=18uO^Q+ZKAnm7Gp#ep<$R6~bi@(F?C;0MyDu~} z`g~`tJt~I|9Zlym5%K{L)|PRY@%Plnd=W0cpFw!<{WFb$d8vZQl!y`=8^sY(mbfx9 z$}>+Qote!?HlFFuBnuYX9Y*ccIu^#$yrI6K)>5ee62J(sfyW5KPKjD~-raidF-Bov zYYkrn@4ZE@NHB#_KIks2AF^>c?I|VFmTNC;zLn!&0o*x~=!JT0|1VinU!^#j`ldmN@2~g5(pqIT&X;8{(@RK3Ii!O z06+->AQFNJ`@^-!ndy8vcI*8k8=46ZZ{Y!;>;oc6f$$FON(X>GU|FI0~ zu>Bgdkg0h>7qq8z7E%BfTaK>tMeLZs0D%Cf>(K?ejL1N+DFBvRPV57Pkjn`W4ico? zxX;;{Ux{tYk$jGKiE>Cgb)??subz3xYg53@sGG)kl)=+HX8?JEI0s$xVvX5S&lf0& z&5jehBe+NwW$Dh@ct}#v2)4hJIk7@{p1!4oPN~(id^53e?je){v||2nt62f*(rSNs71oO3>@4@u_iOoqZzhiUBD2Q&NuO{qVr4JgO&fIpmivWRee8NA&I zd*?~0zz@9lkSqA)|Gs%*Ck$az9V5PsUB2DW@2~ zm($tNkEL@wohRC-be5x;w20zFW6h!SK2nI8BZ2ygbB|1RfE7%s;p!Xb6TJ5f;i(P| z>pR}jd!NG9g@NS_ivzJU^V6!FItMQaiIHh@ca+B*CJXB`zou1DA{A?GZIgHvH=S6v zU~iv{!N$x^7?_+{uIebyLoJ*aVCL{V)rM*8!pyv_>;CSM?P;_qkDGX34*YbGdvIP( zzj$96^O?E6F=h2cYe9(=n0dZ<&FF9n%~2eMVGi>nW7dxNw6dHT%tVm(b4L@F0cIEz zIXBMzHS7O_aIw@oZ1y=a(ik&w-YXqD@KYMV0}zOri?U3o`wJVE-o<|V7+tfy-e3Fh zEpa9ROh70g^^}cEJ}50R9f9#keKFC1`uHt*iZ8}n%8M;ruC4{L@HS7Ik`)M1H~`^;d$7+P=yd7cYuN477d2_ zFHi;Lq_%$~ohk!z0)j${)4xu44lbUEOe39$xJl`}B?2;dT(tNJc}8PHW6as#r#q3# z=#d@ujvxjU0>Z!AI7iov{=%^}E2{vBXY1<_1#8exFJvcGxA z`M-y^!JJ_JEOKk784&?}SbGr;z?ePfmKy`h@6PGpMpx3nVtY>B2Dva$Fn{~+>nvGn z^EHijYAM=~8nQf+AZK4j2N%bTs6Msj=Ime7794o55XDHX&zaxN2VtAl_00(e+5QzG zK>Dx%UBCfAV737B(z`#Oy!!W~B9H7&aE_g`9z5!l9NVGVIFBeIa%4vr**In!28PnA zk)@PLmd(Jkfbh@P{sV1ivUW;00!3gk0Wj(p0GtOA00#k~m%&mT|M*$^cRo7}EVVod zLI6d8tLJJtulCN5b>fz?j8{h1j@d>9llp?uAkLD&dhnCU3}L(NDXWLf%s8NTWP%uY za!v(=3?jGy0=WOo%KKUUYf_oj_fMr6W>?y%g79HgR@uJNm?{!4y0#76gS7s^sG^FQ zbL=;+(tdh0XIp0;(q1NF%#cxGD6`^w=8`P5pYqRVZhGsA2{IH=7GMK2%R!{G@`0@V zFS;fTC{JxQ44*>@?ZY`ws+>8B&s6#xE-o`LsUovTh$^aRC}UX_I$-GfDvb`UO#Q?H zknlYb=gPUlFa6PZ;9=E0l-cP^9yyg!TYsX@V%v`>c^N-0x71R-6(5`VwTG1dmRfUT8g&h?%ko zfFMS5&i*y6QzAooRNy`>!9jl<*;wlq*QZn>jsITXpfcW$eEh)q%A1iXL%3rs81D!bh@xvndC}ij6gF0ltsSo%x~yCCuX#`u7W86 z`>>ORo^dJ^A@zZr$O3&3-~=Hk%fZ?`w2g|WA}GpvJefooZOr%QleLl@;e*E>aX;5S zK~s5+vwzG`J6bs`37AiYq0unmG+4(h>>$rM00K0C3?s!9|Gs^K zw~@horYLb*$Pq_*;=oqJJF(JL{!x{Jg;m1#$wGTj&A+{>oXaT70FluQe&e3CyDYya zs-gqulp>hTM=y$;?vgb8e{bybw+I#>I6yENja0DuCLe@tj>4#>GvbX?u0}o9_nx+7fBoShwH)T_Q{?Dn&0%gG z^T_9?)`e7%338)2zyYVhtSk%Hcu$@HK=8MukkvQ5>$WnO&s1Se`ME)Zxr(YCNeCg$ z&N-qO02XCst=HlM-*DjA$MVTj9ho|pd+R~oCnaKVksfJ!kLEQ{LGH^Q45B<{zIn{% zDRw)if{ErL@^H?BgeUQ2+5%uEiX)DkF3hx!($PT)5Hx~a+7;bOqs_}0y*3mmB4%}F zic7Y^hQWK$L#t@#bGL$*0*nD^oN@wq=Obb5?s&Qlltn}U1V%s{r*%0SHQJwTGz<-f z_=5O`j7Iu`QRHKm*C#%XD$aQtm>mI*h>*?b(7dRa?h5=qi6X+BD@@MUiRb6$9Af)2 zi$BCi#7_XCTQ`n6s^X{u90LLZ0D^<3(w_P>?Q=HnvU!&_qk~=04x&H+Q4E3*7oEb! zd~gY;jYbP73H6+n6$@|9fTt2Luc9u^8%}-- zUD5(;3u{3)nC#TM?g3!UfRzHoP0n7Tm2C~1Q5@xq>%8I3pGGsz4h#2~I4ve=0HXzu z;AO~V&QJ`tbU>d-i)ocq=Ck=cDg6J{<+OyV>`X#*MQ2{7n0WxOC*QnwIk=QXk*TRS zdDHitZ%a6CBJ)@o{DL~`A`R}{I~Y$(Kcz&3t$wtk%lF= zxhu!z?b~$^(vQ2G`Zz~!nI)LgTNT(CdDU#Bc(CQrl>j);7{x12eTLI1mMH>!wnnn(e#_Nl5d-J@4Uo_jmg`%8X! ze(BFiE}4lWHXhyxgiLS&j38Q5`WxStlz6WO;lmc1)ExNMC7tQ`v_@f+t~A_d9!C)X z(9T#Jv2h=}@I5+bFp?o)&1fz_84MU;Vef5y&F0hO7}RAzdQdPsg?|c#L;?4X?*BW1h` zEW`v0U_R2y6W{ssoku@C^=(2w&vHWM3vi+9Y#zo&f(9nV`Kc{e*arXz@OGj_a}#Vk zvK&kEewFR_oNDs5r!ozv2Plw55rR~a65+I;*?7s?%N+Rt&qX#f5m-Ruz`0tV*aPJO zpdG*^o{T~)4YSzN#Xw;I?*l*+hvqIJ4>LPX8a=+8paM=hCo`v zVxtu>g7@BAvi7z074;q)hWk?;T+zakNMa<8HqIhvYqX~CxhY5-`;a`r* z38Wzt9#_>+<3>NMkn3C?R8cr!GQpD$v3Kph1-zAaag>JYO*N7U)AD(kf{ zBI?)Fz&7eROI;?JweTK5@Ycdl-PeC~Mxeelq2sa5!WZ)W)_eje>ACNDw^Z-qTl5Y4&-irmr2rT49nO72_4(iki>!t zhyn-!Lt`Q&6hT=&X9H(D=Ajp$s*}tFea~}R2wU((49B@@RD)Q(r zQtNW`11xNSk>L=?FqGl2HDRH!eVi|dEQ*7t0Di!cPB*}%Et;SXLWH^Y(Kb;K5QGCr z8z2V+eo@QV)0Qp!;qo{u1J`}Re6A&A#e5Q6j`o@VA{vxhff>z$3StM|XVNDk2Of`< zW&JUGDQNw4PE%|1;K2hvh;$T);+PNGsAzjq@>2W(651wdiixr(!pt8GwGX zz~O-=k)?)d3vBcLi9MNCt`H~$6d?e}HBfW8xsPaTtcs#ylq8kW+;)#+VD!Kw3JqWq-8Pv)EF4 zQaTm{u{31KCs~FZux2O#IR^k#7WYl8oV_E7q|trmk3R(_nBmQ*K5eb$gVL&C$`Gym zobx$}#F)gR>d`)7h_hhLvgv6e^Lb`1ppD3gEgeTYz5P|+v$6r!TAKqID-tpQ$N^XY z5CQ-J#LXiTV>afj9;i|XwN)ZV@zMMV4<2Yt(D_GMbY*Qp`;(S|$wO$)qsSYQV}dL~ z`FP41B(Ygj9>OyZ{D3(yH#IFC?(L7cW@%<@4n&&F24?0S02#JcILtYRND0`FISKRV ze@HM$ajB&~gr8D;6o#1}n7FbTv@o1jh9dtV8-zfI`GsQ1pC$1CZVlNWTHqX#fpQ|| zF^W2p2qL2Xi0F^R9sn*If^oqN7wm%+5i>C|j2U|L-`PBa6!+g~&kfC=JmMi@Hbq!n z&hPofPfS&UoBkRaI5O{7F%U1C*tZA|OY0 zfhbTs5E;!Uv)ODU-IB*%X7!OOjbodf>i-LPz(CM^Oe}u&dgS@k3``noz*C{=VITm3 zeX+16Swckp_4(4=4Q4`QNgQYGJrz`Bi3k9OqU?wiCfrn(!py^ZJ)PgM`ae<*oO;(! zp9Bn*0st9>xjA>F8==%H7!h=srp=H!iU0s}eU<-xz#M{K-f|*m$(UP0hFB;7zi(tX14ctJV zJm{{Ozhbu6ozCxg`DQ(v;tEnFa(`6S$PjP@guv~E)#HbDpn}m$ZSpq)hGEQ_gVNW# zG`H*p3o5qMOPag9;TQ9b*{~D|v+NiB;A|WYVJF*0m&b@80}5vI$|vqm65ulUjM1Sz zEu_YoDds^Lpma!S4yFrh-nrvXld95@*OpmK#pc{&YYQ3=uhq` zo$&lH!W82PI1TRo-*E;6fSGgBKn1Qb&}hS~CBxaQZFmYiA`(wR9A$tbAREsc5xnUZ&K&VkrF#2w1wd-Zs;_yYv=MI(m`(%^Pk#yYQkEOKT!SGIu- zn@20>eWCzWiBu=SyxroTCBT)piEu#|8wT&t))J9}Y%Av+-JZ{0(&juqf6gN1#Q|P3 zNcX|n4Kufkw{@TbN7zze3`lS}Eg`&DmM;bn;EBk4XNMd>742M@YY+T99fR8 zIe9(xHa34nxyEpj8J1ts98>eB>T(Wt zrtU2DId=UlwrGnE5~j4)GBXDj2~nh5q9aVm3}6n09Y6d(w2GM zSOkCYAJ>9SoQ}~Y?&8ALfAANVJxe+ZI4b2z>0RkF3qXwY{@TtXI&jWGJY2ybcb9BD zlEf5~5JE5kz2up-1*B|#{}+^P@=yM7OSJ5CgTx>kisAhR_>u+ks~S8tOXaLBGw(A? zfI!%od8B-H>qLpmZVtg)ck8S_q&sC&M4W75LQpzA0TomAJh2Ri!H6c;KS&3ot5^5K z-Ysot3;BDt@|2MU08@<2#JJ5vTIcYV7#U0n;V(E{Kg{Zz;YkkznuIE=Dp*v9Ort0; zLH4yXg!TdGUKJIRJbe{Z6=igYLm-M1M2xb;VDfQm_k4rf8pFV#cP=^mZ#Mr=%*-Pj z0E`IX0N{C4L=q_ik$u4wz=!gh_x>!V3d{$cO zO=8Qf#3u7Zo*euhwlqK?(jUcv?3-I1vBp^~tow8YE=_L%CF1}0g^OL5`)M}$-?4$g zbdSjIp%0Kk#9$^e9I48?c9yJR*30?xUxUm3P^17TCblf(ys){N=8lD)e->FL6C(h? zDsWE4ah*zQ&gR7CoDXal94=+a0Mm%t)}h`}Z+Law=0>eakB*ZpBulz8-NIk%0$lnA z=Cc1lkytzf%w_*&Vlgm@Uo_s@wKkz-l1A)6O!n1TTOEB{@x&8PB#$R@KZRcj-F1qb z)P6X^CHpU3@C)62_T!UbA{}0x`P$y^KC*r1xdRBgk6plNTn@avM30IiI(xk~FO;-p@Yc7O;U!A)5CJ=GfxDn5+S*t0$l=(LI zP~s5fLECIe!iBZ1ar*xoBcSEEzgA)Q(0g5}{bwa9q$V>7W8-&Ag?4qcpDfz*ejf#` zKH7`6pT!+E_a=m+&ET>^P%@F&Qe(-Y;YsvsvWwvCwGVCDu0JG~Y(EW_h_?Oj{GYN; z>{;14Q%!fInEQ>t_~O1Bk19D9CK zyZhsqqex6mKr@Zl^8y+cON5JpbM;((+e8l~FK??m0y#3RwU(;wfV9iQ(>qIThe?hCbO1`Z~eO}JZdFkzg+pGzHz5l1UTHDxIy>P^E zMNzn>{@Rv&ZKpMj^u;?Gm46@`f5Z6e^L4_Q50GSv_Lk4b%aa#>1@E3)oSlP}%Wqc{ zg##g-b5u}y>4N^s%dWU{-fP{t!}zy$+9Ix4`M0L^-=FB5BdT*GU-O#RC@8Ki=&xw# zrF#0JILXDF6%Sq;6}sERHLd>(LHNIE6G`V#dfR{cPygvZRggdW+8xKZxAt2h3dP9C zh$XahY&Na6B;&8QbFKg**|g0jt?lJv%=U7QMd7SPW7FCuE$JBV*3q$W&Jo^{mew)} SI!DJyT3TKTw2AL(umJ$z))h7Y diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp deleted file mode 100755 index 15ffa64b36963e83ece8cf5db7b7d354d0efe191..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16806 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≪}dqXByacJeJDuwlKANjP7Qxhj%cg6}XH8}^0L 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 Z9Do4^g8>MZcLgH_B?SPZBx59_>H>?W5-I=y From 0a14e3645ea76fbe3713aed915408623e89d4f49 Mon Sep 17 00:00:00 2001 From: Sylthionys <94494882+Sylthionys@users.noreply.github.com> Date: Tue, 16 Jun 2026 01:38:55 +0800 Subject: [PATCH 16/32] :recycle: (resources): replace dynamic resource lookups --- .../net/ankio/auto/ui/utils/DisplayUtils.kt | 41 ++++++++++++++---- .../net/ankio/auto/ui/utils/PaletteManager.kt | 42 ++++++++++--------- 2 files changed, 57 insertions(+), 26 deletions(-) diff --git a/app/src/main/java/net/ankio/auto/ui/utils/DisplayUtils.kt b/app/src/main/java/net/ankio/auto/ui/utils/DisplayUtils.kt index 5074e2be6..ee1fc14b1 100644 --- a/app/src/main/java/net/ankio/auto/ui/utils/DisplayUtils.kt +++ b/app/src/main/java/net/ankio/auto/ui/utils/DisplayUtils.kt @@ -15,12 +15,14 @@ package net.ankio.auto.ui.utils +import android.annotation.SuppressLint import android.content.Context import android.content.res.Configuration import android.graphics.Point import android.os.Build import android.util.TypedValue import android.view.Display +import android.view.WindowInsets import android.view.WindowManager import net.ankio.auto.autoApp @@ -51,9 +53,14 @@ object DisplayUtils { if (navHeight < 0) { synchronized(this) { if (navHeight < 0) { // 双重检查锁定 - val res = context.resources - val navId = res.getIdentifier("navigation_bar_height", "dimen", "android") - navHeight = if (navId > 0) res.getDimensionPixelSize(navId) else 0 + navHeight = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { + val windowManager = + context.getSystemService(Context.WINDOW_SERVICE) as WindowManager + windowManager.currentWindowMetrics.windowInsets + .getInsetsIgnoringVisibility(WindowInsets.Type.navigationBars()).bottom + } else { + getApi29NavigationBarHeight(context) + } } } } @@ -69,15 +76,35 @@ object DisplayUtils { if (statusHeight < 0) { synchronized(this) { if (statusHeight < 0) { // 双重检查锁定 - val res = context.resources - val statusId = res.getIdentifier("status_bar_height", "dimen", "android") - statusHeight = if (statusId > 0) res.getDimensionPixelSize(statusId) else 0 + statusHeight = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { + val windowManager = + context.getSystemService(Context.WINDOW_SERVICE) as WindowManager + windowManager.currentWindowMetrics.windowInsets + .getInsetsIgnoringVisibility(WindowInsets.Type.statusBars()).top + } else { + getApi29StatusBarHeight(context) + } } } } return statusHeight } + // API 29 has no supported Context-only WindowMetrics inset API. + @SuppressLint("DiscouragedApi", "InternalInsetResource") + private fun getApi29NavigationBarHeight(context: Context): Int { + val resourceId = + context.resources.getIdentifier("navigation_bar_height", "dimen", "android") + return if (resourceId > 0) context.resources.getDimensionPixelSize(resourceId) else 0 + } + + // API 29 has no supported Context-only WindowMetrics inset API. + @SuppressLint("DiscouragedApi", "InternalInsetResource") + private fun getApi29StatusBarHeight(context: Context): Int { + val resourceId = context.resources.getIdentifier("status_bar_height", "dimen", "android") + return if (resourceId > 0) context.resources.getDimensionPixelSize(resourceId) else 0 + } + /** * 判断当前窗口是否为横屏模式 * 注意:这里判断的是窗口方向,而不是设备方向 @@ -154,4 +181,4 @@ object DisplayUtils { return screenSize } -} \ No newline at end of file +} diff --git a/app/src/main/java/net/ankio/auto/ui/utils/PaletteManager.kt b/app/src/main/java/net/ankio/auto/ui/utils/PaletteManager.kt index 688eb3fe3..0b30542fc 100644 --- a/app/src/main/java/net/ankio/auto/ui/utils/PaletteManager.kt +++ b/app/src/main/java/net/ankio/auto/ui/utils/PaletteManager.kt @@ -7,6 +7,7 @@ import androidx.annotation.ColorInt import androidx.annotation.IntRange import androidx.core.content.ContextCompat import androidx.core.graphics.ColorUtils +import net.ankio.auto.R import kotlin.math.abs /** @@ -49,6 +50,26 @@ object PaletteManager { // 基于 label 的颜色缓存:key=label.hashCode(),value=Duo private val cacheLabelColors = mutableMapOf() + private val backgroundColorResources = intArrayOf( + R.color.palette_01_bg, R.color.palette_02_bg, R.color.palette_03_bg, + R.color.palette_04_bg, R.color.palette_05_bg, R.color.palette_06_bg, + R.color.palette_07_bg, R.color.palette_08_bg, R.color.palette_09_bg, + R.color.palette_10_bg, R.color.palette_11_bg, R.color.palette_12_bg, + R.color.palette_13_bg, R.color.palette_14_bg, R.color.palette_15_bg, + R.color.palette_16_bg, R.color.palette_17_bg, R.color.palette_18_bg, + R.color.palette_19_bg, R.color.palette_20_bg, R.color.palette_21_bg, + R.color.palette_22_bg, R.color.palette_23_bg, R.color.palette_24_bg, + R.color.palette_25_bg, R.color.palette_26_bg, R.color.palette_27_bg, + R.color.palette_28_bg, R.color.palette_29_bg, R.color.palette_30_bg, + R.color.palette_31_bg, R.color.palette_32_bg, R.color.palette_33_bg, + R.color.palette_34_bg, R.color.palette_35_bg, R.color.palette_36_bg, + R.color.palette_37_bg, R.color.palette_38_bg, R.color.palette_39_bg, + R.color.palette_40_bg, R.color.palette_41_bg, R.color.palette_42_bg, + R.color.palette_43_bg, R.color.palette_44_bg, R.color.palette_45_bg, + R.color.palette_46_bg, R.color.palette_47_bg, R.color.palette_48_bg, + R.color.palette_49_bg, R.color.palette_50_bg + ) + /** * 基于 label 获取颜色 * 相同的 label 总是返回相同的颜色 @@ -198,22 +219,12 @@ object PaletteManager { * 命名规则:palette_XX_bg */ fun getBgColorResId( + @Suppress("UNUSED_PARAMETER") context: Context, @IntRange(from = 1, to = TOTAL_FAMILIES.toLong()) index: Int ): Int { val normalized = normalizeIndex(index) - val name = buildBgName(normalized) - val resId = context.resources.getIdentifier(name, "color", context.packageName) - // 理论上应当存在;若异常缺失,回退到 palette_01(兜底不崩溃) - if (resId == 0) { - val fallback = context.resources.getIdentifier( - buildBgName(1), - "color", - context.packageName - ) - return if (fallback != 0) fallback else android.R.color.transparent - } - return resId + return backgroundColorResources.getOrElse(normalized - 1) { R.color.palette_01_bg } } /** @@ -236,11 +247,6 @@ object PaletteManager { } } - private fun buildBgName(index: Int): String { - val idx = String.format("%02d", index) - return "palette_${idx}_bg" - } - // 颜色计算参数 - 基于可访问性标准 const val BRIGHTNESS_THRESHOLD = 0.4f const val DARK_FACTOR = 0.6f @@ -280,5 +286,3 @@ object PaletteManager { ) } } - - From de45e7e4ed95f8f590d1932904037abc7d38f8a4 Mon Sep 17 00:00:00 2001 From: Sylthionys <94494882+Sylthionys@users.noreply.github.com> Date: Tue, 16 Jun 2026 01:41:46 +0800 Subject: [PATCH 17/32] :bookmark: (resources): retain reflected view bindings --- app/src/main/res/raw/keep.xml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 app/src/main/res/raw/keep.xml diff --git a/app/src/main/res/raw/keep.xml b/app/src/main/res/raw/keep.xml new file mode 100644 index 000000000..fd05d87f8 --- /dev/null +++ b/app/src/main/res/raw/keep.xml @@ -0,0 +1,4 @@ + + + From ea606c0e520571578cae037ccc63d05ac3c12f58 Mon Sep 17 00:00:00 2001 From: Sylthionys <94494882+Sylthionys@users.noreply.github.com> Date: Tue, 16 Jun 2026 01:49:53 +0800 Subject: [PATCH 18/32] :bookmark: (resources): retain generated binding layouts --- app/src/main/res/raw/keep.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/res/raw/keep.xml b/app/src/main/res/raw/keep.xml index fd05d87f8..6608cf9a1 100644 --- a/app/src/main/res/raw/keep.xml +++ b/app/src/main/res/raw/keep.xml @@ -1,4 +1,4 @@ - + + tools:keep="@layout/about_dialog,@layout/adapter_asset_group_header,@layout/adapter_assets,@layout/adapter_backup,@layout/adapter_data_rule,@layout/adapter_order,@layout/adapter_replace_preview,@layout/adapter_rule,@layout/component_action_buttons,@layout/component_amount_display,@layout/component_asset,@layout/component_basic_info,@layout/component_bill_tag,@layout/component_book_header,@layout/custom_date_time_picker,@layout/dialog_app,@layout/dialog_assets_map,@layout/dialog_bill_category,@layout/dialog_bill_more,@layout/dialog_bill_select,@layout/dialog_bottom_sheet,@layout/dialog_category_select,@layout/dialog_color_picker,@layout/dialog_data_editor,@layout/dialog_map,@layout/dialog_tag_select,@layout/dialog_update,@layout/float_editor_refactored,@layout/fragment_category_rule,@layout/fragment_category_rule_page,@layout/fragment_intro_page_ai,@layout/fragment_intro_page_app,@layout/fragment_intro_page_feature,@layout/fragment_intro_page_home,@layout/fragment_intro_page_keep,@layout/fragment_intro_page_mode,@layout/fragment_intro_page_permission,@layout/fragment_intro_page_success,@layout/fragment_intro_page_sync,@layout/fragment_plugin_rule_list,@layout/fragment_rule_data_page,@layout/fragment_rule_edit,@layout/fragment_setting_detail,@layout/setting_item_card,@layout/setting_item_color,@layout/setting_item_switch,@layout/setting_item_text,@layout/setting_item_title" /> From 46c44a512b8b79661083c9aa9d04a076f58e26f5 Mon Sep 17 00:00:00 2001 From: Sylthionys <94494882+Sylthionys@users.noreply.github.com> Date: Tue, 16 Jun 2026 01:53:18 +0800 Subject: [PATCH 19/32] :fire: (resources): remove verified unused resources --- app/src/main/res/anim/fade_in.xml | 6 - app/src/main/res/anim/fade_out.xml | 6 - .../main/res/drawable/circle_background.xml | 5 - app/src/main/res/drawable/data_sms.xml | 10 -- .../main/res/drawable/home_app_category.xml | 10 -- app/src/main/res/drawable/home_msg_qq.xml | 26 ---- .../main/res/drawable/home_msg_telegram.xml | 28 ----- app/src/main/res/drawable/ic_arrow_right.xml | 10 -- app/src/main/res/drawable/ic_backup.xml | 10 -- .../main/res/drawable/ic_calendar_month.xml | 10 -- .../main/res/drawable/ic_calendar_month2.xml | 28 ----- .../res/drawable/ic_calendar_view_year.xml | 10 -- .../main/res/drawable/ic_calendar_week.xml | 10 -- .../main/res/drawable/ic_category_stats.xml | 25 ---- app/src/main/res/drawable/ic_chat_bubble.xml | 11 -- app/src/main/res/drawable/ic_close_themed.xml | 10 -- app/src/main/res/drawable/ic_cloud.xml | 25 ---- app/src/main/res/drawable/ic_info_outline.xml | 17 --- app/src/main/res/drawable/ic_payments.xml | 10 -- app/src/main/res/drawable/ic_science.xml | 10 -- app/src/main/res/drawable/ic_screenshot.xml | 10 -- app/src/main/res/drawable/ic_storage.xml | 10 -- app/src/main/res/drawable/ic_summary.xml | 25 ---- app/src/main/res/drawable/ic_switch.xml | 10 -- app/src/main/res/drawable/ic_theme.xml | 25 ---- app/src/main/res/drawable/ic_tip.xml | 10 -- app/src/main/res/drawable/ic_trend.xml | 25 ---- app/src/main/res/drawable/ic_usage.xml | 10 -- app/src/main/res/drawable/icon_eye.xml | 10 -- app/src/main/res/drawable/icon_map.xml | 10 -- app/src/main/res/drawable/icon_question.xml | 10 -- app/src/main/res/drawable/img_geekbar.xml | 19 --- .../main/res/drawable/menu_item_notice.xml | 10 -- .../navigation_rail_item_background.xml | 15 --- app/src/main/res/drawable/rounded_border_.xml | 4 - .../res/drawable/rounded_border_dashed.xml | 10 -- .../res/drawable/setting2_icon_translate.xml | 10 -- .../setting2_icon_webdav_download.xml | 10 -- .../drawable/setting2_icon_webdav_upload.xml | 10 -- .../main/res/drawable/setting_duplicated.xml | 10 -- .../main/res/drawable/setting_icon_color.xml | 10 -- .../main/res/drawable/setting_icon_map.xml | 10 -- .../main/res/drawable/setting_icon_parent.xml | 10 -- .../main/res/drawable/setting_icon_remark.xml | 10 -- app/src/main/res/drawable/shape_circle.xml | 5 - app/src/main/res/menu/asset_menu.xml | 23 ---- app/src/main/res/values-night/colors.xml | 21 +--- app/src/main/res/values-night/themes.xml | 4 - app/src/main/res/values-zh/strings.xml | 111 +---------------- app/src/main/res/values/arrays.xml | 10 -- app/src/main/res/values/colors.xml | 85 +------------ app/src/main/res/values/dimens.xml | 9 -- app/src/main/res/values/integers.xml | 1 - app/src/main/res/values/strings.xml | 114 +----------------- app/src/main/res/values/themes.xml | 6 - app/src/main/res/values/themes_overlay.xml | 1 - 56 files changed, 11 insertions(+), 949 deletions(-) delete mode 100644 app/src/main/res/anim/fade_in.xml delete mode 100644 app/src/main/res/anim/fade_out.xml delete mode 100644 app/src/main/res/drawable/circle_background.xml delete mode 100755 app/src/main/res/drawable/data_sms.xml delete mode 100644 app/src/main/res/drawable/home_app_category.xml delete mode 100755 app/src/main/res/drawable/home_msg_qq.xml delete mode 100755 app/src/main/res/drawable/home_msg_telegram.xml delete mode 100644 app/src/main/res/drawable/ic_arrow_right.xml delete mode 100644 app/src/main/res/drawable/ic_backup.xml delete mode 100644 app/src/main/res/drawable/ic_calendar_month.xml delete mode 100644 app/src/main/res/drawable/ic_calendar_month2.xml delete mode 100644 app/src/main/res/drawable/ic_calendar_view_year.xml delete mode 100644 app/src/main/res/drawable/ic_calendar_week.xml delete mode 100644 app/src/main/res/drawable/ic_category_stats.xml delete mode 100644 app/src/main/res/drawable/ic_chat_bubble.xml delete mode 100644 app/src/main/res/drawable/ic_close_themed.xml delete mode 100644 app/src/main/res/drawable/ic_cloud.xml delete mode 100644 app/src/main/res/drawable/ic_info_outline.xml delete mode 100644 app/src/main/res/drawable/ic_payments.xml delete mode 100644 app/src/main/res/drawable/ic_science.xml delete mode 100644 app/src/main/res/drawable/ic_screenshot.xml delete mode 100644 app/src/main/res/drawable/ic_storage.xml delete mode 100644 app/src/main/res/drawable/ic_summary.xml delete mode 100644 app/src/main/res/drawable/ic_switch.xml delete mode 100755 app/src/main/res/drawable/ic_theme.xml delete mode 100644 app/src/main/res/drawable/ic_tip.xml delete mode 100644 app/src/main/res/drawable/ic_trend.xml delete mode 100644 app/src/main/res/drawable/ic_usage.xml delete mode 100644 app/src/main/res/drawable/icon_eye.xml delete mode 100644 app/src/main/res/drawable/icon_map.xml delete mode 100644 app/src/main/res/drawable/icon_question.xml delete mode 100644 app/src/main/res/drawable/img_geekbar.xml delete mode 100644 app/src/main/res/drawable/menu_item_notice.xml delete mode 100644 app/src/main/res/drawable/navigation_rail_item_background.xml delete mode 100644 app/src/main/res/drawable/rounded_border_.xml delete mode 100644 app/src/main/res/drawable/rounded_border_dashed.xml delete mode 100644 app/src/main/res/drawable/setting2_icon_translate.xml delete mode 100644 app/src/main/res/drawable/setting2_icon_webdav_download.xml delete mode 100644 app/src/main/res/drawable/setting2_icon_webdav_upload.xml delete mode 100644 app/src/main/res/drawable/setting_duplicated.xml delete mode 100644 app/src/main/res/drawable/setting_icon_color.xml delete mode 100644 app/src/main/res/drawable/setting_icon_map.xml delete mode 100644 app/src/main/res/drawable/setting_icon_parent.xml delete mode 100644 app/src/main/res/drawable/setting_icon_remark.xml delete mode 100644 app/src/main/res/drawable/shape_circle.xml delete mode 100644 app/src/main/res/menu/asset_menu.xml diff --git a/app/src/main/res/anim/fade_in.xml b/app/src/main/res/anim/fade_in.xml deleted file mode 100644 index 0fafaa90c..000000000 --- a/app/src/main/res/anim/fade_in.xml +++ /dev/null @@ -1,6 +0,0 @@ - - diff --git a/app/src/main/res/anim/fade_out.xml b/app/src/main/res/anim/fade_out.xml deleted file mode 100644 index 2aa0a50cc..000000000 --- a/app/src/main/res/anim/fade_out.xml +++ /dev/null @@ -1,6 +0,0 @@ - - diff --git a/app/src/main/res/drawable/circle_background.xml b/app/src/main/res/drawable/circle_background.xml deleted file mode 100644 index a6f3dfaa6..000000000 --- a/app/src/main/res/drawable/circle_background.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - \ No newline at end of file diff --git a/app/src/main/res/drawable/data_sms.xml b/app/src/main/res/drawable/data_sms.xml deleted file mode 100755 index 15eea3f1c..000000000 --- a/app/src/main/res/drawable/data_sms.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/home_app_category.xml b/app/src/main/res/drawable/home_app_category.xml deleted file mode 100644 index e3d896532..000000000 --- a/app/src/main/res/drawable/home_app_category.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/home_msg_qq.xml b/app/src/main/res/drawable/home_msg_qq.xml deleted file mode 100755 index dfc7c6b3e..000000000 --- a/app/src/main/res/drawable/home_msg_qq.xml +++ /dev/null @@ -1,26 +0,0 @@ - - - - - diff --git a/app/src/main/res/drawable/home_msg_telegram.xml b/app/src/main/res/drawable/home_msg_telegram.xml deleted file mode 100755 index 9f29bf05e..000000000 --- a/app/src/main/res/drawable/home_msg_telegram.xml +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - diff --git a/app/src/main/res/drawable/ic_arrow_right.xml b/app/src/main/res/drawable/ic_arrow_right.xml deleted file mode 100644 index be1eb149a..000000000 --- a/app/src/main/res/drawable/ic_arrow_right.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/ic_backup.xml b/app/src/main/res/drawable/ic_backup.xml deleted file mode 100644 index c7353391f..000000000 --- a/app/src/main/res/drawable/ic_backup.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/ic_calendar_month.xml b/app/src/main/res/drawable/ic_calendar_month.xml deleted file mode 100644 index ab872f163..000000000 --- a/app/src/main/res/drawable/ic_calendar_month.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/ic_calendar_month2.xml b/app/src/main/res/drawable/ic_calendar_month2.xml deleted file mode 100644 index 3c0c86915..000000000 --- a/app/src/main/res/drawable/ic_calendar_month2.xml +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - diff --git a/app/src/main/res/drawable/ic_calendar_view_year.xml b/app/src/main/res/drawable/ic_calendar_view_year.xml deleted file mode 100644 index bf7eaf594..000000000 --- a/app/src/main/res/drawable/ic_calendar_view_year.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/ic_calendar_week.xml b/app/src/main/res/drawable/ic_calendar_week.xml deleted file mode 100644 index 64f78f83f..000000000 --- a/app/src/main/res/drawable/ic_calendar_week.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/ic_category_stats.xml b/app/src/main/res/drawable/ic_category_stats.xml deleted file mode 100644 index feaaa0950..000000000 --- a/app/src/main/res/drawable/ic_category_stats.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_chat_bubble.xml b/app/src/main/res/drawable/ic_chat_bubble.xml deleted file mode 100644 index 9d0422fde..000000000 --- a/app/src/main/res/drawable/ic_chat_bubble.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - - diff --git a/app/src/main/res/drawable/ic_close_themed.xml b/app/src/main/res/drawable/ic_close_themed.xml deleted file mode 100644 index f80e87885..000000000 --- a/app/src/main/res/drawable/ic_close_themed.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/ic_cloud.xml b/app/src/main/res/drawable/ic_cloud.xml deleted file mode 100644 index 48b0e08d6..000000000 --- a/app/src/main/res/drawable/ic_cloud.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - diff --git a/app/src/main/res/drawable/ic_info_outline.xml b/app/src/main/res/drawable/ic_info_outline.xml deleted file mode 100644 index 4c51a1af7..000000000 --- a/app/src/main/res/drawable/ic_info_outline.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_payments.xml b/app/src/main/res/drawable/ic_payments.xml deleted file mode 100644 index fe512e341..000000000 --- a/app/src/main/res/drawable/ic_payments.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/ic_science.xml b/app/src/main/res/drawable/ic_science.xml deleted file mode 100644 index 15a0f03be..000000000 --- a/app/src/main/res/drawable/ic_science.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/ic_screenshot.xml b/app/src/main/res/drawable/ic_screenshot.xml deleted file mode 100644 index c34c94e70..000000000 --- a/app/src/main/res/drawable/ic_screenshot.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/ic_storage.xml b/app/src/main/res/drawable/ic_storage.xml deleted file mode 100644 index d367e9728..000000000 --- a/app/src/main/res/drawable/ic_storage.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/ic_summary.xml b/app/src/main/res/drawable/ic_summary.xml deleted file mode 100644 index 3e32cafc8..000000000 --- a/app/src/main/res/drawable/ic_summary.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_switch.xml b/app/src/main/res/drawable/ic_switch.xml deleted file mode 100644 index 1b130c9c1..000000000 --- a/app/src/main/res/drawable/ic_switch.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/ic_theme.xml b/app/src/main/res/drawable/ic_theme.xml deleted file mode 100755 index ec8101dd8..000000000 --- a/app/src/main/res/drawable/ic_theme.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - diff --git a/app/src/main/res/drawable/ic_tip.xml b/app/src/main/res/drawable/ic_tip.xml deleted file mode 100644 index 23259c675..000000000 --- a/app/src/main/res/drawable/ic_tip.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/ic_trend.xml b/app/src/main/res/drawable/ic_trend.xml deleted file mode 100644 index 99da20e85..000000000 --- a/app/src/main/res/drawable/ic_trend.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_usage.xml b/app/src/main/res/drawable/ic_usage.xml deleted file mode 100644 index 4eff5f61c..000000000 --- a/app/src/main/res/drawable/ic_usage.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/icon_eye.xml b/app/src/main/res/drawable/icon_eye.xml deleted file mode 100644 index bb1db1718..000000000 --- a/app/src/main/res/drawable/icon_eye.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/icon_map.xml b/app/src/main/res/drawable/icon_map.xml deleted file mode 100644 index b2f736bd5..000000000 --- a/app/src/main/res/drawable/icon_map.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/icon_question.xml b/app/src/main/res/drawable/icon_question.xml deleted file mode 100644 index 4dec8d102..000000000 --- a/app/src/main/res/drawable/icon_question.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/img_geekbar.xml b/app/src/main/res/drawable/img_geekbar.xml deleted file mode 100644 index 0cde36030..000000000 --- a/app/src/main/res/drawable/img_geekbar.xml +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - diff --git a/app/src/main/res/drawable/menu_item_notice.xml b/app/src/main/res/drawable/menu_item_notice.xml deleted file mode 100644 index f0d8a43b4..000000000 --- a/app/src/main/res/drawable/menu_item_notice.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/navigation_rail_item_background.xml b/app/src/main/res/drawable/navigation_rail_item_background.xml deleted file mode 100644 index ac934fc49..000000000 --- a/app/src/main/res/drawable/navigation_rail_item_background.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - - - - - - diff --git a/app/src/main/res/drawable/rounded_border_.xml b/app/src/main/res/drawable/rounded_border_.xml deleted file mode 100644 index 68b539a37..000000000 --- a/app/src/main/res/drawable/rounded_border_.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/app/src/main/res/drawable/rounded_border_dashed.xml b/app/src/main/res/drawable/rounded_border_dashed.xml deleted file mode 100644 index b97afba3f..000000000 --- a/app/src/main/res/drawable/rounded_border_dashed.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/app/src/main/res/drawable/setting2_icon_translate.xml b/app/src/main/res/drawable/setting2_icon_translate.xml deleted file mode 100644 index c2f0b41de..000000000 --- a/app/src/main/res/drawable/setting2_icon_translate.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/setting2_icon_webdav_download.xml b/app/src/main/res/drawable/setting2_icon_webdav_download.xml deleted file mode 100644 index 8313c8354..000000000 --- a/app/src/main/res/drawable/setting2_icon_webdav_download.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/setting2_icon_webdav_upload.xml b/app/src/main/res/drawable/setting2_icon_webdav_upload.xml deleted file mode 100644 index ced59b258..000000000 --- a/app/src/main/res/drawable/setting2_icon_webdav_upload.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/setting_duplicated.xml b/app/src/main/res/drawable/setting_duplicated.xml deleted file mode 100644 index 8abd1447b..000000000 --- a/app/src/main/res/drawable/setting_duplicated.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/setting_icon_color.xml b/app/src/main/res/drawable/setting_icon_color.xml deleted file mode 100644 index 7b4ee261c..000000000 --- a/app/src/main/res/drawable/setting_icon_color.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/setting_icon_map.xml b/app/src/main/res/drawable/setting_icon_map.xml deleted file mode 100644 index 24e2b5533..000000000 --- a/app/src/main/res/drawable/setting_icon_map.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/setting_icon_parent.xml b/app/src/main/res/drawable/setting_icon_parent.xml deleted file mode 100644 index 0b61e9d23..000000000 --- a/app/src/main/res/drawable/setting_icon_parent.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/setting_icon_remark.xml b/app/src/main/res/drawable/setting_icon_remark.xml deleted file mode 100644 index 8a7dbbd9f..000000000 --- a/app/src/main/res/drawable/setting_icon_remark.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/shape_circle.xml b/app/src/main/res/drawable/shape_circle.xml deleted file mode 100644 index 79373caed..000000000 --- a/app/src/main/res/drawable/shape_circle.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - diff --git a/app/src/main/res/menu/asset_menu.xml b/app/src/main/res/menu/asset_menu.xml deleted file mode 100644 index c57e23508..000000000 --- a/app/src/main/res/menu/asset_menu.xml +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - diff --git a/app/src/main/res/values-night/colors.xml b/app/src/main/res/values-night/colors.xml index 3804005b2..850be6032 100644 --- a/app/src/main/res/values-night/colors.xml +++ b/app/src/main/res/values-night/colors.xml @@ -64,30 +64,13 @@ #B0BEC5 #000000 #66000000 - #47000000 - #47000000 - #66000000 - #FFCACACA - #B3FFFFFF - #FF90CAF9 - #FFFF8A80 - #FFFFFFFF - #B3FFFFFF - #FF4E2F30 - #FF5A4C21 - #FF543654 - #FF2E5050 - #FF4A356B - #FF4C3A10 - #FF2F4F2F - #FF5C3D26 #FFE6E1E5 #00000000 - #00000000 - #B3000000 + + diff --git a/app/src/main/res/values-night/themes.xml b/app/src/main/res/values-night/themes.xml index a3c6b4d66..4c17c8b44 100644 --- a/app/src/main/res/values-night/themes.xml +++ b/app/src/main/res/values-night/themes.xml @@ -35,10 +35,6 @@ false - -