├── .editorconfig ├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.yml │ └── request_notify_icon_adaption.yml └── workflows │ ├── commit_ci.yml │ └── pull_request_ci.yml ├── .gitignore ├── .idea ├── AndroidProjectSystem.xml ├── icon.png ├── inspectionProfiles │ └── Project_Default.xml ├── kotlinc.xml ├── ktlint-plugin.xml ├── ktlint.xml ├── migrations.xml ├── runConfigurations.xml └── vcs.xml ├── .secret └── universal.p12 ├── LICENSE ├── PRIVACY.md ├── README.md ├── app ├── .gitignore ├── build.gradle.kts ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── fankes │ │ └── coloros │ │ └── notify │ │ └── ExampleInstrumentedTest.kt │ ├── main │ ├── AndroidManifest.xml │ ├── ic_launcher-playstore.png │ ├── java │ │ └── com │ │ │ └── fankes │ │ │ └── coloros │ │ │ └── notify │ │ │ ├── application │ │ │ └── CNNApplication.kt │ │ │ ├── bean │ │ │ └── IconDataBean.kt │ │ │ ├── const │ │ │ └── ConstFactory.kt │ │ │ ├── data │ │ │ ├── ConfigData.kt │ │ │ └── factory │ │ │ │ ├── CompoundButtonFactory.kt │ │ │ │ └── SeekBarFactory.kt │ │ │ ├── hook │ │ │ ├── HookEntry.kt │ │ │ └── entity │ │ │ │ ├── FrameworkHooker.kt │ │ │ │ └── SystemUIHooker.kt │ │ │ ├── param │ │ │ ├── IconPackParams.kt │ │ │ └── factory │ │ │ │ └── DataFactory.kt │ │ │ ├── service │ │ │ └── QuickStartTileService.kt │ │ │ ├── ui │ │ │ ├── activity │ │ │ │ ├── ConfigureActivity.kt │ │ │ │ ├── MainActivity.kt │ │ │ │ ├── auto │ │ │ │ │ └── NotifyIconRuleUpdateActivity.kt │ │ │ │ └── base │ │ │ │ │ └── BaseActivity.kt │ │ │ └── widget │ │ │ │ └── MaterialSwitch.kt │ │ │ ├── utils │ │ │ ├── factory │ │ │ │ ├── BackPressedEventFactory.kt │ │ │ │ ├── BaseAdapterFactory.kt │ │ │ │ ├── DialogBuilderFactory.kt │ │ │ │ ├── ExceptionFactory.kt │ │ │ │ └── FunctionFactory.kt │ │ │ └── tool │ │ │ │ ├── ActivationPromptTool.kt │ │ │ │ ├── BitmapCompatTool.kt │ │ │ │ ├── GithubReleaseTool.kt │ │ │ │ ├── I18nWarnTool.kt │ │ │ │ ├── IconAdaptationTool.kt │ │ │ │ ├── IconRuleManagerTool.kt │ │ │ │ └── SystemUITool.kt │ │ │ └── wrapper │ │ │ └── BuildConfigWrapper.kt │ └── res │ │ ├── drawable-night │ │ ├── bg_dark_round.xml │ │ └── bg_permotion_round.xml │ │ ├── drawable │ │ ├── bg_button_round.xml │ │ ├── bg_dark_round.xml │ │ ├── bg_green_round.xml │ │ ├── bg_orange_round.xml │ │ ├── bg_permotion_round.xml │ │ ├── bg_yellow_round.xml │ │ ├── ic_about.xml │ │ ├── ic_back.xml │ │ ├── ic_filter.xml │ │ ├── ic_function.xml │ │ ├── ic_github.xml │ │ ├── ic_home.xml │ │ ├── ic_info.xml │ │ ├── ic_message.xml │ │ ├── ic_modify.xml │ │ ├── ic_nf_icon_update.xml │ │ ├── ic_notify.xml │ │ ├── ic_notify_icon.xml │ │ ├── ic_notify_update.xml │ │ ├── ic_page_bottom.xml │ │ ├── ic_page_top.xml │ │ ├── ic_preference.xml │ │ ├── ic_restart.xml │ │ ├── ic_success.xml │ │ ├── ic_sync.xml │ │ ├── ic_system_clock.xml │ │ ├── ic_theme.xml │ │ ├── ic_unsupported.xml │ │ └── ic_warn.xml │ │ ├── layout │ │ ├── activity_config.xml │ │ ├── activity_main.xml │ │ ├── adapter_config.xml │ │ ├── dia_icon_filter.xml │ │ ├── dia_source_from.xml │ │ └── dia_source_from_string.xml │ │ ├── mipmap-anydpi-v26 │ │ ├── ic_launcher.xml │ │ └── ic_launcher_round.xml │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.png │ │ ├── ic_launcher_foreground.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-mdpi │ │ ├── ic_launcher.png │ │ ├── ic_launcher_foreground.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.png │ │ ├── ic_launcher_foreground.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxhdpi │ │ ├── bg_payment_code.jpg │ │ ├── ic_launcher.png │ │ ├── ic_launcher_foreground.png │ │ ├── ic_launcher_round.png │ │ └── ic_yukihookapi.png │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.png │ │ ├── ic_launcher_foreground.png │ │ └── ic_launcher_round.png │ │ ├── values-night │ │ ├── color.xml │ │ └── themes.xml │ │ └── values │ │ ├── array.xml │ │ ├── color.xml │ │ ├── colors.xml │ │ ├── ic_launcher_background.xml │ │ ├── strings.xml │ │ └── themes.xml │ └── test │ └── java │ └── com │ └── fankes │ └── coloros │ └── notify │ └── ExampleUnitTest.kt ├── build.gradle.kts ├── gradle.properties ├── gradle ├── sweet-dependency │ └── sweet-dependency-config.yaml └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── img-src └── icon.png └── settings.gradle.kts /.editorconfig: -------------------------------------------------------------------------------- 1 | # noinspection EditorConfigKeyCorrectness 2 | [{*.kt,*.kts}] 3 | ktlint_standard_annotation = disabled 4 | ktlint_standard_filename = disabled 5 | ktlint_standard_wrapping = disabled 6 | ktlint_standard_import-ordering = enabled 7 | ktlint_standard_max-line-length = disabled 8 | ktlint_standard_multiline-if-else = disabled 9 | ktlint_standard_argument-list-wrapping = disabled 10 | ktlint_standard_parameter-list-wrapping = disabled 11 | ktlint_standard_trailing-comma-on-declaration-site = disabled 12 | ktlint_function_signature_body_expression_wrapping = multiline 13 | ktlint_standard_string-template-indent = disabled 14 | ktlint_standard_function-signature = disabled 15 | ktlint_standard_trailing-comma-on-call-site = disabled 16 | ktlint_standard_multiline-expression-wrapping = disabled 17 | ktlint_standard_no-empty-first-line-in-class-body = disabled 18 | ktlint_standard_if-else-wrapping = disabled 19 | ktlint_standard_if-else-bracing = disabled 20 | ktlint_standard_statement-wrapping = disabled 21 | ktlint_standard_blank-line-before-declaration = disabled 22 | ktlint_standard_no-empty-file = disabled 23 | ktlint_standard_property-naming = disabled 24 | ktlint_standard_function-naming = disabled 25 | ktlint_standard_chain-method-continuation = disabled 26 | ktlint_standard_class-signature = disabled 27 | ktlint_standard_condition-wrapping = disabled 28 | ktlint_standard_class-signature = disabled 29 | ij_continuation_indent_size = 2 30 | indent_size = 4 31 | indent_style = space 32 | insert_final_newline = false 33 | max_line_length = 150 -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.yml: -------------------------------------------------------------------------------- 1 | name: 问题与 BUG 反馈 2 | description: 问题反馈必须使用此模板进行提交 3 | labels: [bug] 4 | title: "[问题与 BUG 反馈] (在这里简要描述问题原因)" 5 | body: 6 | - type: markdown 7 | attributes: 8 | value: | 9 | ### 请在下方填写问题发生的具体原因和复现步骤。 10 | 11 | RealmeUI 与 OxygenOS ≥ 12 都是基于 ColorOS 进行修改而来,理论功能是通用的,请额外注意在下方选择正确的系统类型。 12 | 13 | 我们只接受 ColorOS/RealmeUI/OxygenOS 正规官方版本系统,如果你正在使用官改(第三方修改版)请不要提交任何 BUG 与问题,我们无义务去解决,请自求多福。 14 | 15 | 发生异常、崩溃、闪退或功能性问题,必须提交问题 Log (日志),没有 Log 的 issues 将直接被关闭。 16 | - type: input 17 | attributes: 18 | label: 模块版本 19 | description: 请填写当前使用的模块完整版本号,例如:**2.0** 20 | validations: 21 | required: true 22 | - type: dropdown 23 | attributes: 24 | label: 系统类型 25 | description: 请选择你使用的系统类型。 26 | options: 27 | - ColorOS 28 | - RealmeUI 29 | - OxygenOS 30 | validations: 31 | required: true 32 | - type: input 33 | attributes: 34 | label: 系统版本 35 | description: 这里填写当前的系统版本,以 ColorOS 举例:**ColorOS 12.1 LE2120_11_C.67** 36 | validations: 37 | required: true 38 | - type: dropdown 39 | attributes: 40 | label: Android 版本 41 | options: 42 | - 15 43 | - 14 44 | - 13 45 | - 12L/12.1 46 | - 12 47 | - 11 48 | - 10 49 | - 9 50 | validations: 51 | required: true 52 | - type: input 53 | attributes: 54 | label: Xposed 框架名称与版本号 55 | description: 请填写当前使用的 Xposed 框架,例如:**LSPosed 1.8.4(次版本号)** 56 | validations: 57 | required: true 58 | - type: input 59 | attributes: 60 | label: 与系统界面(系统 UI)同作用域的 Xposed 模块 61 | description: | 62 | 此模块的作用域为系统界面(系统 UI),为确保非其它模块冲突造成的问题,请一定要填写当前你同时激活的相关模块。 63 | 若没有,请直接在下方填写“无”。 64 | validations: 65 | required: true 66 | - type: textarea 67 | attributes: 68 | label: 详细描述问题发生的具体原因 69 | description: 请在下方详细描述问题发生的具体场景、复现步骤和经过,以便我们能够按照你所描述的步骤复现这个问题。 70 | validations: 71 | required: true 72 | - type: textarea 73 | attributes: 74 | label: 提供模块问题 Log 或必要 Log 75 | description: LSPosed 可在日志管理中查看并筛选包含 `ColorOSNotifyIcon` 的日志。 76 | value: | 77 |
展开查看

78 | 
79 |         (此处粘贴问题 Log)
80 | 
81 |         
82 | 83 | validations: 84 | required: true 85 | - type: checkboxes 86 | attributes: 87 | label: 确认一下你提交的信息 88 | description: | 89 | 为了确保 issues 的质量和避免浪费不必要的时间,未勾选下方选项的 issues 将直接被关闭。 90 | 请一定确保你已经**勾选下方的选项**后再提交。 91 | options: 92 | - label: 我确保上述信息准确无误 93 | required: false 94 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/request_notify_icon_adaption.yml: -------------------------------------------------------------------------------- 1 | name: 通知图标优化适配反馈 2 | description: 提交通知图标优化适配必须使用此模板提交 3 | labels: [To be adapted] 4 | title: "[通知图标优化适配反馈]" 5 | body: 6 | - type: markdown 7 | attributes: 8 | value: | 9 | ### 请在下方填写你需要适配的 APP 通知图标的必要信息。 10 | 11 | **China and Chinese only.** 12 | **仅限中国和中文。** 13 | 14 | 以下类型的 APP 不予适配: 15 | 16 | - VPN、翻墙软件 17 | - 涉嫌色情、赌博类软件 18 | - 申请超限权限、涉嫌泄露国家机密行为的软件 19 | 20 | 以下类型的 APP 通知图标暂不适配: 21 | 22 | - 多态彩色图标,状态不唯一,例如 360 极速浏览器 23 | - 规范的原生图标,但未被通知图标规则适配的 (将稍后加入白名单) 24 | - type: input 25 | attributes: 26 | label: APP 名称 27 | description: 这里填写 APP 的名称,例如:**微信** 28 | validations: 29 | required: true 30 | - type: input 31 | attributes: 32 | label: APP 包名 33 | description: 这里填写 APP 的包名,例如:**com.tencent.mm** 34 | validations: 35 | required: true 36 | - type: input 37 | attributes: 38 | label: 通知图标颜色 (HEX) 39 | description: | 40 | 这里填写通知图标在下拉通知栏中的图标颜色,要求为 16 进制,例如:**#ff232323** 41 | 如果不知道什么是 16 进制颜色,可以参考 [这里](https://www.qtccolor.com/tool/hex.aspx) 的取色。 42 | 留空代表使用系统默认主题色。 43 | validations: 44 | required: false 45 | - type: input 46 | attributes: 47 | label: 下载渠道、来源地址链接 48 | description: 请填写我们应该从何处得到你需要适配的这个 APP 的下载链接。 49 | validations: 50 | required: true 51 | - type: textarea 52 | attributes: 53 | label: 简单描述适配的通知图标使用场景 54 | description: 简单描述一下当前 APP 的通知图标在何时会变成彩色的、不规范的以及可触发推送通知的操作,例如小米推送或 HMS 推送。 55 | validations: 56 | required: true 57 | - type: textarea 58 | attributes: 59 | label: 通知单色图标适配素材 (大小 50x50~72x72) 60 | description: 请在这里填写我们能够获得这个图标的网址或在下方的文本框粘贴你需要上传的图标。 61 | validations: 62 | required: false 63 | - type: checkboxes 64 | attributes: 65 | label: 确认一下你提交的信息 66 | description: | 67 | 为了确保 issues 的质量和避免浪费不必要的时间,未勾选下方选项的 issues 将直接被关闭。 68 | 请一定确保你已经**勾选下方的选项**后再提交。 69 | options: 70 | - label: 我确保上述信息准确无误 71 | required: false 72 | -------------------------------------------------------------------------------- /.github/workflows/commit_ci.yml: -------------------------------------------------------------------------------- 1 | name: Automatic Build on Commit 2 | 3 | on: 4 | workflow_dispatch: 5 | push: 6 | branches: [ master ] 7 | paths-ignore: 8 | - '**.md' 9 | - '**.txt' 10 | - '.github/**' 11 | - '!.github/workflows/**' 12 | 13 | jobs: 14 | build: 15 | name: Build CI 16 | if: ${{ success() }} 17 | runs-on: ubuntu-latest 18 | env: 19 | APK_OUTPUT_PATH: 'app/build/outputs/apk' 20 | TG_BOT_TOKEN: ${{ secrets.TELEGRAM_BOT_TOKEN }} 21 | TG_CHAT_ID: ${{ secrets.TELEGRAM_CHAT_ID }} 22 | COMMIT_MESSAGE: |+ 23 | New push to GitHub\! 24 | ``` 25 | ${{ github.event.head_commit.message }} 26 | ```by `${{ github.event.head_commit.author.name }}` 27 | See commit detail [here](${{ github.event.head_commit.url }}) 28 | COMMIT_URL: ${{ github.event.head_commit.url }} 29 | steps: 30 | - uses: actions/checkout@v4 31 | - name: Prepare GitHub Env 32 | run: | 33 | GITHUB_SHA=${{ github.sha }} 34 | GITHUB_CI_COMMIT_ID=${GITHUB_SHA:0:7} 35 | echo "GITHUB_CI_COMMIT_ID=$GITHUB_CI_COMMIT_ID" >> $GITHUB_ENV 36 | - name: Setup cmake 37 | uses: jwlawson/actions-setup-cmake@v1 38 | with: 39 | cmake-version: '3.22.1' 40 | - name: Prepare Java 17 41 | uses: actions/setup-java@v4 42 | with: 43 | java-version: 17 44 | java-package: jdk 45 | distribution: 'temurin' 46 | cache: 'gradle' 47 | - name: Cache Gradle Dependencies 48 | uses: actions/cache@v3 49 | with: 50 | path: | 51 | ~/.gradle/caches 52 | ~/.gradle/wrapper 53 | !~/.gradle/caches/build-cache-* 54 | key: gradle-deps-core-${{ hashFiles('**/build.gradle.kts') }} 55 | restore-keys: | 56 | gradle-deps 57 | - name: Cache Gradle Build 58 | uses: actions/cache@v3 59 | with: 60 | path: | 61 | ~/.gradle/caches/build-cache-* 62 | key: gradle-builds-core-${{ github.sha }} 63 | restore-keys: | 64 | gradle-builds 65 | - name: Build with Gradle 66 | run: | 67 | ./gradlew :app:assembleDebug 68 | ./gradlew :app:assembleRelease 69 | echo "DEBUG_APK_PATH=$(find ${{ env.APK_OUTPUT_PATH }}/debug -name '*.apk')" >> $GITHUB_ENV 70 | echo "RELEASE_APK_PATH=$(find ${{ env.APK_OUTPUT_PATH }}/release -name '*.apk')" >> $GITHUB_ENV 71 | - name: Upload Artifacts (Debug) 72 | uses: actions/upload-artifact@v4 73 | with: 74 | path: ${{ env.DEBUG_APK_PATH }} 75 | name: ColorOSNotifyIcon-debug-${{ github.event.head_commit.id }} 76 | - name: Upload Artifacts (Release) 77 | uses: actions/upload-artifact@v4 78 | with: 79 | path: ${{ env.RELEASE_APK_PATH }} 80 | name: ColorOSNotifyIcon-release-${{ github.event.head_commit.id }} 81 | - name: Post Artifacts to Telegram 82 | run: | 83 | export debug=$(find ${{ env.APK_OUTPUT_PATH }}/debug -name "*.apk") 84 | export release=$(find ${{ env.APK_OUTPUT_PATH }}/release -name "*.apk") 85 | ESCAPED=`python3 -c 'import json,os,urllib.parse; msg = json.dumps(os.environ["COMMIT_MESSAGE"]); print(urllib.parse.quote(msg if len(msg) <= 1024 else json.dumps(os.environ["COMMIT_URL"])))'` 86 | curl -v "https://api.telegram.org/bot${TG_BOT_TOKEN}/sendMediaGroup?chat_id=${TG_CHAT_ID}&media=%5B%7B%22type%22%3A%22document%22%2C%20%22media%22%3A%22attach%3A%2F%2Fdebug%22%7D%2C%7B%22type%22%3A%22document%22%2C%20%22media%22%3A%22attach%3A%2F%2Frelease%22%2C%22parse_mode%22%3A%22MarkdownV2%22%2C%22caption%22:${ESCAPED}%7D%5D" \ 87 | -F debug="@$debug" \ 88 | -F release="@$release" -------------------------------------------------------------------------------- /.github/workflows/pull_request_ci.yml: -------------------------------------------------------------------------------- 1 | name: Pull Request Checker 2 | 3 | on: 4 | pull_request: 5 | branches: [ master ] 6 | paths-ignore: 7 | - '**.md' 8 | - '**.txt' 9 | - '.github/**' 10 | - '!.github/workflows/**' 11 | 12 | jobs: 13 | build: 14 | name: Pull Request Check 15 | if: ${{ success() }} 16 | runs-on: ubuntu-latest 17 | env: 18 | APK_OUTPUT_PATH: 'app/build/outputs/apk' 19 | steps: 20 | - uses: actions/checkout@v4 21 | - name: Prepare GitHub Env 22 | run: | 23 | GITHUB_SHA=${{ github.sha }} 24 | GITHUB_CI_COMMIT_ID=${GITHUB_SHA:0:7} 25 | echo "GITHUB_CI_COMMIT_ID=$GITHUB_CI_COMMIT_ID" >> $GITHUB_ENV 26 | - name: Setup cmake 27 | uses: jwlawson/actions-setup-cmake@v1 28 | with: 29 | cmake-version: '3.22.1' 30 | - name: Prepare Java 17 31 | uses: actions/setup-java@v4 32 | with: 33 | java-version: 17 34 | java-package: jdk 35 | distribution: 'temurin' 36 | cache: 'gradle' 37 | - name: Cache Gradle Dependencies 38 | uses: actions/cache@v3 39 | with: 40 | path: | 41 | ~/.gradle/caches 42 | ~/.gradle/wrapper 43 | !~/.gradle/caches/build-cache-* 44 | key: gradle-deps-core-${{ hashFiles('**/build.gradle.kts') }} 45 | restore-keys: | 46 | gradle-deps 47 | - name: Cache Gradle Build 48 | uses: actions/cache@v3 49 | with: 50 | path: | 51 | ~/.gradle/caches/build-cache-* 52 | key: gradle-builds-core-${{ github.sha }} 53 | restore-keys: | 54 | gradle-builds 55 | - name: Build with Gradle 56 | run: | 57 | ./gradlew :app:assembleDebug 58 | ./gradlew :app:assembleRelease 59 | echo "DEBUG_APK_PATH=$(find ${{ env.APK_OUTPUT_PATH }}/debug -name '*.apk')" >> $GITHUB_ENV 60 | echo "RELEASE_APK_PATH=$(find ${{ env.APK_OUTPUT_PATH }}/release -name '*.apk')" >> $GITHUB_ENV 61 | - name: Upload Artifacts (Debug) 62 | uses: actions/upload-artifact@v4 63 | with: 64 | path: ${{ env.DEBUG_APK_PATH }} 65 | name: ColorOSNotifyIcon-debug-${{ github.event.head_commit.id }} 66 | - name: Upload Artifacts (Release) 67 | uses: actions/upload-artifact@v4 68 | with: 69 | path: ${{ env.RELEASE_APK_PATH }} 70 | name: ColorOSNotifyIcon-release-${{ github.event.head_commit.id }} -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Fully .gtignore for IntelliJ, Android Studio and Gradle based Java projects 2 | ## References: 3 | ## - https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore 4 | ## - https://github.com/android/platform-samples/blob/main/.gitignore 5 | 6 | # User-specific stuff 7 | .idea/**/workspace.xml 8 | .idea/**/tasks.xml 9 | .idea/**/usage.statistics.xml 10 | .idea/**/dictionaries 11 | .idea/**/shelf 12 | 13 | # AWS User-specific 14 | .idea/**/aws.xml 15 | 16 | # Generated files 17 | .idea/**/contentModel.xml 18 | 19 | # Sensitive or high-churn files 20 | .idea/**/dataSources/ 21 | .idea/**/dataSources.ids 22 | .idea/**/dataSources.local.xml 23 | .idea/**/sqlDataSources.xml 24 | .idea/**/dynamic.xml 25 | .idea/**/uiDesigner.xml 26 | .idea/**/dbnavigator.xml 27 | 28 | # Gradle 29 | .idea/**/gradle.xml 30 | .idea/**/libraries 31 | 32 | # Gradle and Maven with auto-import 33 | .idea/.name 34 | .idea/artifacts 35 | .idea/compiler.xml 36 | .idea/jarRepositories.xml 37 | .idea/modules.xml 38 | .idea/*.iml 39 | .idea/modules 40 | .idea/caches 41 | .idea/material_theme** 42 | .idea/other.xml 43 | *.iml 44 | *.ipr 45 | 46 | # Kotlin 47 | .kotlin 48 | 49 | # Misc 50 | .idea/misc.xml 51 | 52 | # CMake 53 | cmake-build-*/ 54 | 55 | # Mongo Explorer plugin 56 | .idea/**/mongoSettings.xml 57 | 58 | # File-based project format 59 | *.iws 60 | 61 | # IntelliJ 62 | out/ 63 | 64 | # mpeltonen/sbt-idea plugin 65 | .idea_modules/ 66 | 67 | # JIRA plugin 68 | atlassian-ide-plugin.xml 69 | 70 | # Cursive Clojure plugin 71 | .idea/replstate.xml 72 | 73 | # SonarLint plugin 74 | .idea/sonarlint/ 75 | 76 | # Crashlytics plugin (for Android Studio and IntelliJ) 77 | com_crashlytics_export_strings.xml 78 | crashlytics.properties 79 | crashlytics-build.properties 80 | fabric.properties 81 | 82 | # Editor-based Rest Client 83 | .idea/httpRequests 84 | 85 | # Android studio 3.1+ serialized cache file 86 | .idea/caches/build_file_checksums.ser 87 | 88 | # Android studio 3.1+ additional 89 | .idea/deployment*.xml 90 | .idea/assetWizardSettings.xml 91 | .idea/androidTestResultsUserPreferences.xml 92 | 93 | # Android projects 94 | **/local.properties 95 | /captures 96 | .externalNativeBuild 97 | .cxx 98 | 99 | # Gradle projects 100 | .gradle 101 | build/ 102 | 103 | # Mkdocs temporary serving folder 104 | docs-gen 105 | site 106 | *.bak 107 | .idea/appInsightsSettings.xml 108 | 109 | # Mac OS 110 | .DS_Store -------------------------------------------------------------------------------- /.idea/AndroidProjectSystem.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /.idea/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fankes/ColorOSNotifyIcon/492d0dad6725f039d08d977f8a2ff81eb25451f6/.idea/icon.png -------------------------------------------------------------------------------- /.idea/inspectionProfiles/Project_Default.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 12 | -------------------------------------------------------------------------------- /.idea/kotlinc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /.idea/ktlint-plugin.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | MANUAL 5 | false 6 | 7 | -------------------------------------------------------------------------------- /.idea/ktlint.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | false 5 | 6 | -------------------------------------------------------------------------------- /.idea/migrations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 10 | -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 16 | 17 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.secret/universal.p12: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fankes/ColorOSNotifyIcon/492d0dad6725f039d08d977f8a2ff81eb25451f6/.secret/universal.p12 -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ColorOS 通知图标增强 2 | 3 | [![GitHub license](https://img.shields.io/github/license/fankes/ColorOSNotifyIcon?color=blue)](https://github.com/fankes/ColorOSNotifyIcon/blob/master/LICENSE) 4 | [![GitHub CI](https://img.shields.io/github/actions/workflow/status/fankes/ColorOSNotifyIcon/commit_ci.yml?label=CI%20builds)](https://github.com/fankes/ColorOSNotifyIcon/actions/workflows/commit_ci.yml) 5 | [![GitHub release](https://img.shields.io/github/v/release/fankes/ColorOSNotifyIcon?display_name=release&logo=github&color=green)](https://github.com/fankes/ColorOSNotifyIcon/releases) 6 | ![GitHub all releases](https://img.shields.io/github/downloads/fankes/ColorOSNotifyIcon/total?label=downloads) 7 | ![GitHub all releases](https://img.shields.io/github/downloads/Xposed-Modules-Repo/com.fankes.coloros.notify/total?label=LSPosed%20downloads&labelColor=F48FB1) 8 | 9 | [![Telegram CI](https://img.shields.io/badge/CI%20builds-Telegram-blue.svg?logo=telegram)](https://t.me/ColorOSNotifyIcon_CI) 10 | [![Telegram](https://img.shields.io/badge/discussion-Telegram-blue.svg?logo=telegram)](https://t.me/XiaofangInternet) 11 | [![QQ](https://img.shields.io/badge/discussion-QQ-blue.svg?logo=tencent-qq&logoColor=red)](https://qm.qq.com/cgi-bin/qm/qr?k=dp2h5YhWiga9WWb_Oh7kSHmx01X8I8ii&jump_from=webapi&authKey=Za5CaFP0lk7+Zgsk2KpoBD7sSaYbeXbsDgFjiWelOeH4VSionpxFJ7V0qQBSqvFM) 12 | [![QQ 频道](https://img.shields.io/badge/discussion-QQ%20频道-blue.svg?logo=tencent-qq&logoColor=red)](https://pd.qq.com/s/44gcy28h) 13 | 14 | LOGO 15 | 16 | Optimize notification icons for ColorOS and adapt to native notification icon specifications. 17 | 18 | 为 ColorOS 优化通知图标以及适配原生通知图标规范,理论支持 OxygenOS 和 RealmeUI。 19 | 20 | ## For Non-Chinese Users 21 | 22 | This project will not be adapted i18n, please stay tuned for my new projects in the future. 23 | 24 | ## 项目迁移公告 25 | 26 | 由于本人同时维护 **MIUI** 与 **ColorOS** 两个系统需要同时维护两个模块,十分不方便,所以我决定在后期逐渐合并两个项目并解耦合为一个新项目并计划适配更多系统与设备,例如原生与类原生系统。 27 | 28 | 在新的项目确定后,会在这里添加新项目的链接,届时我会终止维护这个项目并建议大家转移到新项目。 29 | 30 | ## 适配说明 31 | 32 | - 此模块仅支持 **LSPosed** (作用域“系统界面”)、**~~EdXposed(随时停止支持)~~**、不支持**太极、无极** 33 | 34 | - 目前仅在 ColorOS 12、12.1、13 for OnePlus 上测试通过,如有问题请提交 `issues` 35 | 36 | - 建议在不低于 ColorOS 11 的版本上使用 37 | 38 | ## 注意事项 39 | 40 | 由于 ColorOS 15 版本的系统性通知图标行为变更,系统强制在通知图标初始化阶段就将图标强制替换为 APP 彩色图标进行破坏,所以目前加入了 “系统框架” 41 | 作用域,如果在模块安装后没有自动勾选此作用域,请手动进行勾选并重新启动系统以修复此破坏行为,ColorOS 15 以下版本的系统无需勾选。 42 | 43 | 感谢 [Nep-Timeline](https://github.com/Nep-Timeline) 提供的解决方案。 44 | 45 | ## 历史背景 46 | 47 | 继 MIUI 之后的第二大系统 ColorOS 虽然支持原生通知图标,但是第三方推送五颜六色的图标系统并没有做适配,甚至系统自己的图标都是彩色的,极其不友好。 48 | 49 | 而且从 ColorOS 12 开始,原生图标丢失了着色属性,这也是一种对原生 Android 生态的破坏。 50 | 51 | ## 贡献通知图标优化名单 52 | 53 | 此项目是 `AndroidNotifyIconAdapt` 项目的一部分,详情请参考下方。 54 | 55 | - [Android 通知图标规范适配计划](https://github.com/fankes/AndroidNotifyIconAdapt) 56 | 57 | ## 发行渠道 58 | 59 | | LOGO | [GitHub CI](https://github.com/fankes/ColorOSNotifyIcon/actions/workflows/commit_ci.yml) | CI 自动构建 (测试版) | 60 | |------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------|---------------| 61 | 62 | | LOGO | [Telegram CI 频道](https://t.me/ColorOSNotifyIcon_CI) | CI 自动构建 (测试版) | 63 | |-----------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------|---------------| 64 | 65 | | LOGO | [GitHub Releases](https://github.com/fankes/ColorOSNotifyIcon/releases) | 正式版 (稳定版) | 66 | |------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------|-----------| 67 | 68 | | LOGO | [Xposed-Modules-Repo](https://github.com/Xposed-Modules-Repo/com.fankes.coloros.notify/releases) | 正式版 (稳定版) | 69 | |------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------|-----------| 70 | 71 | | LOGO | [123 云盘 **(密码:al5u)**](https://www.123pan.com/s/5SlUVv-C8DBh.html) | 正式版 (稳定版) | 72 | |------------------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------|-----------| 73 | 74 | 本模块发布地址仅限于上述所列出的地址,从其他非正规渠道下载到的版本或对您造成任何影响均与我们无关。 75 | 76 | ## 请勿用于非法用途 77 | 78 |

1. 本软件免费、由兴趣使然、仅供学习交流而开发,如果你是从其他不明来源的渠道付费得到本软件,则你已上当受骗,若发现欢迎向我们举报。

79 | 80 |

2. 未经本人许可,禁止转载、搬运本软件的安装包及源代码到 GitHub 以外的平台并提供下载链接。

81 | 82 | ## 项目推广 83 | 84 | 85 |
86 |

嘿,还请君留步!👋

87 |

这里有 Android 开发工具、UI 设计、Gradle 插件、Xposed 模块和实用软件等相关项目。

88 |

如果下方的项目能为你提供帮助,不妨为我点个 star 吧!

89 |

所有项目免费、开源,遵循对应开源许可协议。

90 |

→ 查看更多关于我的项目,请点击这里 ←

91 |
92 | 93 | ## Star History 94 | 95 | ![Star History Chart](https://api.star-history.com/svg?repos=fankes/ColorOSNotifyIcon&type=Date) 96 | 97 | ## 隐私政策 98 | 99 | - [PRIVACY](PRIVACY.md) 100 | 101 | ## 许可证 102 | 103 | - [AGPL-3.0](https://www.gnu.org/licenses/agpl-3.0.html) 104 | 105 | ``` 106 | Copyright (C) 20174 Fankes Studio(qzmmcn@163.com) 107 | 108 | This program is free software: you can redistribute it and/or modify 109 | it under the terms of the GNU Affero General Public License as 110 | published by the Free Software Foundation, either version 3 of the 111 | License, or (at your option) any later version. 112 | 113 | This program is distributed in the hope that it will be useful, 114 | but WITHOUT ANY WARRANTY; without even the implied warranty of 115 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 116 | GNU Affero General Public License for more details. 117 | 118 | You should have received a copy of the GNU Affero General Public License 119 | along with this program. If not, see . 120 | ``` 121 | 122 | Powered by [YukiHookAPI](https://github.com/HighCapable/YukiHookAPI) 123 | 124 | 版权所有 © 20174 Fankes Studio(qzmmcn@163.com) -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /src/main/assets/xposed_init 2 | /src/main/resources/META-INF/yukihookapi_init -------------------------------------------------------------------------------- /app/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | autowire(libs.plugins.android.application) 3 | autowire(libs.plugins.kotlin.android) 4 | autowire(libs.plugins.kotlin.ksp) 5 | } 6 | 7 | android { 8 | namespace = property.project.app.packageName 9 | compileSdk = property.project.android.compileSdk 10 | 11 | signingConfigs { 12 | create("universal") { 13 | keyAlias = property.project.app.signing.keyAlias 14 | keyPassword = property.project.app.signing.keyPassword 15 | storeFile = rootProject.file(property.project.app.signing.storeFilePath) 16 | storePassword = property.project.app.signing.storePassword 17 | enableV1Signing = true 18 | enableV2Signing = true 19 | } 20 | } 21 | defaultConfig { 22 | applicationId = property.project.app.packageName 23 | minSdk = property.project.android.minSdk 24 | targetSdk = property.project.android.targetSdk 25 | versionName = property.project.app.versionName 26 | versionCode = property.project.app.versionCode 27 | testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" 28 | } 29 | buildTypes { 30 | all { signingConfig = signingConfigs.getByName("universal") } 31 | release { 32 | isMinifyEnabled = true 33 | isShrinkResources = true 34 | proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro") 35 | } 36 | } 37 | compileOptions { 38 | sourceCompatibility = JavaVersion.VERSION_17 39 | targetCompatibility = JavaVersion.VERSION_17 40 | } 41 | kotlinOptions { 42 | jvmTarget = "17" 43 | freeCompilerArgs = listOf( 44 | "-Xno-param-assertions", 45 | "-Xno-call-assertions", 46 | "-Xno-receiver-assertions" 47 | ) 48 | } 49 | buildFeatures { 50 | buildConfig = true 51 | viewBinding = true 52 | } 53 | lint { checkReleaseBuilds = false } 54 | androidResources.additionalParameters += listOf("--allow-reserved-package-id", "--package-id", "0x37") 55 | } 56 | 57 | androidComponents { 58 | onVariants(selector().all()) { 59 | it.outputs.forEach { output -> 60 | val currentType = it.buildType 61 | 62 | // Workaround for GitHub Actions. 63 | // Why? I don't know, but it works. 64 | // Unresolved reference. None of the following candidates is applicable because of receiver type mismatch: 65 | // public inline fun CharSequence.isNotBlank(): Boolean defined in kotlin.text. 66 | @Suppress("UNNECESSARY_SAFE_CALL", "RemoveRedundantCallsOfConversionMethods") 67 | val currentSuffix = property.github.ci.commit.id?.let { suffix -> 68 | // Workaround for GitHub Actions. 69 | // Strongly transfer type to [String]. 70 | val sSuffix = suffix.toString() 71 | if (sSuffix.isNotBlank()) "-$sSuffix" else "" 72 | } 73 | val currentVersion = "${output.versionName.get()}$currentSuffix(${output.versionCode.get()})" 74 | if (output is com.android.build.api.variant.impl.VariantOutputImpl) 75 | output.outputFileName.set("${property.project.name}-v$currentVersion-$currentType.apk") 76 | } 77 | } 78 | } 79 | 80 | dependencies { 81 | compileOnly(de.robv.android.xposed.api) 82 | implementation(com.highcapable.yukihookapi.api) 83 | ksp(com.highcapable.yukihookapi.ksp.xposed) 84 | implementation(com.fankes.projectpromote.project.promote) 85 | implementation(com.github.topjohnwu.libsu.core) 86 | implementation(com.github.duanhong169.drawabletoolbox) 87 | implementation(com.squareup.okhttp3.okhttp) 88 | implementation(androidx.core.core.ktx) 89 | implementation(androidx.appcompat.appcompat) 90 | implementation(com.google.android.material.material) 91 | implementation(androidx.constraintlayout.constraintlayout) 92 | testImplementation(junit.junit) 93 | androidTestImplementation(androidx.test.ext.junit) 94 | androidTestImplementation(androidx.test.espresso.espresso.core) 95 | } -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | 23 | -ignorewarnings 24 | -optimizationpasses 10 25 | -dontusemixedcaseclassnames 26 | -dontoptimize 27 | -verbose 28 | -overloadaggressively 29 | -allowaccessmodification 30 | -adaptclassstrings 31 | -adaptresourcefilenames 32 | -adaptresourcefilecontents 33 | 34 | -renamesourcefileattribute P 35 | -keepattributes SourceFile,Signature,LineNumberTable 36 | 37 | -assumenosideeffects class kotlin.jvm.internal.Intrinsics { 38 | public static *** throwUninitializedProperty(...); 39 | public static *** throwUninitializedPropertyAccessException(...); 40 | } 41 | 42 | -keep class * extends android.app.Activity 43 | -keep class * implements androidx.viewbinding.ViewBinding { 44 | (); 45 | *** inflate(android.view.LayoutInflater); 46 | } -------------------------------------------------------------------------------- /app/src/androidTest/java/com/fankes/coloros/notify/ExampleInstrumentedTest.kt: -------------------------------------------------------------------------------- 1 | package com.fankes.coloros.notify 2 | 3 | import androidx.test.platform.app.InstrumentationRegistry 4 | import androidx.test.ext.junit.runners.AndroidJUnit4 5 | 6 | import org.junit.Test 7 | import org.junit.runner.RunWith 8 | 9 | import org.junit.Assert.* 10 | 11 | /** 12 | * Instrumented test, which will execute on an Android device. 13 | * 14 | * See [testing documentation](http://d.android.com/tools/testing). 15 | */ 16 | @RunWith(AndroidJUnit4::class) 17 | class ExampleInstrumentedTest { 18 | @Test 19 | fun useAppContext() { 20 | // Context of the app under test. 21 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext 22 | assertEquals("com.fankes.coloros.notify", appContext.packageName) 23 | } 24 | } -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 12 | 15 | 16 | 26 | 27 | 30 | 33 | 36 | 39 | 40 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 70 | 71 | 76 | 77 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | -------------------------------------------------------------------------------- /app/src/main/ic_launcher-playstore.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fankes/ColorOSNotifyIcon/492d0dad6725f039d08d977f8a2ff81eb25451f6/app/src/main/ic_launcher-playstore.png -------------------------------------------------------------------------------- /app/src/main/java/com/fankes/coloros/notify/application/CNNApplication.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * ColorOSNotifyIcon - Optimize notification icons for ColorOS and adapt to native notification icon specifications. 3 | * Copyright (C) 20174 Fankes Studio(qzmmcn@163.com) 4 | * https://github.com/fankes/ColorOSNotifyIcon 5 | * 6 | * This software is non-free but opensource software: you can redistribute it 7 | * and/or modify it under the terms of the GNU Affero General Public License 8 | * as published by the Free Software Foundation; either 9 | * version 3 of the License, or any later version. 10 | *

11 | * 12 | * This software is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | * Affero General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU Affero General Public License 18 | * and eula along with this software. If not, see 19 | * 20 | * 21 | * This file is created by fankes on 2022/1/24. 22 | */ 23 | @file:Suppress("unused") 24 | 25 | package com.fankes.coloros.notify.application 26 | 27 | import androidx.appcompat.app.AppCompatDelegate 28 | import com.fankes.coloros.notify.data.ConfigData 29 | import com.highcapable.yukihookapi.hook.xposed.application.ModuleApplication 30 | 31 | class CNNApplication : ModuleApplication() { 32 | 33 | override fun onCreate() { 34 | super.onCreate() 35 | /** 跟随系统夜间模式 */ 36 | AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM) 37 | /** 装载存储控制类 */ 38 | ConfigData.init(instance = this) 39 | } 40 | } -------------------------------------------------------------------------------- /app/src/main/java/com/fankes/coloros/notify/bean/IconDataBean.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * ColorOSNotifyIcon - Optimize notification icons for ColorOS and adapt to native notification icon specifications. 3 | * Copyright (C) 20174 Fankes Studio(qzmmcn@163.com) 4 | * https://github.com/fankes/ColorOSNotifyIcon 5 | * 6 | * This software is non-free but opensource software: you can redistribute it 7 | * and/or modify it under the terms of the GNU Affero General Public License 8 | * as published by the Free Software Foundation; either 9 | * version 3 of the License, or any later version. 10 | *

11 | * 12 | * This software is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | * Affero General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU Affero General Public License 18 | * and eula along with this software. If not, see 19 | * 20 | * 21 | * This file is created by fankes on 2022/1/30. 22 | */ 23 | package com.fankes.coloros.notify.bean 24 | 25 | import android.graphics.Bitmap 26 | import com.fankes.coloros.notify.utils.factory.base64 27 | import java.io.Serializable 28 | 29 | /** 30 | * 通知栏小图标 bean 31 | * @param appName APP 名称 - 仅限默认语言区域 32 | * @param packageName 包名 33 | * @param iconBitmap 图标位图 34 | * @param iconColor 通知栏中显示的图标颜色 - 设置为 0 使用系统默认颜色 35 | * @param contributorName 贡献者昵称 36 | * @param isEnabled 是否默认启用替换彩色图标 - 关闭后将全部停止替换 37 | * @param isEnabledAll 是否默认启用替换全部图标 38 | */ 39 | data class IconDataBean( 40 | var appName: String, 41 | var packageName: String, 42 | var iconBitmap: Bitmap, 43 | var iconColor: Int = 0, 44 | var contributorName: String, 45 | var isEnabled: Boolean, 46 | var isEnabledAll: Boolean, 47 | ) : Serializable { 48 | fun toEnabledName() = ("$appName$packageName").base64 + "_enable" 49 | fun toEnabledAllName() = ("$appName$packageName").base64 + "_enable_all" 50 | override fun toString() = """ 51 | { 52 | "appName": "$appName", 53 | "packageName": "$packageName", 54 | "iconBitmap": "${iconBitmap.base64}", 55 | "iconColor": "#${Integer.toHexString(iconColor)}", 56 | "contributorName": "$contributorName", 57 | "isEnabled": $isEnabled, 58 | "isEnabledAll": $isEnabledAll 59 | } 60 | """.trimIndent() 61 | } -------------------------------------------------------------------------------- /app/src/main/java/com/fankes/coloros/notify/const/ConstFactory.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * ColorOSNotifyIcon - Optimize notification icons for ColorOS and adapt to native notification icon specifications. 3 | * Copyright (C) 20174 Fankes Studio(qzmmcn@163.com) 4 | * https://github.com/fankes/ColorOSNotifyIcon 5 | * 6 | * This software is non-free but opensource software: you can redistribute it 7 | * and/or modify it under the terms of the GNU Affero General Public License 8 | * as published by the Free Software Foundation; either 9 | * version 3 of the License, or any later version. 10 | *

11 | * 12 | * This software is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | * Affero General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU Affero General Public License 18 | * and eula along with this software. If not, see 19 | * 20 | * 21 | * This file is created by fankes on 2023/2/2. 22 | */ 23 | @file:Suppress("MemberVisibilityCanBePrivate") 24 | 25 | package com.fankes.coloros.notify.const 26 | 27 | import com.fankes.coloros.notify.generated.AppProperties 28 | import com.fankes.coloros.notify.wrapper.BuildConfigWrapper 29 | 30 | /** 31 | * 包名常量定义类 32 | */ 33 | object PackageName { 34 | 35 | /** 系统框架 */ 36 | const val SYSTEM_FRAMEWORK = "android" 37 | 38 | /** 系统界面 (系统 UI) */ 39 | const val SYSTEMUI = "com.android.systemui" 40 | } 41 | 42 | /** 43 | * 通知图标优化名单同步方式定义类 44 | */ 45 | object IconRuleSourceSyncType { 46 | 47 | /** GitHub Raw (代理 - GitHub Proxy) */ 48 | const val GITHUB_RAW_PROXY_1 = 500 49 | 50 | /** GitHub Raw (代理 - 7ED Services) */ 51 | const val GITHUB_RAW_PROXY_2 = 1000 52 | 53 | /** GitHub Raw (直连) */ 54 | const val GITHUB_RAW_DIRECT = 2000 55 | 56 | /** 自定义地址 */ 57 | const val CUSTOM_URL = 3000 58 | } 59 | 60 | /** 61 | * 模块版本常量定义类 62 | */ 63 | object ModuleVersion { 64 | 65 | /** 当前 GitHub 提交的 ID (CI 自动构建) */ 66 | const val GITHUB_COMMIT_ID = AppProperties.GITHUB_CI_COMMIT_ID 67 | 68 | /** 版本名称 */ 69 | const val NAME = BuildConfigWrapper.VERSION_NAME 70 | 71 | /** 版本号 */ 72 | const val CODE = BuildConfigWrapper.VERSION_CODE 73 | 74 | /** 是否为 CI 自动构建版本 */ 75 | val isCiMode = GITHUB_COMMIT_ID.isNotBlank() 76 | 77 | /** 当前版本名称后缀 */ 78 | val suffix = GITHUB_COMMIT_ID.let { if (it.isNotBlank()) "-$it" else "" } 79 | 80 | override fun toString() = "$NAME$suffix($CODE)" 81 | } -------------------------------------------------------------------------------- /app/src/main/java/com/fankes/coloros/notify/data/ConfigData.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * ColorOSNotifyIcon - Optimize notification icons for ColorOS and adapt to native notification icon specifications. 3 | * Copyright (C) 20174 Fankes Studio(qzmmcn@163.com) 4 | * https://github.com/fankes/ColorOSNotifyIcon 5 | * 6 | * This software is non-free but opensource software: you can redistribute it 7 | * and/or modify it under the terms of the GNU Affero General Public License 8 | * as published by the Free Software Foundation; either 9 | * version 3 of the License, or any later version. 10 | *

11 | * 12 | * This software is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | * Affero General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU Affero General Public License 18 | * and eula along with this software. If not, see 19 | * 20 | * 21 | * This file is created by fankes on 2023/2/2. 22 | */ 23 | @file:Suppress("MemberVisibilityCanBePrivate") 24 | 25 | package com.fankes.coloros.notify.data 26 | 27 | import android.content.Context 28 | import com.fankes.coloros.notify.const.IconRuleSourceSyncType 29 | import com.fankes.coloros.notify.utils.factory.isUpperOfAndroidS 30 | import com.highcapable.yukihookapi.hook.factory.prefs 31 | import com.highcapable.yukihookapi.hook.log.YLog 32 | import com.highcapable.yukihookapi.hook.param.PackageParam 33 | import com.highcapable.yukihookapi.hook.xposed.prefs.data.PrefsData 34 | 35 | /** 36 | * 全局配置存储控制类 37 | */ 38 | object ConfigData { 39 | 40 | /** 启用模块 */ 41 | val ENABLE_MODULE = PrefsData("_enable_module", true) 42 | 43 | /** 启用模块日志 */ 44 | val ENABLE_MODULE_LOG = PrefsData("_enable_module_log", false) 45 | 46 | /** 启用通知图标兼容模式 */ 47 | val ENABLE_COLOR_ICON_COMPAT = PrefsData("_color_icon_compat", false) 48 | 49 | /** 移除开发者选项警告通知 */ 50 | val ENABLE_REMOVE_DEV_NOTIFY = PrefsData("_remove_dev_notify", true) 51 | 52 | /** 移除充电完成通知 */ 53 | val ENABLE_REMOVE_CHANGE_COMPLETE_NOTIFY = PrefsData("_remove_charge_complete_notify", false) 54 | 55 | /** 移除免打扰通知 */ 56 | val ENABLE_REMOVE_DND_ALERT_NOTIFY = PrefsData("_remove_dndalert_notify", false) 57 | 58 | /** 启用 Material 3 通知图标风格 */ 59 | val ENABLE_MD3_NOTIFY_ICON_STYLE = PrefsData("_notify_icon_md3_style", isUpperOfAndroidS) 60 | 61 | /** 通知栏中的通知图标圆角程度 */ 62 | val NOTIFY_ICON_CORNER_SIZE = PrefsData("_notify_icon_corner", 15) 63 | 64 | /** 强制通知栏中的通知图标使用系统着色 */ 65 | val ENABLE_NOTIFY_ICON_FORCE_SYSTEM_COLOR = PrefsData("_notify_icon_force_system_color", false) 66 | 67 | /** 强制通知栏中的通知图标为 APP 图标 */ 68 | val ENABLE_NOTIFY_ICON_FORCE_APP_ICON = PrefsData("_notify_icon_force_app_icon", false) 69 | 70 | /** 启用媒体通知播放时自动展开 */ 71 | val ENABLE_NOTIFY_MEDIA_PANEL_AUTO_EXP = PrefsData("_enable_notify_media_panel_auto_exp", false) 72 | 73 | /** 启用自定义通知面板背景透明度 */ 74 | val ENABLE_NOTIFY_PANEL_ALPHA = PrefsData("_enable_notify_panel_alpha_pst", false) 75 | 76 | /** 自定义通知面板背景透明度 */ 77 | val NOTIFY_PANEL_ALPHA_LEVEL = PrefsData("_notify_panel_alpha_pst", 75) 78 | 79 | /** 启用通知图标优化 */ 80 | val ENABLE_NOTIFY_ICON_FIX = PrefsData("_notify_icon_fix", true) 81 | 82 | /** 使用占位符修补未适配的通知图标 */ 83 | val ENABLE_NOTIFY_ICON_FIX_PLACEHOLDER = PrefsData("_notify_icon_fix_placeholder", false) 84 | 85 | /** 提醒未适配通知图标的新安装应用 */ 86 | val ENABLE_NOTIFY_ICON_FIX_NOTIFY = PrefsData("_notify_icon_fix_notify", true) 87 | 88 | /** 启用通知图标优化名单自动更新 */ 89 | val ENABLE_NOTIFY_ICON_FIX_AUTO = PrefsData("_enable_notify_icon_fix_auto", true) 90 | 91 | /** 通知图标优化名单自动更新时间 */ 92 | val NOTIFY_ICON_FIX_AUTO_TIME = PrefsData("_notify_icon_fix_auto_time", "07:00") 93 | 94 | /** 通知图标优化适配数据 */ 95 | val NOTIFY_ICONS_DATA = PrefsData("_notify_icon_datas", "") 96 | 97 | /** 通知图标优化名单同步方式 */ 98 | val ICON_RULE_SOURCE_SYNC_TYPE = PrefsData("_rule_source_sync_way", IconRuleSourceSyncType.GITHUB_RAW_PROXY_1) 99 | 100 | /** 通知图标优化名单同步地址 */ 101 | val ICON_RULE_SOURCE_SYNC_CUSTOM_URL = PrefsData("_rule_source_sync_way_custom_url", "") 102 | 103 | /** 当前实例 - [Context] or [PackageParam] */ 104 | private var instance: Any? = null 105 | 106 | /** 107 | * 初始化存储控制类 108 | * @param instance 实例 - 只能是 [Context] or [PackageParam] 109 | * @throws IllegalStateException 如果类型错误 110 | */ 111 | fun init(instance: Any) { 112 | when (instance) { 113 | is Context, is PackageParam -> this.instance = instance 114 | else -> error("Unknown type for init ConfigData") 115 | } 116 | } 117 | 118 | /** 119 | * 读取 [String] 数据 120 | * @param data 键值数据模板 121 | * @return [String] 122 | */ 123 | private fun getString(data: PrefsData) = when (instance) { 124 | is Context -> (instance as Context).prefs().get(data) 125 | is PackageParam -> (instance as PackageParam).prefs.get(data) 126 | else -> error("Unknown type for get prefs data") 127 | } 128 | 129 | /** 130 | * 存入 [String] 数据 131 | * @param data 键值数据模板 132 | * @param value 键值内容 133 | */ 134 | private fun putString(data: PrefsData, value: String) { 135 | when (instance) { 136 | is Context -> (instance as Context).prefs().edit { put(data, value) } 137 | is PackageParam -> YLog.warn("Not support for this method") 138 | else -> error("Unknown type for put prefs data") 139 | } 140 | } 141 | 142 | /** 143 | * 读取 [Int] 数据 144 | * @param data 键值数据模板 145 | * @return [Int] 146 | */ 147 | internal fun getInt(data: PrefsData) = when (instance) { 148 | is Context -> (instance as Context).prefs().get(data) 149 | is PackageParam -> (instance as PackageParam).prefs.get(data) 150 | else -> error("Unknown type for get prefs data") 151 | } 152 | 153 | /** 154 | * 存入 [Int] 数据 155 | * @param data 键值数据模板 156 | * @param value 键值内容 157 | */ 158 | internal fun putInt(data: PrefsData, value: Int) { 159 | when (instance) { 160 | is Context -> (instance as Context).prefs().edit { put(data, value) } 161 | is PackageParam -> YLog.warn("Not support for this method") 162 | else -> error("Unknown type for put prefs data") 163 | } 164 | } 165 | 166 | /** 167 | * 读取 [Boolean] 数据 168 | * @param data 键值数据模板 169 | * @return [Boolean] 170 | */ 171 | internal fun getBoolean(data: PrefsData) = when (instance) { 172 | is Context -> (instance as Context).prefs().get(data) 173 | is PackageParam -> (instance as PackageParam).prefs.get(data) 174 | else -> error("Unknown type for get prefs data") 175 | } 176 | 177 | /** 178 | * 存入 [Boolean] 数据 179 | * @param data 键值数据模板 180 | * @param value 键值内容 181 | */ 182 | internal fun putBoolean(data: PrefsData, value: Boolean) { 183 | when (instance) { 184 | is Context -> (instance as Context).prefs().edit { put(data, value) } 185 | is PackageParam -> YLog.warn("Not support for this method") 186 | else -> error("Unknown type for put prefs data") 187 | } 188 | } 189 | 190 | /** 191 | * 是否启用模块 192 | * @return [Boolean] 193 | */ 194 | var isEnableModule 195 | get() = getBoolean(ENABLE_MODULE) 196 | set(value) { 197 | putBoolean(ENABLE_MODULE, value) 198 | } 199 | 200 | /** 201 | * 是否启用模块日志 202 | * @return [Boolean] 203 | */ 204 | var isEnableModuleLog 205 | get() = getBoolean(ENABLE_MODULE_LOG) 206 | set(value) { 207 | putBoolean(ENABLE_MODULE_LOG, value) 208 | } 209 | 210 | /** 211 | * 是否启用通知图标兼容模式 212 | * @return [Boolean] 213 | */ 214 | var isEnableColorIconCompat 215 | get() = getBoolean(ENABLE_COLOR_ICON_COMPAT) 216 | set(value) { 217 | putBoolean(ENABLE_COLOR_ICON_COMPAT, value) 218 | } 219 | 220 | /** 221 | * 是否移除开发者选项警告通知 222 | * @return [Boolean] 223 | */ 224 | var isEnableRemoveDevNotify 225 | get() = getBoolean(ENABLE_REMOVE_DEV_NOTIFY) 226 | set(value) { 227 | putBoolean(ENABLE_REMOVE_DEV_NOTIFY, value) 228 | } 229 | 230 | /** 231 | * 是否移除充电完成通知 232 | * @return [Boolean] 233 | */ 234 | var isEnableRemoveChangeCompleteNotify 235 | get() = getBoolean(ENABLE_REMOVE_CHANGE_COMPLETE_NOTIFY) 236 | set(value) { 237 | putBoolean(ENABLE_REMOVE_CHANGE_COMPLETE_NOTIFY, value) 238 | } 239 | 240 | /** 241 | * 是否移除免打扰通知 242 | * @return [Boolean] 243 | */ 244 | var isEnableRemoveDndAlertNotify 245 | get() = getBoolean(ENABLE_REMOVE_DND_ALERT_NOTIFY) 246 | set(value) { 247 | putBoolean(ENABLE_REMOVE_DND_ALERT_NOTIFY, value) 248 | } 249 | 250 | /** 251 | * 是否启用 material 3 通知图标风格 252 | * @return [Boolean] 253 | */ 254 | var isEnableMd3NotifyIconStyle 255 | get() = getBoolean(ENABLE_MD3_NOTIFY_ICON_STYLE) 256 | set(value) { 257 | putBoolean(ENABLE_MD3_NOTIFY_ICON_STYLE, value) 258 | } 259 | 260 | /** 261 | * 通知栏中的通知图标圆角程度 262 | * @return [Int] 263 | */ 264 | var notifyIconCornerSize 265 | get() = getInt(NOTIFY_ICON_CORNER_SIZE) 266 | set(value) { 267 | putInt(NOTIFY_ICON_CORNER_SIZE, value) 268 | } 269 | 270 | /** 271 | * 是否强制通知栏中的通知图标使用系统着色 272 | * @return [Boolean] 273 | */ 274 | var isEnableNotifyIconForceSystemColor 275 | get() = getBoolean(ENABLE_NOTIFY_ICON_FORCE_SYSTEM_COLOR) 276 | set(value) { 277 | putBoolean(ENABLE_NOTIFY_ICON_FORCE_SYSTEM_COLOR, value) 278 | } 279 | 280 | /** 281 | * 是否强制通知栏中的通知图标为 APP 图标 282 | * @return [Boolean] 283 | */ 284 | var isEnableNotifyIconForceAppIcon 285 | get() = getBoolean(ENABLE_NOTIFY_ICON_FORCE_APP_ICON) 286 | set(value) { 287 | putBoolean(ENABLE_NOTIFY_ICON_FORCE_APP_ICON, value) 288 | } 289 | 290 | /** 291 | * 是否启用媒体通知播放时自动展开 292 | * @return [Boolean] 293 | */ 294 | var isEnableNotifyMediaPanelAutoExp 295 | get() = getBoolean(ENABLE_NOTIFY_MEDIA_PANEL_AUTO_EXP) 296 | set(value) { 297 | putBoolean(ENABLE_NOTIFY_MEDIA_PANEL_AUTO_EXP, value) 298 | } 299 | 300 | /** 301 | * 是否启用自定义通知面板背景透明度 302 | * @return [Boolean] 303 | */ 304 | var isEnableNotifyPanelAlpha 305 | get() = getBoolean(ENABLE_NOTIFY_PANEL_ALPHA) 306 | set(value) { 307 | putBoolean(ENABLE_NOTIFY_PANEL_ALPHA, value) 308 | } 309 | 310 | /** 311 | * 自定义通知面板背景透明度 312 | * @return [Int] 313 | */ 314 | var notifyPanelAlphaLevel 315 | get() = getInt(NOTIFY_PANEL_ALPHA_LEVEL) 316 | set(value) { 317 | putInt(NOTIFY_PANEL_ALPHA_LEVEL, value) 318 | } 319 | 320 | /** 321 | * 是否启用通知图标优化 322 | * @return [Boolean] 323 | */ 324 | var isEnableNotifyIconFix 325 | get() = getBoolean(ENABLE_NOTIFY_ICON_FIX) 326 | set(value) { 327 | putBoolean(ENABLE_NOTIFY_ICON_FIX, value) 328 | } 329 | 330 | /** 331 | * 是否使用占位符修补未适配的通知图标 332 | * @return [Boolean] 333 | */ 334 | var isEnableNotifyIconFixPlaceholder 335 | get() = getBoolean(ENABLE_NOTIFY_ICON_FIX_PLACEHOLDER) 336 | set(value) { 337 | putBoolean(ENABLE_NOTIFY_ICON_FIX_PLACEHOLDER, value) 338 | } 339 | 340 | /** 341 | * 是否提醒未适配通知图标的新安装应用 342 | * @return [Boolean] 343 | */ 344 | var isEnableNotifyIconFixNotify 345 | get() = getBoolean(ENABLE_NOTIFY_ICON_FIX_NOTIFY) 346 | set(value) { 347 | putBoolean(ENABLE_NOTIFY_ICON_FIX_NOTIFY, value) 348 | } 349 | 350 | /** 351 | * 是否启用通知图标优化名单自动更新 352 | * @return [Boolean] 353 | */ 354 | var isEnableNotifyIconFixAuto 355 | get() = getBoolean(ENABLE_NOTIFY_ICON_FIX_AUTO) 356 | set(value) { 357 | putBoolean(ENABLE_NOTIFY_ICON_FIX_AUTO, value) 358 | } 359 | 360 | /** 361 | * 通知图标优化名单自动更新时间 362 | * @return [String] 363 | */ 364 | var notifyIconFixAutoTime 365 | get() = getString(NOTIFY_ICON_FIX_AUTO_TIME) 366 | set(value) { 367 | putString(NOTIFY_ICON_FIX_AUTO_TIME, value) 368 | } 369 | 370 | /** 371 | * 通知图标优化名单同步方式 372 | * @return [Int] 373 | */ 374 | var iconRuleSourceSyncType 375 | get() = getInt(ICON_RULE_SOURCE_SYNC_TYPE) 376 | set(value) { 377 | putInt(ICON_RULE_SOURCE_SYNC_TYPE, value) 378 | } 379 | 380 | /** 381 | * 通知图标优化名单同步地址 382 | * @return [String] 383 | */ 384 | var iconRuleSourceSyncCustomUrl 385 | get() = getString(ICON_RULE_SOURCE_SYNC_CUSTOM_URL) 386 | set(value) { 387 | putString(ICON_RULE_SOURCE_SYNC_CUSTOM_URL, value) 388 | } 389 | } -------------------------------------------------------------------------------- /app/src/main/java/com/fankes/coloros/notify/data/factory/CompoundButtonFactory.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * ColorOSNotifyIcon - Optimize notification icons for ColorOS and adapt to native notification icon specifications. 3 | * Copyright (C) 20174 Fankes Studio(qzmmcn@163.com) 4 | * https://github.com/fankes/ColorOSNotifyIcon 5 | * 6 | * This software is non-free but opensource software: you can redistribute it 7 | * and/or modify it under the terms of the GNU Affero General Public License 8 | * as published by the Free Software Foundation; either 9 | * version 3 of the License, or any later version. 10 | *

11 | * 12 | * This software is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | * Affero General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU Affero General Public License 18 | * and eula along with this software. If not, see 19 | * 20 | * 21 | * This file is created by fankes on 2023/2/3. 22 | */ 23 | @file:Suppress("unused", "MemberVisibilityCanBePrivate") 24 | 25 | package com.fankes.coloros.notify.data.factory 26 | 27 | import android.widget.CompoundButton 28 | import com.fankes.coloros.notify.data.ConfigData 29 | import com.highcapable.yukihookapi.hook.xposed.prefs.data.PrefsData 30 | 31 | /** 32 | * 绑定到 [CompoundButton] 自动设置选中状态 33 | * @param data 键值数据模板 34 | * @param initiate 方法体 35 | */ 36 | fun CompoundButton.bind(data: PrefsData, initiate: CompoundButtonDataBinder.(CompoundButton) -> Unit = {}) { 37 | val binder = CompoundButtonDataBinder(button = this).also { initiate(it, this) } 38 | isChecked = ConfigData.getBoolean(data).also { binder.initializeCallback?.invoke(it) } 39 | binder.applyChangesCallback = { ConfigData.putBoolean(data, it) } 40 | setOnCheckedChangeListener { button, isChecked -> 41 | if (button.isPressed) { 42 | if (binder.isAutoApplyChanges) binder.applyChangesCallback?.invoke(isChecked) 43 | binder.changedCallback?.invoke(isChecked) 44 | } 45 | } 46 | } 47 | 48 | /** 49 | * [CompoundButton] 数据绑定管理器实例 50 | * @param button 当前实例 51 | */ 52 | class CompoundButtonDataBinder(private val button: CompoundButton) { 53 | 54 | /** 状态初始化回调事件 */ 55 | internal var initializeCallback: ((Boolean) -> Unit)? = null 56 | 57 | /** 状态改变回调事件 */ 58 | internal var changedCallback: ((Boolean) -> Unit)? = null 59 | 60 | /** 应用更改回调事件 */ 61 | internal var applyChangesCallback: ((Boolean) -> Unit)? = null 62 | 63 | /** 是否启用自动应用更改 */ 64 | var isAutoApplyChanges = true 65 | 66 | /** 67 | * 监听状态初始化 68 | * @param result 回调结果 69 | */ 70 | fun onInitialize(result: (Boolean) -> Unit) { 71 | initializeCallback = result 72 | } 73 | 74 | /** 75 | * 监听状态改变 76 | * @param result 回调结果 77 | */ 78 | fun onChanged(result: (Boolean) -> Unit) { 79 | changedCallback = result 80 | } 81 | 82 | /** 重新初始化 */ 83 | fun reinitialize() { 84 | initializeCallback?.invoke(button.isChecked) 85 | } 86 | 87 | /** 应用更改并重新初始化 */ 88 | fun applyChangesAndReinitialize() { 89 | applyChanges() 90 | reinitialize() 91 | } 92 | 93 | /** 应用更改 */ 94 | fun applyChanges() { 95 | applyChangesCallback?.invoke(button.isChecked) 96 | } 97 | 98 | /** 取消更改 */ 99 | fun cancelChanges() { 100 | button.isChecked = button.isChecked.not() 101 | } 102 | } -------------------------------------------------------------------------------- /app/src/main/java/com/fankes/coloros/notify/data/factory/SeekBarFactory.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * ColorOSNotifyIcon - Optimize notification icons for ColorOS and adapt to native notification icon specifications. 3 | * Copyright (C) 20174 Fankes Studio(qzmmcn@163.com) 4 | * https://github.com/fankes/ColorOSNotifyIcon 5 | * 6 | * This software is non-free but opensource software: you can redistribute it 7 | * and/or modify it under the terms of the GNU Affero General Public License 8 | * as published by the Free Software Foundation; either 9 | * version 3 of the License, or any later version. 10 | *

11 | * 12 | * This software is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | * Affero General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU Affero General Public License 18 | * and eula along with this software. If not, see 19 | * 20 | * 21 | * This file is created by fankes on 2023/2/4. 22 | */ 23 | @file:Suppress("SetTextI18n") 24 | 25 | package com.fankes.coloros.notify.data.factory 26 | 27 | import android.widget.SeekBar 28 | import android.widget.TextView 29 | import com.fankes.coloros.notify.data.ConfigData 30 | import com.highcapable.yukihookapi.hook.xposed.prefs.data.PrefsData 31 | 32 | /** 33 | * 绑定到 [SeekBar] 自动设置进度与 [TextView] 34 | * @param data 键值数据模板 35 | * @param textView 文本框 36 | * @param suffix 文本显示的后缀 - 默认空 37 | * @param onChange 当改变停止时回调 38 | */ 39 | fun SeekBar.bind(data: PrefsData, textView: TextView, suffix: String = "", onChange: (Int) -> Unit = {}) { 40 | ConfigData.getInt(data).also { value -> 41 | textView.text = "$value$suffix" 42 | progress = value 43 | } 44 | setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener { 45 | 46 | override fun onProgressChanged(seekBar: SeekBar?, progress: Int, fromUser: Boolean) { 47 | textView.text = "$progress$suffix" 48 | } 49 | 50 | override fun onStopTrackingTouch(seekBar: SeekBar) { 51 | ConfigData.putInt(data, seekBar.progress) 52 | onChange(seekBar.progress) 53 | } 54 | 55 | override fun onStartTrackingTouch(seekBar: SeekBar?) {} 56 | }) 57 | } -------------------------------------------------------------------------------- /app/src/main/java/com/fankes/coloros/notify/hook/HookEntry.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * ColorOSNotifyIcon - Optimize notification icons for ColorOS and adapt to native notification icon specifications. 3 | * Copyright (C) 20174 Fankes Studio(qzmmcn@163.com) 4 | * https://github.com/fankes/ColorOSNotifyIcon 5 | * 6 | * This software is non-free but opensource software: you can redistribute it 7 | * and/or modify it under the terms of the GNU Affero General Public License 8 | * as published by the Free Software Foundation; either 9 | * version 3 of the License, or any later version. 10 | *

11 | * 12 | * This software is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | * Affero General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU Affero General Public License 18 | * and eula along with this software. If not, see 19 | * 20 | * 21 | * This file is created by fankes on 2022/2/26. 22 | */ 23 | package com.fankes.coloros.notify.hook 24 | 25 | import com.fankes.coloros.notify.const.PackageName 26 | import com.fankes.coloros.notify.data.ConfigData 27 | import com.fankes.coloros.notify.hook.entity.FrameworkHooker 28 | import com.fankes.coloros.notify.hook.entity.SystemUIHooker 29 | import com.fankes.coloros.notify.utils.factory.isNotColorOS 30 | import com.highcapable.yukihookapi.annotation.xposed.InjectYukiHookWithXposed 31 | import com.highcapable.yukihookapi.hook.factory.configs 32 | import com.highcapable.yukihookapi.hook.factory.encase 33 | import com.highcapable.yukihookapi.hook.log.YLog 34 | import com.highcapable.yukihookapi.hook.xposed.proxy.IYukiHookXposedInit 35 | 36 | @InjectYukiHookWithXposed 37 | object HookEntry : IYukiHookXposedInit { 38 | 39 | override fun onInit() = configs { 40 | debugLog { 41 | tag = "ColorOSNotifyIcon" 42 | isRecord = true 43 | elements(PRIORITY) 44 | } 45 | isDebug = false 46 | } 47 | 48 | override fun onHook() = encase { 49 | if (isNotColorOS) return@encase YLog.warn("Aborted Hook -> This System is not ColorOS") 50 | loadSystem(FrameworkHooker) 51 | loadApp(PackageName.SYSTEMUI) { 52 | ConfigData.init(instance = this) 53 | if (ConfigData.isEnableModule) 54 | loadHooker(SystemUIHooker) 55 | else YLog.warn("Aborted Hook -> Hook Closed") 56 | } 57 | } 58 | } -------------------------------------------------------------------------------- /app/src/main/java/com/fankes/coloros/notify/hook/entity/FrameworkHooker.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * ColorOSNotifyIcon - Optimize notification icons for ColorOS and adapt to native notification icon specifications. 3 | * Copyright (C) 20174 Fankes Studio(qzmmcn@163.com) 4 | * https://github.com/fankes/ColorOSNotifyIcon 5 | * 6 | * This software is non-free but opensource software: you can redistribute it 7 | * and/or modify it under the terms of the GNU Affero General Public License 8 | * as published by the Free Software Foundation; either 9 | * version 3 of the License, or any later version. 10 | *

11 | * 12 | * This software is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | * Affero General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU Affero General Public License 18 | * and eula along with this software. If not, see 19 | * 20 | * 21 | * This file is created by Nep-Timeline on 2025/5/27. 22 | */ 23 | package com.fankes.coloros.notify.hook.entity 24 | 25 | import com.highcapable.yukihookapi.hook.entity.YukiBaseHooker 26 | import com.highcapable.yukihookapi.hook.factory.method 27 | import com.highcapable.yukihookapi.hook.type.android.NotificationClass 28 | import com.highcapable.yukihookapi.hook.type.java.BooleanType 29 | import com.highcapable.yukihookapi.hook.type.java.StringClass 30 | 31 | /** 32 | * 系统框架核心 Hook 类 33 | */ 34 | object FrameworkHooker : YukiBaseHooker() { 35 | 36 | /** ColorOS 存在的类 - 旧版本不存在 */ 37 | private val OplusNotificationFixHelperClass by lazyClassOrNull("com.android.server.notification.OplusNotificationFixHelper") 38 | 39 | override fun onHook() { 40 | /** 拦截 ColorOS 覆盖应用通知图标 */ 41 | OplusNotificationFixHelperClass?.method { 42 | name = "fixSmallIcon" 43 | param(NotificationClass, StringClass, StringClass, BooleanType) 44 | }?.ignored()?.hook()?.intercept() 45 | } 46 | } -------------------------------------------------------------------------------- /app/src/main/java/com/fankes/coloros/notify/param/IconPackParams.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * ColorOSNotifyIcon - Optimize notification icons for ColorOS and adapt to native notification icon specifications. 3 | * Copyright (C) 20174 Fankes Studio(qzmmcn@163.com) 4 | * https://github.com/fankes/ColorOSNotifyIcon 5 | * 6 | * This software is non-free but opensource software: you can redistribute it 7 | * and/or modify it under the terms of the GNU Affero General Public License 8 | * as published by the Free Software Foundation; either 9 | * version 3 of the License, or any later version. 10 | *

11 | * 12 | * This software is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | * Affero General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU Affero General Public License 18 | * and eula along with this software. If not, see 19 | * 20 | * 21 | * This file is created by fankes on 2022/1/24. 22 | */ 23 | @file:Suppress("MemberVisibilityCanBePrivate") 24 | 25 | package com.fankes.coloros.notify.param 26 | 27 | import android.content.Context 28 | import android.graphics.Color 29 | import com.fankes.coloros.notify.bean.IconDataBean 30 | import com.fankes.coloros.notify.data.ConfigData 31 | import com.fankes.coloros.notify.utils.factory.bitmap 32 | import com.fankes.coloros.notify.utils.factory.safeOf 33 | import com.fankes.coloros.notify.utils.factory.safeOfNan 34 | import com.fankes.coloros.notify.utils.factory.safeOfNull 35 | import com.fankes.coloros.notify.utils.factory.snake 36 | import com.highcapable.yukihookapi.hook.factory.prefs 37 | import com.highcapable.yukihookapi.hook.param.PackageParam 38 | import org.json.JSONArray 39 | import org.json.JSONObject 40 | 41 | /** 42 | * 通知栏小图标适配类 43 | * 44 | * 国内 APP 不规范的图标将由这里完成其自定义单色小图标绘制 45 | * @param context 实例 - 二选一 46 | * @param param 实例 - 二选一 47 | */ 48 | class IconPackParams(private val context: Context? = null, private val param: PackageParam? = null) { 49 | 50 | /** 51 | * 已存储的 JSON 数据 52 | * @return [String] 53 | */ 54 | internal val storageDataJson get() = (context?.prefs() ?: param?.prefs)?.get(ConfigData.NOTIFY_ICONS_DATA) 55 | 56 | /** 57 | * 获取图标数据 58 | * @return [Array] 通知栏小图标数组 59 | */ 60 | val iconDatas 61 | get() = ArrayList().apply { 62 | storageDataJson?.also { 63 | if (it.isNotBlank()) runCatching { 64 | JSONArray(it).also { array -> 65 | for (i in 0 until array.length()) runCatching { 66 | add(convertToBean(array.get(i) as JSONObject)!!) 67 | }.onFailure { context?.snake(msg = "部分规则加载失败") } 68 | } 69 | }.onFailure { context?.snake(msg = "规则加载发生错误") } 70 | } 71 | } 72 | 73 | /** 74 | * 转换为 [IconDataBean] 75 | * @param jsonObject Json 实例 76 | * @return [IconDataBean] or null 77 | */ 78 | private fun convertToBean(jsonObject: JSONObject) = safeOfNull { 79 | jsonObject.let { 80 | IconDataBean( 81 | appName = it.getString("appName"), 82 | packageName = it.getString("packageName"), 83 | isEnabled = it.getBoolean("isEnabled"), 84 | isEnabledAll = it.getBoolean("isEnabledAll"), 85 | iconBitmap = it.getString("iconBitmap").bitmap, 86 | iconColor = safeOfNan { Color.parseColor(it.getString("iconColor")) }, 87 | contributorName = it.getString("contributorName") 88 | ) 89 | } 90 | } 91 | 92 | /** 93 | * 拼接图标数组数据 94 | * @param dataJson1 图标数据 JSON 95 | * @param dataJson2 图标数据 JSON 96 | * @return [String] 拼接后的数据 97 | */ 98 | fun splicingJsonArray(dataJson1: String, dataJson2: String) = safeOf(default = "[]") { 99 | dataJson1.replace("]", "") + "," + dataJson2.replace("[", "") 100 | } 101 | 102 | /** 103 | * 是否不为合法 JSON 104 | * @param json 数据 105 | * @return [Boolean] 106 | */ 107 | fun isNotVaildJson(json: String) = !isJsonArray(json) && !isJsonObject(json) 108 | 109 | /** 110 | * 是否为 JSON 数组 111 | * @param json 数据 112 | * @return [Boolean] 113 | */ 114 | fun isJsonArray(json: String) = json.trim().let { it.startsWith("[") && it.endsWith("]") } 115 | 116 | /** 117 | * 是否为 JSON 实例 118 | * @param json 数据 119 | * @return [Boolean] 120 | */ 121 | fun isJsonObject(json: String) = json.trim().let { it.startsWith("{") && it.endsWith("}") } 122 | 123 | /** 124 | * 是否为异常地址 125 | * @param json 数据 126 | * @return [Boolean] 127 | */ 128 | fun isHackString(json: String) = json.contains("Checking your browser before accessing") 129 | 130 | /** 131 | * 比较图标数据不相等 132 | * @param dataJson 图标数据 JSON 133 | * @return [Boolean] 是否不相等 134 | */ 135 | fun isCompareDifferent(dataJson: String) = storageDataJson?.trim() != dataJson.trim() 136 | 137 | /** 138 | * 保存图标数据 139 | * @param dataJson 图标数据 JSON 140 | */ 141 | fun save(dataJson: String) = context?.prefs()?.edit { put(ConfigData.NOTIFY_ICONS_DATA, dataJson) } 142 | } -------------------------------------------------------------------------------- /app/src/main/java/com/fankes/coloros/notify/param/factory/DataFactory.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * ColorOSNotifyIcon - Optimize notification icons for ColorOS and adapt to native notification icon specifications. 3 | * Copyright (C) 20174 Fankes Studio(qzmmcn@163.com) 4 | * https://github.com/fankes/ColorOSNotifyIcon 5 | * 6 | * This software is non-free but opensource software: you can redistribute it 7 | * and/or modify it under the terms of the GNU Affero General Public License 8 | * as published by the Free Software Foundation; either 9 | * version 3 of the License, or any later version. 10 | *

11 | * 12 | * This software is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | * Affero General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU Affero General Public License 18 | * and eula along with this software. If not, see 19 | * 20 | * 21 | * This file is created by fankes on 2022/2/15. 22 | * This file is Modified by fankes on 2023/2/3. 23 | */ 24 | package com.fankes.coloros.notify.param.factory 25 | 26 | import android.content.Context 27 | import com.fankes.coloros.notify.bean.IconDataBean 28 | import com.highcapable.yukihookapi.hook.factory.prefs 29 | import com.highcapable.yukihookapi.hook.param.PackageParam 30 | 31 | /** 32 | * 获取此 APP 的通知图标是否被 Hook 33 | * @param bean 图标 bean 34 | */ 35 | fun PackageParam.isAppNotifyHookOf(bean: IconDataBean) = prefs.getBoolean(bean.toEnabledName(), bean.isEnabled) 36 | 37 | /** 38 | * 获取此 APP 的通知图标是否被 Hook 39 | * @param bean 图标 bean 40 | */ 41 | fun Context.isAppNotifyHookOf(bean: IconDataBean) = prefs().getBoolean(bean.toEnabledName(), bean.isEnabled) 42 | 43 | /** 44 | * 设置 Hook 此 APP 的通知图标 45 | * @param bean 图标 bean 46 | * @param isHook 是否 Hook 47 | */ 48 | fun Context.putAppNotifyHookOf(bean: IconDataBean, isHook: Boolean) = prefs().edit { putBoolean(bean.toEnabledName(), isHook) } 49 | 50 | /** 51 | * 获取此 APP 的通知图标是否被全部 Hook 52 | * @param bean 图标 bean 53 | */ 54 | fun PackageParam.isAppNotifyHookAllOf(bean: IconDataBean) = prefs.getBoolean(bean.toEnabledAllName(), bean.isEnabledAll) 55 | 56 | /** 57 | * 获取此 APP 的通知图标是否被全部 Hook 58 | * @param bean 图标 bean 59 | */ 60 | fun Context.isAppNotifyHookAllOf(bean: IconDataBean) = prefs().getBoolean(bean.toEnabledAllName(), bean.isEnabledAll) 61 | 62 | /** 63 | * 设置全部 Hook 此 APP 的通知图标 64 | * @param bean 图标 bean 65 | * @param isHook 是否 Hook 66 | */ 67 | fun Context.putAppNotifyHookAllOf(bean: IconDataBean, isHook: Boolean) = prefs().edit { putBoolean(bean.toEnabledAllName(), isHook) } -------------------------------------------------------------------------------- /app/src/main/java/com/fankes/coloros/notify/service/QuickStartTileService.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * ColorOSNotifyIcon - Optimize notification icons for ColorOS and adapt to native notification icon specifications. 3 | * Copyright (C) 20174 Fankes Studio(qzmmcn@163.com) 4 | * https://github.com/fankes/ColorOSNotifyIcon 5 | * 6 | * This software is non-free but opensource software: you can redistribute it 7 | * and/or modify it under the terms of the GNU Affero General Public License 8 | * as published by the Free Software Foundation; either 9 | * version 3 of the License, or any later version. 10 | *

11 | * 12 | * This software is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | * Affero General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU Affero General Public License 18 | * and eula along with this software. If not, see 19 | * 20 | * 21 | * This file is created by fankes on 2022/3/26. 22 | */ 23 | package com.fankes.coloros.notify.service 24 | 25 | import android.service.quicksettings.TileService 26 | import com.fankes.coloros.notify.ui.activity.ConfigureActivity 27 | import com.fankes.coloros.notify.utils.factory.navigate 28 | 29 | class QuickStartTileService : TileService() { 30 | 31 | override fun onClick() { 32 | super.onClick() 33 | /** 启动通知图标优化列表窗口 */ 34 | navigate() 35 | } 36 | } -------------------------------------------------------------------------------- /app/src/main/java/com/fankes/coloros/notify/ui/activity/ConfigureActivity.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * ColorOSNotifyIcon - Optimize notification icons for ColorOS and adapt to native notification icon specifications. 3 | * Copyright (C) 20174 Fankes Studio(qzmmcn@163.com) 4 | * https://github.com/fankes/ColorOSNotifyIcon 5 | * 6 | * This software is non-free but opensource software: you can redistribute it 7 | * and/or modify it under the terms of the GNU Affero General Public License 8 | * as published by the Free Software Foundation; either 9 | * version 3 of the License, or any later version. 10 | *

11 | * 12 | * This software is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | * Affero General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU Affero General Public License 18 | * and eula along with this software. If not, see 19 | * 20 | * 21 | * This file is created by fankes on 2022/1/30. 22 | */ 23 | @file:Suppress("SetTextI18n", "InflateParams") 24 | 25 | package com.fankes.coloros.notify.ui.activity 26 | 27 | import androidx.core.view.isVisible 28 | import com.fankes.coloros.notify.R 29 | import com.fankes.coloros.notify.bean.IconDataBean 30 | import com.fankes.coloros.notify.data.ConfigData 31 | import com.fankes.coloros.notify.databinding.ActivityConfigBinding 32 | import com.fankes.coloros.notify.databinding.AdapterConfigBinding 33 | import com.fankes.coloros.notify.databinding.DiaIconFilterBinding 34 | import com.fankes.coloros.notify.param.IconPackParams 35 | import com.fankes.coloros.notify.param.factory.isAppNotifyHookAllOf 36 | import com.fankes.coloros.notify.param.factory.isAppNotifyHookOf 37 | import com.fankes.coloros.notify.param.factory.putAppNotifyHookAllOf 38 | import com.fankes.coloros.notify.param.factory.putAppNotifyHookOf 39 | import com.fankes.coloros.notify.ui.activity.base.BaseActivity 40 | import com.fankes.coloros.notify.utils.factory.addOnBackPressedEvent 41 | import com.fankes.coloros.notify.utils.factory.bindAdapter 42 | import com.fankes.coloros.notify.utils.factory.callOnBackPressed 43 | import com.fankes.coloros.notify.utils.factory.colorOf 44 | import com.fankes.coloros.notify.utils.factory.copyToClipboard 45 | import com.fankes.coloros.notify.utils.factory.navigate 46 | import com.fankes.coloros.notify.utils.factory.openBrowser 47 | import com.fankes.coloros.notify.utils.factory.showDialog 48 | import com.fankes.coloros.notify.utils.factory.snake 49 | import com.fankes.coloros.notify.utils.factory.toast 50 | import com.fankes.coloros.notify.utils.tool.IconRuleManagerTool 51 | import com.fankes.coloros.notify.utils.tool.SystemUITool 52 | import com.highcapable.yukihookapi.YukiHookAPI 53 | 54 | class ConfigureActivity : BaseActivity() { 55 | 56 | /** 当前筛选条件 */ 57 | private var filterText = "" 58 | 59 | /** 回调适配器改变 */ 60 | private var onChanged: (() -> Unit)? = null 61 | 62 | /** 回调滚动事件改变 */ 63 | private var onScrollEvent: ((Boolean) -> Unit)? = null 64 | 65 | /** 全部的通知图标优化数据 */ 66 | private var iconAllDatas = ArrayList() 67 | 68 | override fun onCreate() { 69 | /** 检查激活和启用状态 */ 70 | if (YukiHookAPI.Status.isXposedModuleActive.not() || ConfigData.isEnableModule.not()) { 71 | showDialog { 72 | title = "模块不可用" 73 | msg = "模块没有激活或已被停用,你无法使用这里的功能,请先激活或启用模块。" 74 | confirmButton(text = "我知道了") { finish() } 75 | noCancelable() 76 | } 77 | return 78 | } 79 | /** 返回按钮点击事件 */ 80 | binding.titleBackIcon.setOnClickListener { callOnBackPressed() } 81 | /** 刷新适配器结果相关 */ 82 | refreshAdapterResult() 83 | /** 设置上下按钮点击事件 */ 84 | binding.configTitleUp.setOnClickListener { 85 | snake(msg = "滚动到顶部") 86 | onScrollEvent?.invoke(false) 87 | } 88 | binding.configTitleDown.setOnClickListener { 89 | snake(msg = "滚动到底部") 90 | onScrollEvent?.invoke(true) 91 | } 92 | /** 设置过滤按钮点击事件 */ 93 | binding.configTitleFilter.setOnClickListener { 94 | showDialog { 95 | title = "按条件过滤" 96 | binding.iconFiltersEdit.apply { 97 | requestFocus() 98 | invalidate() 99 | if (filterText.isNotBlank()) { 100 | setText(filterText) 101 | setSelection(filterText.length) 102 | } 103 | } 104 | confirmButton { 105 | if (binding.iconFiltersEdit.text.toString().isNotBlank()) { 106 | filterText = binding.iconFiltersEdit.text.toString().trim() 107 | refreshAdapterResult() 108 | } else { 109 | toast(msg = "条件不能为空") 110 | it.performClick() 111 | } 112 | } 113 | cancelButton() 114 | if (filterText.isNotBlank()) 115 | neutralButton(text = "清除条件") { 116 | filterText = "" 117 | refreshAdapterResult() 118 | } 119 | } 120 | } 121 | /** 设置同步列表按钮点击事件 */ 122 | binding.configTitleSync.setOnClickListener { onStartRefresh() } 123 | /** 设置列表元素和 Adapter */ 124 | binding.configListView.apply { 125 | bindAdapter { 126 | onBindDatas { iconDatas } 127 | onBindViews { binding, position -> 128 | iconDatas[position].also { bean -> 129 | binding.adpAppIcon.setImageBitmap(bean.iconBitmap) 130 | (if (bean.iconColor != 0) bean.iconColor else resources.colorOf(R.color.colorTextGray)).also { color -> 131 | binding.adpAppIcon.setColorFilter(color) 132 | binding.adpAppName.setTextColor(color) 133 | } 134 | binding.adpAppName.text = bean.appName 135 | binding.adpAppPkgName.text = bean.packageName 136 | binding.adpCbrName.text = "贡献者:" + bean.contributorName 137 | isAppNotifyHookOf(bean).also { e -> 138 | binding.adpAppOpenSwitch.isChecked = e 139 | binding.adpAppAllSwitch.isEnabled = e 140 | } 141 | binding.adpAppOpenSwitch.setOnCheckedChangeListener { btn, b -> 142 | if (btn.isPressed.not()) return@setOnCheckedChangeListener 143 | putAppNotifyHookOf(bean, b) 144 | binding.adpAppAllSwitch.isEnabled = b 145 | SystemUITool.refreshSystemUI(context = this@ConfigureActivity) 146 | } 147 | binding.adpAppAllSwitch.isChecked = isAppNotifyHookAllOf(bean) 148 | binding.adpAppAllSwitch.setOnCheckedChangeListener { btn, b -> 149 | if (btn.isPressed.not()) return@setOnCheckedChangeListener 150 | fun saveState() { 151 | putAppNotifyHookAllOf(bean, b) 152 | SystemUITool.refreshSystemUI(context = this@ConfigureActivity) 153 | } 154 | if (b) showDialog { 155 | title = "全部替换" 156 | msg = "此功能仅针对严重不遵守规范的 APP 通知图标才需要开启,例如:APP 推送通知后无法识别出现的黑白块图标。\n\n" + 157 | "此功能在一般情况下请保持关闭并跟随在线规则的配置,并不要随意改变此配置," + 158 | "开启后 APP 的通知图标可能会被规则破坏,你确定还要开启吗?" 159 | confirmButton { saveState() } 160 | cancelButton { btn.isChecked = btn.isChecked.not() } 161 | noCancelable() 162 | } else saveState() 163 | } 164 | } 165 | } 166 | }.apply { 167 | setOnItemLongClickListener { _, _, p, _ -> 168 | showDialog { 169 | title = "复制“${iconDatas[p].appName}”的规则" 170 | msg = "是否复制单条规则到剪贴板?" 171 | confirmButton { copyToClipboard(iconDatas[p].toString()) } 172 | cancelButton() 173 | } 174 | true 175 | } 176 | onChanged = { notifyDataSetChanged() } 177 | } 178 | onScrollEvent = { post { setSelection(if (it) iconDatas.lastIndex else 0) } } 179 | } 180 | /** 设置点击事件 */ 181 | binding.configCbrButton.setOnClickListener { 182 | showDialog { 183 | title = "感谢你的贡献" 184 | msg = "通知图标优化名单需要大家的共同维护才能得以完善,请选择你的贡献方式。" 185 | confirmButton(text = "贡献规则") { openBrowser(IconRuleManagerTool.RULES_CONTRIBUTING_URL) } 186 | cancelButton(text = "请求适配") { openBrowser(IconRuleManagerTool.RULES_FEEDBACK_URL) } 187 | neutralButton(text = "暂时不用") 188 | } 189 | } 190 | /** 装载数据 */ 191 | mockLocalData() 192 | /** 更新数据 */ 193 | when { 194 | intent?.getBooleanExtra("isNewAppSupport", false) == true -> 195 | showDialog { 196 | val appName = intent?.getStringExtra("appName") ?: "" 197 | val pkgName = intent?.getStringExtra("pkgName") ?: "" 198 | title = "新安装应用通知图标适配" 199 | msg = "你已安装 $appName($pkgName)\n\n" + 200 | "此应用未在通知优化名单中发现适配数据,若此应用发送的通知为彩色图标," + 201 | "可随时点击本页面下方的“贡献通知图标优化名单”按钮提交贡献或请求适配。\n\n" + 202 | "若你已知晓此应用会遵守原生通知图标规范,可忽略此提示。\n\n" + 203 | "你可以现在立即同步适配列表,以获取最新的适配数据。" 204 | confirmButton(text = "同步列表") { onStartRefresh() } 205 | cancelButton(text = "复制名称+包名") { copyToClipboard(content = "$appName($pkgName)") } 206 | neutralButton(text = "取消") 207 | noCancelable() 208 | } 209 | intent?.getBooleanExtra("isDirectUpdate", false) == true -> onStartRefresh(isByHand = false) 210 | intent?.getBooleanExtra("isShowUpdDialog", true) == true -> onStartRefresh() 211 | } 212 | /** 清除数据 */ 213 | intent?.apply { 214 | removeExtra("isNewAppSupport") 215 | removeExtra("isDirectUpdate") 216 | removeExtra("isShowUpdDialog") 217 | } 218 | /** 设置返回监听事件 */ 219 | addOnBackPressedEvent { 220 | if (MainActivity.isActivityLive.not()) 221 | showDialog { 222 | title = "提示" 223 | msg = "要返回模块主页吗?" 224 | confirmButton { 225 | releaseEventAndBack() 226 | navigate() 227 | } 228 | cancelButton { releaseEventAndBack() } 229 | } 230 | else releaseEventAndBack() 231 | } 232 | } 233 | 234 | /** 235 | * 开始同步 236 | * @param isByHand 是否手动同步 - 默认是 237 | */ 238 | private fun onStartRefresh(isByHand: Boolean = true) { 239 | if (isByHand) 240 | IconRuleManagerTool.syncByHand(context = this) { 241 | filterText = "" 242 | mockLocalData() 243 | } 244 | else 245 | IconRuleManagerTool.sync(context = this) { 246 | filterText = "" 247 | mockLocalData() 248 | } 249 | } 250 | 251 | /** 装载或刷新本地数据 */ 252 | private fun mockLocalData() { 253 | iconAllDatas = IconPackParams(context = this).iconDatas 254 | refreshAdapterResult() 255 | } 256 | 257 | /** 刷新适配器结果相关 */ 258 | private fun refreshAdapterResult() { 259 | onChanged?.invoke() 260 | binding.configTitleCountText.text = 261 | if (filterText.isBlank()) "已适配 ${iconDatas.size} 个 APP 的通知图标" 262 | else "“$filterText” 匹配到 ${iconDatas.size} 个结果" 263 | binding.configListNoDataView.apply { 264 | text = if (iconAllDatas.isEmpty()) "噫,竟然什么都没有~\n请点击右上角同步按钮获取云端数据" else "噫,竟然什么都没找到~" 265 | isVisible = iconDatas.isEmpty() 266 | } 267 | } 268 | 269 | /** 270 | * 当前结果下的图标数组 271 | * @return [Array] 272 | */ 273 | private val iconDatas 274 | get() = if (filterText.isBlank()) iconAllDatas 275 | else iconAllDatas.filter { 276 | it.appName.lowercase().contains(filterText.lowercase()) || it.packageName.lowercase().contains(filterText.lowercase()) 277 | } 278 | } -------------------------------------------------------------------------------- /app/src/main/java/com/fankes/coloros/notify/ui/activity/auto/NotifyIconRuleUpdateActivity.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * ColorOSNotifyIcon - Optimize notification icons for ColorOS and adapt to native notification icon specifications. 3 | * Copyright (C) 20174 Fankes Studio(qzmmcn@163.com) 4 | * https://github.com/fankes/ColorOSNotifyIcon 5 | * 6 | * This software is non-free but opensource software: you can redistribute it 7 | * and/or modify it under the terms of the GNU Affero General Public License 8 | * as published by the Free Software Foundation; either 9 | * version 3 of the License, or any later version. 10 | *

11 | * 12 | * This software is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | * Affero General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU Affero General Public License 18 | * and eula along with this software. If not, see 19 | * 20 | * 21 | * This file is created by fankes on 2022/3/26. 22 | */ 23 | @file:Suppress("DEPRECATION") 24 | 25 | package com.fankes.coloros.notify.ui.activity.auto 26 | 27 | import android.app.Activity 28 | import android.os.Bundle 29 | import android.view.View 30 | import android.view.WindowManager 31 | import com.fankes.coloros.notify.ui.activity.base.BaseActivity 32 | import com.fankes.coloros.notify.utils.factory.delayedRun 33 | import com.fankes.coloros.notify.utils.tool.IconRuleManagerTool 34 | import com.fankes.coloros.notify.utils.tool.SystemUITool 35 | 36 | class NotifyIconRuleUpdateActivity : Activity() { 37 | 38 | override fun onCreate(savedInstanceState: Bundle?) { 39 | super.onCreate(savedInstanceState) 40 | /** 设置透明窗口 */ 41 | window?.decorView?.systemUiVisibility = 42 | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or View.SYSTEM_UI_FLAG_LAYOUT_STABLE 43 | window?.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS) 44 | window?.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION) 45 | /** 检测运行状态 */ 46 | if (BaseActivity.isMainThreadRunning) { 47 | finish() 48 | return 49 | } 50 | /** 拉取云端数据 */ 51 | IconRuleManagerTool.sync(context = this) { 52 | /** 刷新系统界面 */ 53 | SystemUITool.refreshSystemUI() 54 | /** 结束当前窗口 */ 55 | runOnUiThread { delayedRun(ms = 1000) { finish() } } 56 | } 57 | /** 切换到后台 */ 58 | moveTaskToBack(true) 59 | } 60 | } -------------------------------------------------------------------------------- /app/src/main/java/com/fankes/coloros/notify/ui/activity/base/BaseActivity.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * ColorOSNotifyIcon - Optimize notification icons for ColorOS and adapt to native notification icon specifications. 3 | * Copyright (C) 20174 Fankes Studio(qzmmcn@163.com) 4 | * https://github.com/fankes/ColorOSNotifyIcon 5 | * 6 | * This software is non-free but opensource software: you can redistribute it 7 | * and/or modify it under the terms of the GNU Affero General Public License 8 | * as published by the Free Software Foundation; either 9 | * version 3 of the License, or any later version. 10 | *

11 | * 12 | * This software is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | * Affero General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU Affero General Public License 18 | * and eula along with this software. If not, see 19 | * 20 | * 21 | * This file is created by fankes on 2022/1/30. 22 | */ 23 | @file:Suppress("DEPRECATION") 24 | 25 | package com.fankes.coloros.notify.ui.activity.base 26 | 27 | import android.os.Build 28 | import android.os.Bundle 29 | import androidx.appcompat.app.AppCompatActivity 30 | import androidx.core.content.res.ResourcesCompat 31 | import androidx.core.view.WindowCompat 32 | import androidx.viewbinding.ViewBinding 33 | import com.fankes.coloros.notify.R 34 | import com.fankes.coloros.notify.utils.factory.isNotSystemInDarkMode 35 | import com.highcapable.yukihookapi.hook.factory.current 36 | import com.highcapable.yukihookapi.hook.factory.method 37 | import com.highcapable.yukihookapi.hook.type.android.LayoutInflaterClass 38 | 39 | abstract class BaseActivity : AppCompatActivity() { 40 | 41 | companion object { 42 | 43 | /** 应用是否正在运行 */ 44 | var isMainThreadRunning = false 45 | } 46 | 47 | /** 获取绑定布局对象 */ 48 | lateinit var binding: VB 49 | 50 | override fun onCreate(savedInstanceState: Bundle?) { 51 | super.onCreate(savedInstanceState) 52 | isMainThreadRunning = true 53 | binding = current().generic()?.argument()?.method { 54 | name = "inflate" 55 | param(LayoutInflaterClass) 56 | }?.get()?.invoke(layoutInflater) ?: error("binding failed") 57 | if (Build.VERSION.SDK_INT >= 35) binding.root.fitsSystemWindows = true 58 | setContentView(binding.root) 59 | /** 隐藏系统的标题栏 */ 60 | supportActionBar?.hide() 61 | /** 初始化沉浸状态栏 */ 62 | WindowCompat.getInsetsController(window, window.decorView).apply { 63 | isAppearanceLightStatusBars = isNotSystemInDarkMode 64 | isAppearanceLightNavigationBars = isNotSystemInDarkMode 65 | } 66 | ResourcesCompat.getColor(resources, R.color.colorThemeBackground, null).also { 67 | window?.statusBarColor = it 68 | window?.navigationBarColor = it 69 | window?.navigationBarDividerColor = it 70 | } 71 | /** 装载子类 */ 72 | onCreate() 73 | } 74 | 75 | /** 回调 [onCreate] 方法 */ 76 | abstract fun onCreate() 77 | } -------------------------------------------------------------------------------- /app/src/main/java/com/fankes/coloros/notify/ui/widget/MaterialSwitch.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * ColorOSNotifyIcon - Optimize notification icons for ColorOS and adapt to native notification icon specifications. 3 | * Copyright (C) 20174 Fankes Studio(qzmmcn@163.com) 4 | * https://github.com/fankes/ColorOSNotifyIcon 5 | * 6 | * This software is non-free but opensource software: you can redistribute it 7 | * and/or modify it under the terms of the GNU Affero General Public License 8 | * as published by the Free Software Foundation; either 9 | * version 3 of the License, or any later version. 10 | *

11 | * 12 | * This software is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | * Affero General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU Affero General Public License 18 | * and eula along with this software. If not, see 19 | * 20 | * 21 | * This file is created by fankes on 2022/1/8. 22 | */ 23 | @file:Suppress("SameParameterValue") 24 | 25 | package com.fankes.coloros.notify.ui.widget 26 | 27 | import android.content.Context 28 | import android.content.res.ColorStateList 29 | import android.graphics.Color 30 | import android.text.TextUtils 31 | import android.util.AttributeSet 32 | import androidx.appcompat.widget.SwitchCompat 33 | import com.fankes.coloros.notify.utils.factory.dp 34 | import com.fankes.coloros.notify.utils.factory.isSystemInDarkMode 35 | import top.defaults.drawabletoolbox.DrawableBuilder 36 | 37 | class MaterialSwitch(context: Context, attrs: AttributeSet?) : SwitchCompat(context, attrs) { 38 | 39 | private fun toColors(selected: Int, pressed: Int, normal: Int): ColorStateList { 40 | val colors = intArrayOf(selected, pressed, normal) 41 | val states = arrayOfNulls(3) 42 | states[0] = intArrayOf(android.R.attr.state_checked) 43 | states[1] = intArrayOf(android.R.attr.state_pressed) 44 | states[2] = intArrayOf() 45 | return ColorStateList(states, colors) 46 | } 47 | 48 | private val thumbColor get() = if (context.isSystemInDarkMode) 0xFF7C7C7C else 0xFFCCCCCC 49 | 50 | init { 51 | trackDrawable = DrawableBuilder() 52 | .rectangle() 53 | .rounded() 54 | .solidColor(0xFF656565.toInt()) 55 | .height(20.dp(context)) 56 | .cornerRadius(15.dp(context)) 57 | .build() 58 | thumbDrawable = DrawableBuilder() 59 | .rectangle() 60 | .rounded() 61 | .solidColor(Color.WHITE) 62 | .size(20.dp(context), 20.dp(context)) 63 | .cornerRadius(20.dp(context)) 64 | .strokeWidth(8.dp(context)) 65 | .strokeColor(Color.TRANSPARENT) 66 | .build() 67 | trackTintList = toColors( 68 | 0xFF656565.toInt(), 69 | thumbColor.toInt(), 70 | thumbColor.toInt() 71 | ) 72 | isSingleLine = true 73 | ellipsize = TextUtils.TruncateAt.END 74 | } 75 | } -------------------------------------------------------------------------------- /app/src/main/java/com/fankes/coloros/notify/utils/factory/BackPressedEventFactory.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * ColorOSNotifyIcon - Optimize notification icons for ColorOS and adapt to native notification icon specifications. 3 | * Copyright (C) 20174 Fankes Studio(qzmmcn@163.com) 4 | * https://github.com/fankes/ColorOSNotifyIcon 5 | * 6 | * This software is non-free but opensource software: you can redistribute it 7 | * and/or modify it under the terms of the GNU Affero General Public License 8 | * as published by the Free Software Foundation; either 9 | * version 3 of the License, or any later version. 10 | *

11 | * 12 | * This software is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | * Affero General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU Affero General Public License 18 | * and eula along with this software. If not, see 19 | * 20 | * 21 | * This file is created by fankes on 2022/10/6. 22 | */ 23 | package com.fankes.coloros.notify.utils.factory 24 | 25 | import androidx.activity.OnBackPressedCallback 26 | import androidx.appcompat.app.AppCompatActivity 27 | 28 | /** 已添加的返回监听事件 */ 29 | private val onBackPressedCallbacks = HashMap() 30 | 31 | /** 32 | * 手动调用返回事件 33 | * @param ignored 是否忽略现有返回监听事件立即返回 - 否则将执行返回事件 - 默认否 34 | */ 35 | fun AppCompatActivity.callOnBackPressed(ignored: Boolean = false) { 36 | if (isDestroyed) return 37 | onBackPressedCallbacks[this]?.isEnabled = ignored.not() 38 | onBackPressedDispatcher.onBackPressed() 39 | } 40 | 41 | /** 42 | * 添加返回监听事件 43 | * @param callback 回调事件 44 | */ 45 | fun AppCompatActivity.addOnBackPressedEvent(callback: OnBackPressedEvent.() -> Unit) { 46 | object : OnBackPressedCallback(true) { 47 | override fun handleOnBackPressed() { 48 | OnBackPressedEvent(this@addOnBackPressedEvent).apply(callback) 49 | } 50 | }.also { result -> 51 | onBackPressedCallbacks.computeIfAbsent(this) { 52 | onBackPressedDispatcher.addCallback(result) 53 | result 54 | } 55 | } 56 | } 57 | 58 | /** 59 | * 返回监听事件实现类 60 | * @param instance 当前实例 61 | */ 62 | class OnBackPressedEvent(private val instance: AppCompatActivity) { 63 | 64 | /** 立即释放返回事件并调用返回功能 */ 65 | fun releaseEventAndBack() = instance.callOnBackPressed(ignored = true) 66 | } -------------------------------------------------------------------------------- /app/src/main/java/com/fankes/coloros/notify/utils/factory/BaseAdapterFactory.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * ColorOSNotifyIcon - Optimize notification icons for ColorOS and adapt to native notification icon specifications. 3 | * Copyright (C) 20174 Fankes Studio(qzmmcn@163.com) 4 | * https://github.com/fankes/ColorOSNotifyIcon 5 | * 6 | * This software is non-free but opensource software: you can redistribute it 7 | * and/or modify it under the terms of the GNU Affero General Public License 8 | * as published by the Free Software Foundation; either 9 | * version 3 of the License, or any later version. 10 | *

11 | * 12 | * This software is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | * Affero General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU Affero General Public License 18 | * and eula along with this software. If not, see 19 | * 20 | * 21 | * This file is created by fankes on 2022/6/3. 22 | */ 23 | package com.fankes.coloros.notify.utils.factory 24 | 25 | import android.content.Context 26 | import android.view.LayoutInflater 27 | import android.view.View 28 | import android.view.ViewGroup 29 | import android.widget.BaseAdapter 30 | import android.widget.ListView 31 | import androidx.viewbinding.ViewBinding 32 | import com.highcapable.yukihookapi.hook.factory.method 33 | import com.highcapable.yukihookapi.hook.type.android.LayoutInflaterClass 34 | 35 | /** 36 | * 绑定 [BaseAdapter] 到 [ListView] 37 | * @param initiate 方法体 38 | * @return [BaseAdapter] 39 | */ 40 | inline fun ListView.bindAdapter(initiate: BaseAdapterCreater.() -> Unit) = 41 | BaseAdapterCreater(context).apply(initiate).baseAdapter?.apply { adapter = this } ?: error("BaseAdapter not binded") 42 | 43 | /** 44 | * [BaseAdapter] 创建类 45 | * @param context 实例 46 | */ 47 | class BaseAdapterCreater(val context: Context) { 48 | 49 | /** 当前 [List] 回调 */ 50 | var listDataCallback: (() -> List<*>)? = null 51 | 52 | /** 当前 [BaseAdapter] */ 53 | var baseAdapter: BaseAdapter? = null 54 | 55 | /** 56 | * 绑定 [List] 到 [ListView] 57 | * @param result 回调数据 58 | */ 59 | fun onBindDatas(result: (() -> List<*>)) { 60 | listDataCallback = result 61 | } 62 | 63 | /** 64 | * 绑定 [BaseAdapter] 到 [ListView] 65 | * @param bindViews 回调 - ([VB] 每项,[Int] 下标) 66 | */ 67 | inline fun onBindViews(crossinline bindViews: (binding: VB, position: Int) -> Unit) { 68 | baseAdapter = object : BaseAdapter() { 69 | override fun getCount() = listDataCallback?.let { it() }?.size ?: 0 70 | override fun getItem(position: Int) = listDataCallback?.let { it() }?.get(position) 71 | override fun getItemId(position: Int) = position.toLong() 72 | override fun getView(position: Int, convertView: View?, parent: ViewGroup?): View { 73 | var holderView = convertView 74 | val holder: VB 75 | if (convertView == null) { 76 | holder = VB::class.java.method { 77 | name = "inflate" 78 | param(LayoutInflaterClass) 79 | }.get().invoke(LayoutInflater.from(context)) ?: error("ViewHolder binding failed") 80 | holderView = holder.root.apply { tag = holder } 81 | } else holder = convertView.tag as VB 82 | bindViews(holder, position) 83 | return holderView ?: error("ViewHolder binding failed") 84 | } 85 | } 86 | } 87 | } -------------------------------------------------------------------------------- /app/src/main/java/com/fankes/coloros/notify/utils/factory/DialogBuilderFactory.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * ColorOSNotifyIcon - Optimize notification icons for ColorOS and adapt to native notification icon specifications. 3 | * Copyright (C) 20174 Fankes Studio(qzmmcn@163.com) 4 | * https://github.com/fankes/ColorOSNotifyIcon 5 | * 6 | * This software is non-free but opensource software: you can redistribute it 7 | * and/or modify it under the terms of the GNU Affero General Public License 8 | * as published by the Free Software Foundation; either 9 | * version 3 of the License, or any later version. 10 | *

11 | * 12 | * This software is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | * Affero General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU Affero General Public License 18 | * and eula along with this software. If not, see 19 | * 20 | * 21 | * This file is created by fankes on 2022/1/7. 22 | */ 23 | @file:Suppress("unused") 24 | 25 | package com.fankes.coloros.notify.utils.factory 26 | 27 | import android.app.Dialog 28 | import android.app.TimePickerDialog 29 | import android.content.Context 30 | import android.view.Gravity 31 | import android.view.LayoutInflater 32 | import android.view.View 33 | import android.view.ViewGroup 34 | import android.widget.LinearLayout 35 | import android.widget.TextView 36 | import androidx.appcompat.app.AlertDialog 37 | import androidx.viewbinding.ViewBinding 38 | import com.google.android.material.dialog.MaterialAlertDialogBuilder 39 | import com.google.android.material.progressindicator.CircularProgressIndicator 40 | import com.highcapable.yukihookapi.YukiHookAPI 41 | import com.highcapable.yukihookapi.hook.factory.method 42 | import com.highcapable.yukihookapi.hook.type.android.LayoutInflaterClass 43 | 44 | /** 45 | * 显示时间选择对话框 46 | * @param timeSet 当前时间 - 不写将使用当前时间格式:HH:mm 47 | * @param result 回调 - 小时与分钟 HH:mm 48 | */ 49 | fun Context.showTimePicker(timeSet: String = "", result: (String) -> Unit) = 50 | TimePickerDialog(this, { _, h, m -> result("${h.autoZero}:${m.autoZero}") }, timeSet.hour, timeSet.minute, true).show() 51 | 52 | /** 53 | * 构造 [VB] 自定义 View 对话框 54 | * @param initiate 对话框方法体 55 | */ 56 | @JvmName(name = "showDialog_Generics") 57 | inline fun Context.showDialog(initiate: DialogBuilder.() -> Unit) = 58 | DialogBuilder(context = this, VB::class.java).apply(initiate).show() 59 | 60 | /** 61 | * 构造对话框 62 | * @param initiate 对话框方法体 63 | */ 64 | inline fun Context.showDialog(initiate: DialogBuilder<*>.() -> Unit) = DialogBuilder(context = this).apply(initiate).show() 65 | 66 | /** 67 | * 对话框构造器 68 | * @param context 实例 69 | * @param bindingClass [ViewBinding] 的 [Class] 实例 or null 70 | */ 71 | class DialogBuilder(val context: Context, private val bindingClass: Class<*>? = null) { 72 | 73 | /** 实例对象 */ 74 | private var instance: AlertDialog.Builder? = null 75 | 76 | /** 对话框取消监听 */ 77 | private var onCancel: (() -> Unit)? = null 78 | 79 | /** 对话框实例 */ 80 | private var dialogInstance: Dialog? = null 81 | 82 | /** 自定义布局 */ 83 | private var customLayoutView: View? = null 84 | 85 | /** 86 | * 获取 [DialogBuilder] 绑定布局对象 87 | * @return [VB] 88 | */ 89 | val binding by lazy { 90 | bindingClass?.method { 91 | name = "inflate" 92 | param(LayoutInflaterClass) 93 | }?.get()?.invoke(LayoutInflater.from(context))?.apply { 94 | customLayoutView = root 95 | } ?: error("This dialog maybe not a custom view dialog") 96 | } 97 | 98 | init { 99 | if (YukiHookAPI.Status.isXposedEnvironment) error("This dialog is not allowed to created in Xposed environment") 100 | instance = MaterialAlertDialogBuilder(context) 101 | } 102 | 103 | /** 设置对话框不可关闭 */ 104 | fun noCancelable() { 105 | instance?.setCancelable(false) 106 | } 107 | 108 | /** 设置对话框标题 */ 109 | var title 110 | get() = "" 111 | set(value) { 112 | instance?.setTitle(value) 113 | } 114 | 115 | /** 设置对话框消息内容 */ 116 | var msg 117 | get() = "" 118 | set(value) { 119 | instance?.setMessage(value) 120 | } 121 | 122 | /** 设置进度条对话框消息内容 */ 123 | var progressContent 124 | get() = "" 125 | set(value) { 126 | if (customLayoutView == null) 127 | customLayoutView = LinearLayout(context).apply { 128 | orientation = LinearLayout.HORIZONTAL 129 | gravity = Gravity.CENTER or Gravity.START 130 | addView(CircularProgressIndicator(context).apply { 131 | isIndeterminate = true 132 | trackCornerRadius = 10.dp(context) 133 | }) 134 | addView(View(context).apply { layoutParams = ViewGroup.LayoutParams(20.dp(context), 5) }) 135 | addView(TextView(context).apply { 136 | tag = "progressContent" 137 | text = value 138 | }) 139 | setPadding(20.dp(context), 20.dp(context), 20.dp(context), 20.dp(context)) 140 | } 141 | else customLayoutView?.findViewWithTag("progressContent")?.text = value 142 | } 143 | 144 | /** 145 | * 设置对话框确定按钮 146 | * @param text 按钮文本内容 147 | * @param callback 点击事件 148 | */ 149 | fun confirmButton(text: String = "确定", callback: () -> Unit = {}) { 150 | instance?.setPositiveButton(text) { _, _ -> callback() } 151 | } 152 | 153 | /** 154 | * 设置对话框取消按钮 155 | * @param text 按钮文本内容 156 | * @param callback 点击事件 157 | */ 158 | fun cancelButton(text: String = "取消", callback: () -> Unit = {}) { 159 | instance?.setNegativeButton(text) { _, _ -> callback() } 160 | } 161 | 162 | /** 163 | * 设置对话框第三个按钮 164 | * @param text 按钮文本内容 165 | * @param callback 点击事件 166 | */ 167 | fun neutralButton(text: String = "更多", callback: () -> Unit = {}) { 168 | instance?.setNeutralButton(text) { _, _ -> callback() } 169 | } 170 | 171 | /** 172 | * 当对话框关闭时 173 | * @param callback 回调 174 | */ 175 | fun onCancel(callback: () -> Unit) { 176 | onCancel = callback 177 | } 178 | 179 | /** 取消对话框 */ 180 | fun cancel() = dialogInstance?.cancel() 181 | 182 | /** 显示对话框 */ 183 | fun show() = runInSafe { 184 | /** 若当前自定义 View 的对话框没有调用 [binding] 将会对其手动调用一次以确保显示布局 */ 185 | if (bindingClass != null) binding 186 | instance?.create()?.apply { 187 | customLayoutView?.let { setView(it) } 188 | dialogInstance = this 189 | setOnCancelListener { onCancel?.invoke() } 190 | }?.show() 191 | } 192 | } -------------------------------------------------------------------------------- /app/src/main/java/com/fankes/coloros/notify/utils/factory/ExceptionFactory.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * ColorOSNotifyIcon - Optimize notification icons for ColorOS and adapt to native notification icon specifications. 3 | * Copyright (C) 20174 Fankes Studio(qzmmcn@163.com) 4 | * https://github.com/fankes/ColorOSNotifyIcon 5 | * 6 | * This software is non-free but opensource software: you can redistribute it 7 | * and/or modify it under the terms of the GNU Affero General Public License 8 | * as published by the Free Software Foundation; either 9 | * version 3 of the License, or any later version. 10 | *

11 | * 12 | * This software is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | * Affero General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU Affero General Public License 18 | * and eula along with this software. If not, see 19 | * 20 | * 21 | * This file is created by fankes on 2022/3/13. 22 | */ 23 | @file:Suppress("unused") 24 | 25 | package com.fankes.coloros.notify.utils.factory 26 | 27 | import com.highcapable.yukihookapi.hook.log.YLog 28 | 29 | /** 30 | * 忽略异常返回值 31 | * @param result 回调 - 如果异常为空 32 | * @return [T] 发生异常时返回设定值否则返回正常值 33 | */ 34 | inline fun safeOfNull(result: () -> T): T? = safeOf(default = null, result) 35 | 36 | /** 37 | * 忽略异常返回值 38 | * @param result 回调 - 如果异常为 false 39 | * @return [Boolean] 发生异常时返回设定值否则返回正常值 40 | */ 41 | inline fun safeOfFalse(result: () -> Boolean) = safeOf(default = false, result) 42 | 43 | /** 44 | * 忽略异常返回值 45 | * @param result 回调 - 如果异常为 true 46 | * @return [Boolean] 发生异常时返回设定值否则返回正常值 47 | */ 48 | inline fun safeOfTrue(result: () -> Boolean) = safeOf(default = true, result) 49 | 50 | /** 51 | * 忽略异常返回值 52 | * @param result 回调 - 如果异常为 false 53 | * @return [String] 发生异常时返回设定值否则返回正常值 54 | */ 55 | inline fun safeOfNothing(result: () -> String) = safeOf(default = "", result) 56 | 57 | /** 58 | * 忽略异常返回值 59 | * @param result 回调 - 如果异常为 false 60 | * @return [Int] 发生异常时返回设定值否则返回正常值 61 | */ 62 | inline fun safeOfNan(result: () -> Int) = safeOf(default = 0, result) 63 | 64 | /** 65 | * 忽略异常返回值 66 | * @param default 异常返回值 67 | * @param result 正常回调值 68 | * @return [T] 发生异常时返回设定值否则返回正常值 69 | */ 70 | inline fun safeOf(default: T, result: () -> T) = try { 71 | result() 72 | } catch (_: Throwable) { 73 | default 74 | } 75 | 76 | /** 77 | * 忽略异常运行 78 | * @param msg 出错输出的消息 - 默认为空 79 | * @param block 正常回调 80 | */ 81 | inline fun T.runInSafe(msg: String = "", block: () -> Unit) { 82 | runCatching(block).onFailure { if (msg.isNotBlank()) YLog.error(msg, it) } 83 | } -------------------------------------------------------------------------------- /app/src/main/java/com/fankes/coloros/notify/utils/tool/ActivationPromptTool.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * ColorOSNotifyIcon - Optimize notification icons for ColorOS and adapt to native notification icon specifications. 3 | * Copyright (C) 20174 Fankes Studio(qzmmcn@163.com) 4 | * https://github.com/fankes/ColorOSNotifyIcon 5 | * 6 | * This software is non-free but opensource software: you can redistribute it 7 | * and/or modify it under the terms of the GNU Affero General Public License 8 | * as published by the Free Software Foundation; either 9 | * version 3 of the License, or any later version. 10 | *

11 | * 12 | * This software is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | * Affero General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU Affero General Public License 18 | * and eula along with this software. If not, see 19 | * 20 | * 21 | * This file is created by fankes on 2023/4/17. 22 | */ 23 | package com.fankes.coloros.notify.utils.tool 24 | 25 | import android.app.Notification 26 | import android.app.NotificationChannel 27 | import android.app.NotificationManager 28 | import android.app.PendingIntent 29 | import android.content.ComponentName 30 | import android.content.Context 31 | import android.content.Intent 32 | import android.graphics.drawable.Icon 33 | import android.os.Build 34 | import androidx.core.graphics.drawable.toBitmap 35 | import com.fankes.coloros.notify.R 36 | import com.fankes.coloros.notify.utils.factory.appIconOf 37 | import com.fankes.coloros.notify.wrapper.BuildConfigWrapper 38 | 39 | /** 40 | * 模块更新激活提醒通知工具类 41 | */ 42 | object ActivationPromptTool { 43 | 44 | /** 当前模块的包名 */ 45 | private const val MODULE_PACKAGE_NAME = BuildConfigWrapper.APPLICATION_ID 46 | 47 | /** 推送通知的渠道名称 */ 48 | private const val NOTIFY_CHANNEL = "activationPromptId" 49 | 50 | /** 51 | * 推送提醒通知 52 | * @param context 当前实例 53 | * @param packageName 当前 APP 包名 54 | */ 55 | fun prompt(context: Context, packageName: String) { 56 | if (packageName != BuildConfigWrapper.APPLICATION_ID) return 57 | context.getSystemService(NotificationManager::class.java)?.apply { 58 | createNotificationChannel( 59 | NotificationChannel( 60 | NOTIFY_CHANNEL, "ColorOS 通知图标增强 - 版本更新", 61 | NotificationManager.IMPORTANCE_DEFAULT 62 | ).apply { enableLights(false) } 63 | ) 64 | notify(packageName.hashCode(), Notification.Builder(context, NOTIFY_CHANNEL).apply { 65 | setShowWhen(true) 66 | setContentTitle("模块已更新") 67 | setContentText("点按通知打开模块以完成新版本激活。") 68 | setColor(0xFF4E8A5A.toInt()) 69 | setAutoCancel(true) 70 | setSmallIcon(Icon.createWithResource(MODULE_PACKAGE_NAME, R.drawable.ic_notify_update)) 71 | setLargeIcon(context.appIconOf(packageName)?.toBitmap()) 72 | setContentIntent( 73 | PendingIntent.getActivity( 74 | context, packageName.hashCode(), 75 | Intent().apply { 76 | component = ComponentName(MODULE_PACKAGE_NAME, "$MODULE_PACKAGE_NAME.ui.activity.MainActivity") 77 | flags = Intent.FLAG_ACTIVITY_NEW_TASK 78 | }, if (Build.VERSION.SDK_INT < 31) PendingIntent.FLAG_UPDATE_CURRENT else PendingIntent.FLAG_IMMUTABLE 79 | ) 80 | ) 81 | }.build()) 82 | } 83 | } 84 | } -------------------------------------------------------------------------------- /app/src/main/java/com/fankes/coloros/notify/utils/tool/BitmapCompatTool.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * ColorOSNotifyIcon - Optimize notification icons for ColorOS and adapt to native notification icon specifications. 3 | * Copyright (C) 20174 Fankes Studio(qzmmcn@163.com) 4 | * https://github.com/fankes/ColorOSNotifyIcon 5 | * 6 | * This software is non-free but opensource software: you can redistribute it 7 | * and/or modify it under the terms of the GNU Affero General Public License 8 | * as published by the Free Software Foundation; either 9 | * version 3 of the License, or any later version. 10 | *

11 | * 12 | * This software is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | * Affero General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU Affero General Public License 18 | * and eula along with this software. If not, see 19 | * 20 | * 21 | * This file is created by fankes on 2023/1/28. 22 | */ 23 | package com.fankes.coloros.notify.utils.tool 24 | 25 | import android.graphics.Bitmap 26 | import android.graphics.Canvas 27 | import android.graphics.Matrix 28 | import android.graphics.Paint 29 | import android.graphics.PorterDuff 30 | import android.graphics.drawable.AnimationDrawable 31 | import android.graphics.drawable.BitmapDrawable 32 | import android.graphics.drawable.Drawable 33 | import android.graphics.drawable.VectorDrawable 34 | import android.util.ArrayMap 35 | import androidx.core.graphics.drawable.toBitmap 36 | import com.fankes.coloros.notify.utils.factory.safeOfFalse 37 | import kotlin.math.abs 38 | 39 | /** 40 | * 这是一个从 AOSP 源码中分离出来的功能 41 | * 42 | * 主要作用于兼容部分第三方系统修改颜色判断代码造成判断位图灰度功能失效 43 | */ 44 | object BitmapCompatTool { 45 | 46 | /** 缓存已判断的结果防止卡顿 */ 47 | private var cachedBitmapGrayscales = ArrayMap() 48 | 49 | private var tempBuffer = intArrayOf(0) 50 | private var tempCompactBitmap: Bitmap? = null 51 | private var tempCompactBitmapCanvas: Canvas? = null 52 | private var tempCompactBitmapPaint: Paint? = null 53 | private val tempMatrix = Matrix() 54 | 55 | /** 56 | * 判断 [Drawable] 是否为灰度位图 57 | * @param drawable 要判断的 [Drawable] 58 | * @return [Boolean] 是否灰度 59 | */ 60 | fun isGrayscaleDrawable(drawable: Drawable) = safeOfFalse { 61 | when (drawable) { 62 | is BitmapDrawable -> isGrayscaleBitmap(drawable.bitmap) 63 | is AnimationDrawable -> !(drawable.numberOfFrames <= 0 || !isGrayscaleBitmap(drawable.getFrame(0).toBitmap())) 64 | is VectorDrawable -> true 65 | else -> isGrayscaleBitmap(drawable.toBitmap()) 66 | } 67 | } 68 | 69 | /** 70 | * 判断 [Bitmap] 是否为灰度位图 71 | * @param bitmap 要判断的位图 72 | * @return [Boolean] 是否灰度 73 | */ 74 | private fun isGrayscaleBitmap(bitmap: Bitmap) = 75 | cachedBitmapGrayscales[bitmap.generationId] ?: let { 76 | var height = bitmap.height 77 | var width = bitmap.width 78 | if (height > 64 || width > 64) { 79 | if (tempCompactBitmap == null) { 80 | tempCompactBitmap = Bitmap.createBitmap(64, 64, Bitmap.Config.ARGB_8888) 81 | .also { tempCompactBitmapCanvas = Canvas(it) } 82 | tempCompactBitmapPaint = Paint(Paint.FILTER_BITMAP_FLAG).apply { isFilterBitmap = true } 83 | } 84 | tempMatrix.reset() 85 | tempMatrix.setScale(64f / width, 64f / height, 0f, 0f) 86 | tempCompactBitmapCanvas?.drawColor(0, PorterDuff.Mode.SRC) 87 | tempCompactBitmapCanvas?.drawBitmap(bitmap, tempMatrix, tempCompactBitmapPaint) 88 | height = 64 89 | width = 64 90 | } 91 | val size = height * width 92 | ensureBufferSize(size) 93 | tempCompactBitmap?.getPixels(tempBuffer, 0, width, 0, 0, width, height) 94 | for (i in 0 until size) 95 | if (isGrayscaleColor(tempBuffer[i]).not()) { 96 | cachedBitmapGrayscales[bitmap.generationId] = false 97 | return@let false 98 | } 99 | cachedBitmapGrayscales[bitmap.generationId] = true 100 | true 101 | } 102 | 103 | /** 104 | * 提纯 [Bitmap] 颜色判断灰度 105 | * @param color 颜色 106 | * @return [Boolean] 是否灰度 107 | */ 108 | private fun isGrayscaleColor(color: Int): Boolean { 109 | if (color shr 24 and 255 < 50) return true 110 | val r = color shr 16 and 255 111 | val g = color shr 8 and 255 112 | val b = color and 255 113 | return !(abs(r - g) >= 20 || abs(r - b) >= 20 || abs(g - b) >= 20) 114 | } 115 | 116 | /** 117 | * 计算字节数组 118 | * @param size 大小 119 | */ 120 | private fun ensureBufferSize(size: Int) { 121 | if (tempBuffer.size < size) tempBuffer = IntArray(size) 122 | } 123 | } -------------------------------------------------------------------------------- /app/src/main/java/com/fankes/coloros/notify/utils/tool/GithubReleaseTool.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * ColorOSNotifyIcon - Optimize notification icons for ColorOS and adapt to native notification icon specifications. 3 | * Copyright (C) 20174 Fankes Studio(qzmmcn@163.com) 4 | * https://github.com/fankes/ColorOSNotifyIcon 5 | * 6 | * This software is non-free but opensource software: you can redistribute it 7 | * and/or modify it under the terms of the GNU Affero General Public License 8 | * as published by the Free Software Foundation; either 9 | * version 3 of the License, or any later version. 10 | *

11 | * 12 | * This software is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | * Affero General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU Affero General Public License 18 | * and eula along with this software. If not, see 19 | * 20 | * 21 | * This file is created by fankes on 2022/3/20. 22 | */ 23 | package com.fankes.coloros.notify.utils.tool 24 | 25 | import android.app.Activity 26 | import android.content.Context 27 | import android.icu.text.SimpleDateFormat 28 | import android.icu.util.Calendar 29 | import android.icu.util.TimeZone 30 | import com.fankes.coloros.notify.utils.factory.isNetWorkSuccess 31 | import com.fankes.coloros.notify.utils.factory.openBrowser 32 | import com.fankes.coloros.notify.utils.factory.openSelfSetting 33 | import com.fankes.coloros.notify.utils.factory.runInSafe 34 | import com.fankes.coloros.notify.utils.factory.showDialog 35 | import okhttp3.Call 36 | import okhttp3.Callback 37 | import okhttp3.OkHttpClient 38 | import okhttp3.Request 39 | import okhttp3.Response 40 | import org.json.JSONObject 41 | import java.io.IOException 42 | import java.io.Serializable 43 | import java.util.Locale 44 | 45 | /** 46 | * 获取 GitHub Release 最新版本工具类 47 | */ 48 | object GithubReleaseTool { 49 | 50 | /** 仓库作者 */ 51 | private const val REPO_AUTHOR = "fankes" 52 | 53 | /** 仓库名称 */ 54 | private const val REPO_NAME = "ColorOSNotifyIcon" 55 | 56 | /** 57 | * 获取最新版本信息 58 | * @param context 实例 59 | * @param version 当前版本 60 | * @param result 成功后回调 - ([String] 最新版本,[Function] 更新对话框方法体) 61 | */ 62 | fun checkingForUpdate(context: Context, version: String, result: (String, () -> Unit) -> Unit) = checkingInternetConnect(context) { 63 | OkHttpClient().newBuilder().build().newCall( 64 | Request.Builder() 65 | .url("https://api.github.com/repos/$REPO_AUTHOR/$REPO_NAME/releases/latest") 66 | .get() 67 | .build() 68 | ).enqueue(object : Callback { 69 | override fun onFailure(call: Call, e: IOException) {} 70 | 71 | override fun onResponse(call: Call, response: Response) = runInSafe { 72 | JSONObject(response.body.string()).apply { 73 | GithubReleaseBean( 74 | name = getString("name"), 75 | htmlUrl = getString("html_url"), 76 | content = getString("body"), 77 | date = getString("published_at").localTime() 78 | ).apply { 79 | fun showUpdate() = context.showDialog { 80 | title = "最新版本 $name" 81 | msg = "发布于 $date\n\n" + 82 | "更新日志\n\n" + content 83 | confirmButton(text = "更新") { context.openBrowser(htmlUrl) } 84 | cancelButton() 85 | } 86 | if (name != version) (context as? Activity?)?.runOnUiThread { 87 | showUpdate() 88 | result(name) { showUpdate() } 89 | } 90 | } 91 | } 92 | } 93 | }) 94 | } 95 | 96 | /** 97 | * 检查网络连接情况 98 | * @param context 实例 99 | * @param result 已连接回调 100 | */ 101 | private fun checkingInternetConnect(context: Context, result: () -> Unit) = runInSafe { 102 | if (context.isNetWorkSuccess) 103 | OkHttpClient().newBuilder().build().newCall( 104 | Request.Builder() 105 | .url("https://www.baidu.com") 106 | .get() 107 | .build() 108 | ).enqueue(object : Callback { 109 | override fun onFailure(call: Call, e: IOException) { 110 | (context as? Activity?)?.runOnUiThread { 111 | context.showDialog { 112 | title = "网络不可用" 113 | msg = "应用的联网权限可能已被禁用,请开启联网权限以定期检查更新。" 114 | confirmButton(text = "去开启") { context.openSelfSetting() } 115 | cancelButton() 116 | noCancelable() 117 | } 118 | } 119 | } 120 | 121 | override fun onResponse(call: Call, response: Response) = runInSafe { 122 | (context as? Activity?)?.runOnUiThread { runInSafe { result() } } 123 | } 124 | }) 125 | } 126 | 127 | /** 128 | * 格式化时间为本地时区 129 | * @return [String] 本地时区时间 130 | */ 131 | private fun String.localTime() = replace("T", " ").replace("Z", "").let { 132 | runCatching { 133 | val local = SimpleDateFormat("yyyy-MM-dd HH:mm", Locale.ROOT).apply { timeZone = Calendar.getInstance().timeZone } 134 | val current = SimpleDateFormat("yyyy-MM-dd HH:mm", Locale.ROOT).apply { timeZone = TimeZone.getTimeZone("GMT") } 135 | local.format(current.parse(it)) 136 | }.getOrNull() ?: it 137 | } 138 | 139 | /** 140 | * GitHub Release bean 141 | * @param name 版本名称 142 | * @param htmlUrl 网页地址 143 | * @param content 更新日志 144 | * @param date 发布时间 145 | */ 146 | private data class GithubReleaseBean( 147 | var name: String, 148 | var htmlUrl: String, 149 | var content: String, 150 | var date: String 151 | ) : Serializable 152 | } -------------------------------------------------------------------------------- /app/src/main/java/com/fankes/coloros/notify/utils/tool/I18nWarnTool.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * ColorOSNotifyIcon - Optimize notification icons for ColorOS and adapt to native notification icon specifications. 3 | * Copyright (C) 20174 Fankes Studio(qzmmcn@163.com) 4 | * https://github.com/fankes/ColorOSNotifyIcon 5 | * 6 | * This software is non-free but opensource software: you can redistribute it 7 | * and/or modify it under the terms of the GNU Affero General Public License 8 | * as published by the Free Software Foundation; either 9 | * version 3 of the License, or any later version. 10 | *

11 | * 12 | * This software is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | * Affero General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU Affero General Public License 18 | * and eula along with this software. If not, see 19 | * 20 | * 21 | * This file is created by fankes on 2023/2/3. 22 | */ 23 | package com.fankes.coloros.notify.utils.tool 24 | 25 | import android.content.Context 26 | import com.fankes.coloros.notify.utils.factory.showDialog 27 | import com.highcapable.yukihookapi.hook.factory.prefs 28 | import com.highcapable.yukihookapi.hook.xposed.prefs.data.PrefsData 29 | import java.util.Locale 30 | 31 | /** 32 | * I18n 适配警告提示工具类 33 | */ 34 | object I18nWarnTool { 35 | 36 | /** 推广已读存储键值 */ 37 | private val LOCALE_WARN_READED = PrefsData("locale_warn_readed", false) 38 | 39 | /** 40 | * 检查并显示 I18n 适配警告对话框 41 | * @param context 实例 42 | */ 43 | fun checkingOrShowing(context: Context) { 44 | fun saveReaded() = context.prefs().edit { put(LOCALE_WARN_READED, value = true) } 45 | if (Locale.getDefault().language.startsWith("zh").not() && context.prefs().get(LOCALE_WARN_READED).not()) 46 | context.showDialog { 47 | title = "Notice of I18n Support" 48 | msg = "This Xposed Module is only for Chinese and the Chinese region.\n\n" + 49 | "Currently, there will be no internationalization adaptation.\n\n" + 50 | "There may be plans for internationalization adaptation in the future, so stay tuned." 51 | confirmButton(text = "Got It") { saveReaded() } 52 | noCancelable() 53 | } 54 | } 55 | } -------------------------------------------------------------------------------- /app/src/main/java/com/fankes/coloros/notify/utils/tool/IconAdaptationTool.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * ColorOSNotifyIcon - Optimize notification icons for ColorOS and adapt to native notification icon specifications. 3 | * Copyright (C) 20174 Fankes Studio(qzmmcn@163.com) 4 | * https://github.com/fankes/ColorOSNotifyIcon 5 | * 6 | * This software is non-free but opensource software: you can redistribute it 7 | * and/or modify it under the terms of the GNU Affero General Public License 8 | * as published by the Free Software Foundation; either 9 | * version 3 of the License, or any later version. 10 | *

11 | * 12 | * This software is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | * Affero General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU Affero General Public License 18 | * and eula along with this software. If not, see 19 | * 20 | * 21 | * This file is created by fankes on 2022/3/20. 22 | */ 23 | package com.fankes.coloros.notify.utils.tool 24 | 25 | import android.app.Notification 26 | import android.app.NotificationChannel 27 | import android.app.NotificationManager 28 | import android.app.PendingIntent 29 | import android.content.ComponentName 30 | import android.content.Context 31 | import android.content.Intent 32 | import android.graphics.drawable.Icon 33 | import android.os.Build 34 | import androidx.core.graphics.drawable.toBitmap 35 | import com.fankes.coloros.notify.R 36 | import com.fankes.coloros.notify.hook.HookEntry 37 | import com.fankes.coloros.notify.utils.factory.appIconOf 38 | import com.fankes.coloros.notify.utils.factory.appNameOf 39 | import com.fankes.coloros.notify.utils.factory.isDebugApp 40 | import com.fankes.coloros.notify.utils.factory.isSystemApp 41 | import com.fankes.coloros.notify.utils.factory.runInSafe 42 | import com.fankes.coloros.notify.utils.factory.stampToDate 43 | import com.fankes.coloros.notify.wrapper.BuildConfigWrapper 44 | 45 | /** 46 | * 通知图标适配推送通知类 47 | * 48 | * 这个类需要在 [HookEntry] 中调用 49 | */ 50 | object IconAdaptationTool { 51 | 52 | /** 当前模块的包名 */ 53 | private const val MODULE_PACKAGE_NAME = BuildConfigWrapper.APPLICATION_ID 54 | 55 | /** 推送通知的渠道名称 */ 56 | private const val NOTIFY_CHANNEL = "notifyRuleSupportId" 57 | 58 | /** 已过期的时间 */ 59 | private val outTimeLimits = HashSet() 60 | 61 | /** 62 | * 推送新 APP 安装适配通知 63 | * @param context 实例 64 | * @param packageName 安装的 APP 包名 65 | */ 66 | fun pushNewAppSupportNotify(context: Context, packageName: String) { 67 | if (packageName.startsWith("com.google.android.trichromelibrary")) return 68 | if (context.isSystemApp(packageName) || context.isDebugApp(packageName)) return 69 | context.getSystemService(NotificationManager::class.java)?.apply { 70 | createNotificationChannel( 71 | NotificationChannel( 72 | NOTIFY_CHANNEL, "通知图标优化适配", 73 | NotificationManager.IMPORTANCE_DEFAULT 74 | ).apply { enableLights(false) } 75 | ) 76 | notify(packageName.hashCode(), Notification.Builder(context, NOTIFY_CHANNEL).apply { 77 | setShowWhen(true) 78 | setContentTitle("您已安装 ${context.appNameOf(packageName)}") 79 | setContentText("尚未适配此应用,点按打开在线规则。") 80 | setColor(0xFF2993F0.toInt()) 81 | setAutoCancel(true) 82 | setSmallIcon(Icon.createWithResource(MODULE_PACKAGE_NAME, R.drawable.ic_unsupported)) 83 | setLargeIcon(context.appIconOf(packageName)?.toBitmap()) 84 | setContentIntent( 85 | PendingIntent.getActivity( 86 | context, packageName.hashCode(), 87 | Intent().apply { 88 | component = ComponentName( 89 | MODULE_PACKAGE_NAME, 90 | "$MODULE_PACKAGE_NAME.ui.activity.ConfigureActivity" 91 | ) 92 | flags = Intent.FLAG_ACTIVITY_NEW_TASK 93 | }.apply { 94 | putExtra("isNewAppSupport", true) 95 | putExtra("appName", context.appNameOf(packageName)) 96 | putExtra("pkgName", packageName) 97 | }, if (Build.VERSION.SDK_INT < 31) PendingIntent.FLAG_UPDATE_CURRENT else PendingIntent.FLAG_IMMUTABLE 98 | ) 99 | ) 100 | }.build()) 101 | } 102 | } 103 | 104 | /** 105 | * 检测 APP 卸载后移除相关通知 106 | * @param context 实例 107 | * @param packageName 卸载的 APP 包名 108 | */ 109 | fun removeNewAppSupportNotify(context: Context, packageName: String) = runInSafe { 110 | context.getSystemService(NotificationManager::class.java)?.cancel(packageName.hashCode()) 111 | } 112 | 113 | /** 114 | * 自动更新通知图标优化在线规则 115 | * @param context 实例 116 | * @param timeSet 设定的时间 117 | */ 118 | fun prepareAutoUpdateIconRule(context: Context, timeSet: String) = runInSafe { 119 | System.currentTimeMillis().also { 120 | val nowTime = it.stampToDate(format = "HH:mm") 121 | if (timeSet != nowTime || outTimeLimits.any { e -> e == nowTime }) return 122 | outTimeLimits.add(nowTime) 123 | context.startActivity( 124 | Intent().apply { 125 | component = ComponentName(MODULE_PACKAGE_NAME, "$MODULE_PACKAGE_NAME.ui.activity.auto.NotifyIconRuleUpdateActivity") 126 | flags = Intent.FLAG_ACTIVITY_NEW_TASK 127 | } 128 | ) 129 | } 130 | } 131 | } -------------------------------------------------------------------------------- /app/src/main/java/com/fankes/coloros/notify/utils/tool/SystemUITool.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * ColorOSNotifyIcon - Optimize notification icons for ColorOS and adapt to native notification icon specifications. 3 | * Copyright (C) 20174 Fankes Studio(qzmmcn@163.com) 4 | * https://github.com/fankes/ColorOSNotifyIcon 5 | * 6 | * This software is non-free but opensource software: you can redistribute it 7 | * and/or modify it under the terms of the GNU Affero General Public License 8 | * as published by the Free Software Foundation; either 9 | * version 3 of the License, or any later version. 10 | *

11 | * 12 | * This software is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | * Affero General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU Affero General Public License 18 | * and eula along with this software. If not, see 19 | * 20 | * 21 | * This file is created by fankes on 2022/2/8. 22 | */ 23 | package com.fankes.coloros.notify.utils.tool 24 | 25 | import android.content.Context 26 | import android.os.Build 27 | import androidx.activity.result.ActivityResultLauncher 28 | import androidx.activity.result.contract.ActivityResultContracts 29 | import androidx.appcompat.app.AppCompatActivity 30 | import com.fankes.coloros.notify.const.PackageName 31 | import com.fankes.coloros.notify.data.ConfigData 32 | import com.fankes.coloros.notify.ui.activity.MainActivity 33 | import com.fankes.coloros.notify.utils.factory.colorOSFullVersion 34 | import com.fankes.coloros.notify.utils.factory.delayedRun 35 | import com.fankes.coloros.notify.utils.factory.execShell 36 | import com.fankes.coloros.notify.utils.factory.showDialog 37 | import com.fankes.coloros.notify.utils.factory.snake 38 | import com.google.android.material.snackbar.Snackbar 39 | import com.highcapable.yukihookapi.YukiHookAPI 40 | import com.highcapable.yukihookapi.hook.factory.dataChannel 41 | import com.highcapable.yukihookapi.hook.log.YLog 42 | import com.highcapable.yukihookapi.hook.log.data.YLogData 43 | import com.highcapable.yukihookapi.hook.param.PackageParam 44 | import com.highcapable.yukihookapi.hook.xposed.channel.data.ChannelData 45 | import java.util.Locale 46 | 47 | /** 48 | * 系统界面工具 49 | */ 50 | object SystemUITool { 51 | 52 | private val CALL_HOST_REFRESH_CACHING = ChannelData("call_host_refresh_caching", false) 53 | private val CALL_MODULE_REFRESH_RESULT = ChannelData("call_module_refresh_result", false) 54 | 55 | /** 当前全部调试日志 */ 56 | private var debugLogs = listOf() 57 | 58 | /** 当前启动器实例 */ 59 | private var launcher: ActivityResultLauncher? = null 60 | 61 | /** 62 | * 宿主注册监听 63 | */ 64 | object Host { 65 | 66 | /** 67 | * 监听系统界面刷新改变 68 | * @param param 实例 69 | * @param result 回调 - ([Boolean] 是否成功) 70 | */ 71 | fun onRefreshSystemUI(param: PackageParam, result: (Boolean) -> Boolean) { 72 | param.dataChannel.with { wait(CALL_HOST_REFRESH_CACHING) { put(CALL_MODULE_REFRESH_RESULT, result(it)) } } 73 | } 74 | } 75 | 76 | /** 77 | * 检查模块是否激活 78 | * @param context 实例 79 | * @param result 成功后回调 80 | */ 81 | fun checkingActivated(context: Context, result: (Boolean) -> Unit) = 82 | context.dataChannel(PackageName.SYSTEMUI).checkingVersionEquals(result = result) 83 | 84 | /** 85 | * 注册导出调试日志启动器到 [AppCompatActivity] 86 | * @param activity 实例 87 | */ 88 | fun registerExportDebugLogsLauncher(activity: AppCompatActivity) { 89 | launcher = activity.registerForActivityResult(ActivityResultContracts.CreateDocument("*/application")) { result -> 90 | runCatching { 91 | result?.let { e -> 92 | val content = "" + 93 | "================================================================\n" + 94 | " Generated by ColorOSNotifyIcon\n" + 95 | " Project Url: https://github.com/fankes/ColorOSNotifyIcon\n" + 96 | "================================================================\n\n" + 97 | "[Device Brand]: ${Build.BRAND}\n" + 98 | "[Device Model]: ${Build.MODEL}\n" + 99 | "[Display]: ${Build.DISPLAY}\n" + 100 | "[Android Version]: ${Build.VERSION.RELEASE}\n" + 101 | "[Android API Level]: ${Build.VERSION.SDK_INT}\n" + 102 | "[ColorOS Version]: $colorOSFullVersion\n" + 103 | "[System Locale]: ${Locale.getDefault()}\n\n" + YLog.contents(debugLogs).trim() 104 | activity.contentResolver?.openOutputStream(e)?.apply { write(content.toByteArray()) }?.close() 105 | activity.snake(msg = "导出完成") 106 | } ?: activity.snake(msg = "已取消操作") 107 | }.onFailure { activity.snake(msg = "导出过程发生错误") } 108 | } 109 | } 110 | 111 | /** 112 | * 获取并导出全部调试日志 113 | * @param context 实例 114 | */ 115 | fun obtainAndExportDebugLogs(context: Context) { 116 | /** 执行导出操作 */ 117 | fun doExport() { 118 | context.dataChannel(PackageName.SYSTEMUI).obtainLoggerInMemoryData { 119 | if (it.isNotEmpty()) { 120 | debugLogs = it 121 | runCatching { launcher?.launch("coloros_notification_icons_processing_logs.log") } 122 | .onFailure { context.snake(msg = "启动系统文件选择器失败") } 123 | } else context.snake(msg = "暂无调试日志") 124 | } 125 | } 126 | if (YukiHookAPI.Status.isXposedModuleActive) 127 | context.showDialog { 128 | title = "导出全部调试日志" 129 | msg = "调试日志中会包含当前系统推送的全部通知内容,其中可能包含你的个人隐私," + 130 | "你可以在导出后的日志文件中选择将这些敏感信息模糊化处理再进行共享," + 131 | "开发者使用并查看你导出的调试日志仅为排查与修复问题,并且在之后会及时销毁这些日志。\n\n" + 132 | "继续导出即代表你已阅读并知悉上述内容。" 133 | confirmButton(text = "继续") { doExport() } 134 | cancelButton() 135 | } 136 | else context.snake(msg = "模块没有激活,请先激活模块") 137 | } 138 | 139 | /** 140 | * 重启系统界面 141 | * @param context 实例 142 | */ 143 | fun restartSystemUI(context: Context) { 144 | /** 动态刷新功能是否可用 */ 145 | val isDynamicAvailable = ConfigData.isEnableModule && MainActivity.isModuleRegular && MainActivity.isModuleValied 146 | 147 | /** 当 Root 权限获取失败时显示对话框 */ 148 | fun showWhenAccessRootFail() = 149 | context.showDialog { 150 | title = "获取 Root 权限失败" 151 | msg = "当前无法获取 Root 权限,请确认你的设备已经被 Root 且同意授予 Root 权限。\n" + 152 | "如果你正在使用 Magisk 并安装了 Shamiko 模块," + 153 | "请确认当前是否正处于白名单模式。 (白名单模式将导致无法申请 Root 权限)" 154 | confirmButton(text = "我知道了") 155 | } 156 | context.showDialog { 157 | title = "重启系统界面" 158 | msg = "你确定要立即重启系统界面吗?\n\n" + 159 | "重启过程会黑屏并等待进入锁屏重新解锁。" + (if (isDynamicAvailable) 160 | "\n\n你也可以选择“立即生效”来动态刷新系统界面并生效当前模块设置。" else "") 161 | confirmButton { 162 | execShell(cmd = "pgrep systemui").also { pid -> 163 | if (pid.isNotBlank()) 164 | execShell(cmd = "kill -9 $pid") 165 | else showWhenAccessRootFail() 166 | } 167 | } 168 | cancelButton() 169 | if (isDynamicAvailable) neutralButton(text = "立即生效") { refreshSystemUI(context) } 170 | } 171 | } 172 | 173 | /** 174 | * 刷新系统界面状态栏与通知图标 175 | * @param context 实例 176 | * @param isRefreshCacheOnly 仅刷新缓存不刷新图标和通知改变 - 默认:否 177 | * @param callback 成功后回调 178 | */ 179 | fun refreshSystemUI(context: Context? = null, isRefreshCacheOnly: Boolean = false, callback: () -> Unit = {}) { 180 | /** 181 | * 刷新系统界面 182 | * @param result 回调结果 183 | */ 184 | fun doRefresh(result: (Boolean) -> Unit) { 185 | context?.dataChannel(PackageName.SYSTEMUI)?.with { 186 | wait(CALL_MODULE_REFRESH_RESULT) { result(it) } 187 | put(CALL_HOST_REFRESH_CACHING, isRefreshCacheOnly) 188 | } 189 | } 190 | when { 191 | YukiHookAPI.Status.isXposedModuleActive && context is AppCompatActivity -> 192 | context.showDialog { 193 | title = "请稍后" 194 | progressContent = "正在等待系统界面刷新" 195 | /** 是否等待成功 */ 196 | var isWaited = false 197 | /** 设置等待延迟 */ 198 | delayedRun(ms = 5000) { 199 | if (isWaited) return@delayedRun 200 | cancel() 201 | context.snake(msg = "预计响应超时,建议重启系统界面", actionText = "立即重启") { restartSystemUI(context) } 202 | } 203 | checkingActivated(context) { isValied -> 204 | when { 205 | isValied.not() -> { 206 | cancel() 207 | isWaited = true 208 | context.snake(msg = "请重启系统界面以生效模块更新", actionText = "立即重启") { restartSystemUI(context) } 209 | } 210 | else -> doRefresh { 211 | cancel() 212 | isWaited = true 213 | callback() 214 | if (it.not()) context.snake(msg = "刷新失败,建议重启系统界面", actionText = "立即重启") { restartSystemUI(context) } 215 | } 216 | } 217 | } 218 | noCancelable() 219 | } 220 | YukiHookAPI.Status.isXposedModuleActive.not() && context is AppCompatActivity -> context.snake(msg = "模块没有激活,更改不会生效") 221 | else -> doRefresh { callback() } 222 | } 223 | } 224 | 225 | /** 226 | * 显示需要重启系统界面的 [Snackbar] 227 | * @param context 实例 228 | */ 229 | fun showNeedRestartSnake(context: Context) = 230 | if (YukiHookAPI.Status.isXposedModuleActive) 231 | context.snake(msg = "设置需要重启系统界面才能生效", actionText = "立即重启") { restartSystemUI(context) } 232 | else context.snake(msg = "模块没有激活,更改不会生效") 233 | } -------------------------------------------------------------------------------- /app/src/main/java/com/fankes/coloros/notify/wrapper/BuildConfigWrapper.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * ColorOSNotifyIcon - Optimize notification icons for ColorOS and adapt to native notification icon specifications. 3 | * Copyright (C) 20174 Fankes Studio(qzmmcn@163.com) 4 | * https://github.com/fankes/ColorOSNotifyIcon 5 | * 6 | * This software is non-free but opensource software: you can redistribute it 7 | * and/or modify it under the terms of the GNU Affero General Public License 8 | * as published by the Free Software Foundation; either 9 | * version 3 of the License, or any later version. 10 | *

11 | * 12 | * This software is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | * Affero General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU Affero General Public License 18 | * and eula along with this software. If not, see 19 | * 20 | * 21 | * This file is created by fankes on 2023/9/17. 22 | */ 23 | @file:Suppress("unused") 24 | 25 | package com.fankes.coloros.notify.wrapper 26 | 27 | import com.fankes.coloros.notify.BuildConfig 28 | 29 | /** 30 | * 对 [BuildConfig] 的包装 31 | */ 32 | object BuildConfigWrapper { 33 | const val APPLICATION_ID = BuildConfig.APPLICATION_ID 34 | const val VERSION_NAME = BuildConfig.VERSION_NAME 35 | const val VERSION_CODE = BuildConfig.VERSION_CODE 36 | val isDebug = BuildConfig.DEBUG 37 | } -------------------------------------------------------------------------------- /app/src/main/res/drawable-night/bg_dark_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-night/bg_permotion_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/bg_button_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/bg_dark_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/bg_green_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/bg_orange_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/bg_permotion_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/bg_yellow_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_about.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_back.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_filter.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_function.xml: -------------------------------------------------------------------------------- 1 | 6 | 13 | 20 | 27 | 34 | 40 | 46 | 52 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_github.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_home.xml: -------------------------------------------------------------------------------- 1 | 6 | 13 | 19 | 25 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_info.xml: -------------------------------------------------------------------------------- 1 | 6 | 12 | 16 | 23 | 30 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_message.xml: -------------------------------------------------------------------------------- 1 | 6 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_modify.xml: -------------------------------------------------------------------------------- 1 | 6 | 13 | 20 | 27 | 30 | 33 | 36 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_nf_icon_update.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_notify.xml: -------------------------------------------------------------------------------- 1 | 6 | 13 | 20 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_notify_icon.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_notify_update.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_page_bottom.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_page_top.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_preference.xml: -------------------------------------------------------------------------------- 1 | 6 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_restart.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 12 | 15 | 18 | 21 | 24 | 27 | 30 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_success.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_sync.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_system_clock.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_theme.xml: -------------------------------------------------------------------------------- 1 | 6 | 10 | 16 | 22 | 28 | 34 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_unsupported.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_warn.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_config.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 20 | 21 | 31 | 32 | 39 | 40 | 49 | 50 | 58 | 59 | 60 | 67 | 68 | 76 | 77 | 85 | 86 | 94 | 95 | 96 | 107 | 108 | 115 | 116 | 125 | 126 | 127 | 131 | 132 | 143 | 144 | 155 | 156 | 157 | 171 | -------------------------------------------------------------------------------- /app/src/main/res/layout/adapter_config.xml: -------------------------------------------------------------------------------- 1 | 2 | 13 | 14 | 20 | 21 | 26 | 27 | 33 | 34 | 42 | 43 | 44 | 54 | 55 | 63 | 64 | 65 | 69 | 70 | 78 | 79 | 86 | 87 | -------------------------------------------------------------------------------- /app/src/main/res/layout/dia_icon_filter.xml: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 15 | 16 | 23 | 24 | -------------------------------------------------------------------------------- /app/src/main/res/layout/dia_source_from.xml: -------------------------------------------------------------------------------- 1 | 2 | 12 | 13 | 22 | 23 | 26 | 27 | 33 | 34 | 40 | 41 | 47 | 48 | 54 | 55 | 56 | 62 | 63 | 70 | 71 | 72 | 78 | 79 | 88 | 89 | 95 | 96 | 109 | 110 | 123 | 124 | 125 | -------------------------------------------------------------------------------- /app/src/main/res/layout/dia_source_from_string.xml: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 22 | 23 | 26 | 27 | 35 | 36 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fankes/ColorOSNotifyIcon/492d0dad6725f039d08d977f8a2ff81eb25451f6/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fankes/ColorOSNotifyIcon/492d0dad6725f039d08d977f8a2ff81eb25451f6/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fankes/ColorOSNotifyIcon/492d0dad6725f039d08d977f8a2ff81eb25451f6/app/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fankes/ColorOSNotifyIcon/492d0dad6725f039d08d977f8a2ff81eb25451f6/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fankes/ColorOSNotifyIcon/492d0dad6725f039d08d977f8a2ff81eb25451f6/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fankes/ColorOSNotifyIcon/492d0dad6725f039d08d977f8a2ff81eb25451f6/app/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fankes/ColorOSNotifyIcon/492d0dad6725f039d08d977f8a2ff81eb25451f6/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fankes/ColorOSNotifyIcon/492d0dad6725f039d08d977f8a2ff81eb25451f6/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fankes/ColorOSNotifyIcon/492d0dad6725f039d08d977f8a2ff81eb25451f6/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/bg_payment_code.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fankes/ColorOSNotifyIcon/492d0dad6725f039d08d977f8a2ff81eb25451f6/app/src/main/res/mipmap-xxhdpi/bg_payment_code.jpg -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fankes/ColorOSNotifyIcon/492d0dad6725f039d08d977f8a2ff81eb25451f6/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fankes/ColorOSNotifyIcon/492d0dad6725f039d08d977f8a2ff81eb25451f6/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fankes/ColorOSNotifyIcon/492d0dad6725f039d08d977f8a2ff81eb25451f6/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_yukihookapi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fankes/ColorOSNotifyIcon/492d0dad6725f039d08d977f8a2ff81eb25451f6/app/src/main/res/mipmap-xxhdpi/ic_yukihookapi.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fankes/ColorOSNotifyIcon/492d0dad6725f039d08d977f8a2ff81eb25451f6/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fankes/ColorOSNotifyIcon/492d0dad6725f039d08d977f8a2ff81eb25451f6/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fankes/ColorOSNotifyIcon/492d0dad6725f039d08d977f8a2ff81eb25451f6/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/values-night/color.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #FF2D2D2D 4 | #FFCFCFCF 5 | #FFD3D3D3 6 | -------------------------------------------------------------------------------- /app/src/main/res/values-night/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 17 | -------------------------------------------------------------------------------- /app/src/main/res/values/array.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | android 5 | com.android.systemui 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/values/color.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #FFFFFFFF 4 | #FF777777 5 | #FF323B42 6 | -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #656565 4 | #FF000000 5 | #FFFFFFFF 6 | #00000000 7 | -------------------------------------------------------------------------------- /app/src/main/res/values/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #4E8A5A 4 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | ColorOS 通知图标增强 3 | 通知图标 4 | -------------------------------------------------------------------------------- /app/src/main/res/values/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 17 | -------------------------------------------------------------------------------- /app/src/test/java/com/fankes/coloros/notify/ExampleUnitTest.kt: -------------------------------------------------------------------------------- 1 | package com.fankes.coloros.notify 2 | 3 | import org.junit.Test 4 | 5 | import org.junit.Assert.* 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * See [testing documentation](http://d.android.com/tools/testing). 11 | */ 12 | class ExampleUnitTest { 13 | @Test 14 | fun addition_isCorrect() { 15 | assertEquals(4, 2 + 2) 16 | } 17 | } -------------------------------------------------------------------------------- /build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | autowire(libs.plugins.android.application) apply false 3 | autowire(libs.plugins.kotlin.android) apply false 4 | autowire(libs.plugins.kotlin.ksp) apply false 5 | } -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Compiler Configuration 2 | org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 3 | android.useAndroidX=true 4 | android.nonTransitiveRClass=true 5 | kotlin.code.style=official 6 | kotlin.incremental.useClasspathSnapshot=true 7 | # Project Configuration 8 | project.name=ColorOSNotifyIcon 9 | project.android.compileSdk=35 10 | project.android.minSdk=29 11 | project.android.targetSdk=35 12 | project.app.packageName=com.fankes.coloros.notify 13 | project.app.versionName="1.100" 14 | project.app.versionCode=23 15 | project.app.signing.keyAlias=public 16 | project.app.signing.keyPassword="123456" 17 | project.app.signing.storePassword="123456" 18 | project.app.signing.storeFilePath=.secret/universal.p12 -------------------------------------------------------------------------------- /gradle/sweet-dependency/sweet-dependency-config.yaml: -------------------------------------------------------------------------------- 1 | preferences: 2 | autowire-on-sync-mode: UPDATE_OPTIONAL_DEPENDENCIES 3 | repositories-mode: FAIL_ON_PROJECT_REPOS 4 | 5 | repositories: 6 | gradle-plugin-portal: 7 | scope: PLUGINS 8 | google: 9 | maven-central: 10 | jit-pack: 11 | sonatype-oss-releases: 12 | rovo89-xposed-api: 13 | scope: LIBRARIES 14 | url: https://api.xposed.info/ 15 | content: 16 | include: 17 | group: 18 | de.robv.android.xposed 19 | fankes-maven-releases: 20 | url: https://raw.githubusercontent.com/fankes/maven-repository/main/repository/releases 21 | 22 | plugins: 23 | com.android.application: 24 | alias: android-application 25 | version: 8.9.0 26 | org.jetbrains.kotlin.android: 27 | alias: kotlin-android 28 | version: 2.1.10 29 | com.google.devtools.ksp: 30 | alias: kotlin-ksp 31 | version: 2.1.10-1.0.31 32 | 33 | libraries: 34 | com.fankes.projectpromote: 35 | project-promote: 36 | version: 1.0.0 37 | repositories: 38 | fankes-maven-releases 39 | de.robv.android.xposed: 40 | api: 41 | version: 82 42 | repositories: 43 | rovo89-xposed-api 44 | com.highcapable.yukihookapi: 45 | api: 46 | version: 1.2.1 47 | ksp-xposed: 48 | version-ref: ::api 49 | com.github.topjohnwu.libsu: 50 | core: 51 | version: 5.2.2 52 | auto-update: false 53 | com.github.duanhong169: 54 | drawabletoolbox: 55 | version: 1.0.7 56 | com.squareup.okhttp3: 57 | okhttp: 58 | version: 5.0.0-alpha.14 59 | androidx.core: 60 | core-ktx: 61 | version: 1.15.0 62 | androidx.appcompat: 63 | appcompat: 64 | version: 1.7.0 65 | com.google.android.material: 66 | material: 67 | version: 1.12.0 68 | androidx.constraintlayout: 69 | constraintlayout: 70 | version: 2.2.1 71 | androidx.test.ext: 72 | junit: 73 | version: 1.2.1 74 | androidx.test.espresso: 75 | espresso-core: 76 | version: 3.6.1 77 | junit: 78 | junit: 79 | version: 4.13.2 -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fankes/ColorOSNotifyIcon/492d0dad6725f039d08d977f8a2ff81eb25451f6/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip 3 | distributionPath=wrapper/dists 4 | zipStorePath=wrapper/dists 5 | zipStoreBase=GRADLE_USER_HOME -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # 4 | # Copyright 2015 the original author or authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | ## 21 | ## Gradle start up script for UN*X 22 | ## 23 | ############################################################################## 24 | 25 | # Attempt to set APP_HOME 26 | # Resolve links: $0 may be a link 27 | PRG="$0" 28 | # Need this for relative symlinks. 29 | while [ -h "$PRG" ] ; do 30 | ls=`ls -ld "$PRG"` 31 | link=`expr "$ls" : '.*-> \(.*\)$'` 32 | if expr "$link" : '/.*' > /dev/null; then 33 | PRG="$link" 34 | else 35 | PRG=`dirname "$PRG"`"/$link" 36 | fi 37 | done 38 | SAVED="`pwd`" 39 | cd "`dirname \"$PRG\"`/" >/dev/null 40 | APP_HOME="`pwd -P`" 41 | cd "$SAVED" >/dev/null 42 | 43 | APP_NAME="Gradle" 44 | APP_BASE_NAME=`basename "$0"` 45 | 46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 48 | 49 | # Use the maximum available, or set MAX_FD != -1 to use that value. 50 | MAX_FD="maximum" 51 | 52 | warn () { 53 | echo "$*" 54 | } 55 | 56 | die () { 57 | echo 58 | echo "$*" 59 | echo 60 | exit 1 61 | } 62 | 63 | # OS specific support (must be 'true' or 'false'). 64 | cygwin=false 65 | msys=false 66 | darwin=false 67 | nonstop=false 68 | case "`uname`" in 69 | CYGWIN* ) 70 | cygwin=true 71 | ;; 72 | Darwin* ) 73 | darwin=true 74 | ;; 75 | MINGW* ) 76 | msys=true 77 | ;; 78 | NONSTOP* ) 79 | nonstop=true 80 | ;; 81 | esac 82 | 83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 84 | 85 | 86 | # Determine the Java command to use to start the JVM. 87 | if [ -n "$JAVA_HOME" ] ; then 88 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 89 | # IBM's JDK on AIX uses strange locations for the executables 90 | JAVACMD="$JAVA_HOME/jre/sh/java" 91 | else 92 | JAVACMD="$JAVA_HOME/bin/java" 93 | fi 94 | if [ ! -x "$JAVACMD" ] ; then 95 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 96 | 97 | Please set the JAVA_HOME variable in your environment to match the 98 | location of your Java installation." 99 | fi 100 | else 101 | JAVACMD="java" 102 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 103 | 104 | Please set the JAVA_HOME variable in your environment to match the 105 | location of your Java installation." 106 | fi 107 | 108 | # Increase the maximum file descriptors if we can. 109 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 110 | MAX_FD_LIMIT=`ulimit -H -n` 111 | if [ $? -eq 0 ] ; then 112 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 113 | MAX_FD="$MAX_FD_LIMIT" 114 | fi 115 | ulimit -n $MAX_FD 116 | if [ $? -ne 0 ] ; then 117 | warn "Could not set maximum file descriptor limit: $MAX_FD" 118 | fi 119 | else 120 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 121 | fi 122 | fi 123 | 124 | # For Darwin, add options to specify how the application appears in the dock 125 | if $darwin; then 126 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 127 | fi 128 | 129 | # For Cygwin or MSYS, switch paths to Windows format before running java 130 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then 131 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 132 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 133 | 134 | JAVACMD=`cygpath --unix "$JAVACMD"` 135 | 136 | # We build the pattern for arguments to be converted via cygpath 137 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 138 | SEP="" 139 | for dir in $ROOTDIRSRAW ; do 140 | ROOTDIRS="$ROOTDIRS$SEP$dir" 141 | SEP="|" 142 | done 143 | OURCYGPATTERN="(^($ROOTDIRS))" 144 | # Add a user-defined pattern to the cygpath arguments 145 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 146 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 147 | fi 148 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 149 | i=0 150 | for arg in "$@" ; do 151 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 152 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 153 | 154 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 155 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 156 | else 157 | eval `echo args$i`="\"$arg\"" 158 | fi 159 | i=`expr $i + 1` 160 | done 161 | case $i in 162 | 0) set -- ;; 163 | 1) set -- "$args0" ;; 164 | 2) set -- "$args0" "$args1" ;; 165 | 3) set -- "$args0" "$args1" "$args2" ;; 166 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;; 167 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 168 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 169 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 170 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 171 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 172 | esac 173 | fi 174 | 175 | # Escape application args 176 | save () { 177 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 178 | echo " " 179 | } 180 | APP_ARGS=`save "$@"` 181 | 182 | # Collect all arguments for the java command, following the shell quoting and substitution rules 183 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 184 | 185 | exec "$JAVACMD" "$@" 186 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if "%ERRORLEVEL%"=="0" goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 84 | exit /b 1 85 | 86 | :mainEnd 87 | if "%OS%"=="Windows_NT" endlocal 88 | 89 | :omega 90 | -------------------------------------------------------------------------------- /img-src/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fankes/ColorOSNotifyIcon/492d0dad6725f039d08d977f8a2ff81eb25451f6/img-src/icon.png -------------------------------------------------------------------------------- /settings.gradle.kts: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | gradlePluginPortal() 4 | google() 5 | mavenCentral() 6 | } 7 | } 8 | plugins { 9 | id("com.highcapable.sweetdependency") version "1.0.4" 10 | id("com.highcapable.sweetproperty") version "1.0.5" 11 | } 12 | sweetProperty { 13 | global { 14 | all { 15 | permanentKeyValues("GITHUB_CI_COMMIT_ID" to "") 16 | generateFrom(ROOT_PROJECT, SYSTEM_ENV) 17 | } 18 | sourcesCode { 19 | includeKeys("GITHUB_CI_COMMIT_ID") 20 | // 关闭类型自动转换功能,防止一些特殊 "COMMIT ID" 被生成为数值 21 | isEnableTypeAutoConversion = false 22 | } 23 | } 24 | rootProject { all { isEnable = false } } 25 | } 26 | rootProject.name = "ColorOSNotifyIcon" 27 | include(":app") --------------------------------------------------------------------------------